I'm using React with Tailwind CSS and I'm trying to do a skill progress bar with gradient color but the color don't match at all when it reaches a certain percentage.
For example 15% of the full width, it should be all blue and not different colors. How can I fix it?
Here's the code of it
Tailwind CSS
.progressbar {
/* Size */
height: 10px;
/* Content alignment */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
/* Style */
border-radius: 60px;
background-color: #f0ecec;
overflow: hidden;
}
.bar {
/* Size */
width: 0%;
/* Style */
background: rgb(255, 174, 105);
background: linear-gradient(to right, #12B0FF 0%, #4399E7 7%, #5282DF 15%, #6565D7 22%, #951CC1 30%, #A517B8 37%, #A717B3 45%, #AF18A8 52%, #C83E82 60%, #CF4978 67%, #DE5E62 75%, #E86E51 84%, #F27D42 93%, #FA8838 100%);
}
.progressbar-text-container {
/* Size */
width: 30px;
/* Content alignment */
display: flex;
flex-direction: row;
/* Styling */
font-weight: bold;
font-size: 30px;
}
ProgressBar.js
import { motion, animate } from 'framer-motion';
import { useEffect, useState } from "react";
function Progressbar({ name, value })
{
// Replaced useRef with useState
const [progressText, setProgressText] = useState("0");
useEffect(() =>
{
animate(parseInt(progressText), value, {
duration: 1.5,
onUpdate: (cv) =>
{
// Updating state instead of direct DOM manipulation
setProgressText(cv.toFixed(0));
}
});
}, [value]);
return (
<div className="progressbar-container py-2">
<div className="flex py-1 justify-between">
<p className="progress-bar-title">{name}</p>
<div className="flex justify-end">
{/* Replaced ref with state variable */}
<p className="progress-bar-title">{progressText}</p>
<p className="progress-bar-title">%</p>
</div>
</div>
<div className="progressbar">
<motion.div
className="bar"
animate={{
width: `${value}%`,
}}
transition={{
duration: 1.5
}}
/>
</div>
</div>
);
}
export default Progressbar;
Consider having all the .bar
elements the same width
of 100%
so that their background gradients are the same size. Then, use clip-path:
inset()
to set their painted size:
const { motion, animate } = Motion;
const { useEffect, useState } = React;
function Progressbar({ name, value })
{
// Replaced useRef with useState
const [progressText, setProgressText] = useState("0");
useEffect(() =>
{
animate(parseInt(progressText), value, {
duration: 1.5,
onUpdate: (cv) =>
{
// Updating state instead of direct DOM manipulation
setProgressText(cv.toFixed(0));
}
});
}, [value]);
return (
<div className="progressbar-container py-2">
<div className="flex py-1 justify-between">
<p className="progress-bar-title">{name}</p>
<div className="flex justify-end">
{/* Replaced ref with state variable */}
<p className="progress-bar-title">{progressText}</p>
<p className="progress-bar-title">%</p>
</div>
</div>
<div className="progressbar">
<motion.div
className="bar"
animate={{
clipPath: `inset(0 ${100 - value}% 0 0)`,
}}
transition={{
duration: 1.5
}}
/>
</div>
</div>
);
};
function App() {
return (
<React.Fragment>
<Progressbar value={100} name="foo"/>
<Progressbar value={50} name="bar"/>
<Progressbar value={25} name="baz"/>
</React.Fragment>
);
}
ReactDOM.createRoot(document.getElementById('app')).render(<App/>);
.progressbar {
/* Size */
height: 10px;
/* Content alignment */
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
/* Style */
border-radius: 60px;
background-color: #f0ecec;
overflow: hidden;
}
.bar {
/* Size */
width: 100%;
/* Style */
background: rgb(255, 174, 105);
background: linear-gradient(to right, #12B0FF 0%, #4399E7 7%, #5282DF 15%, #6565D7 22%, #951CC1 30%, #A517B8 37%, #A717B3 45%, #AF18A8 52%, #C83E82 60%, #CF4978 67%, #DE5E62 75%, #E86E51 84%, #F27D42 93%, #FA8838 100%);
clip-path: inset(0 100% 0 0);
}
.progressbar-text-container {
/* Size */
width: 30px;
/* Content alignment */
display: flex;
flex-direction: row;
/* Styling */
font-weight: bold;
font-size: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js" integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js" integrity="sha512-MOCpqoRoisCTwJ8vQQiciZv0qcpROCidek3GTFS6KTk2+y7munJIlKCVkFCYY+p3ErYFXCjmFjnfTTRSC1OHWQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/framer-motion@10.16.1/dist/framer-motion.js" integrity="sha256-sS+KKDemoS6qg22Hi++wc5PAlgej08U6vDUKk/Y9K8Y=" crossorigin="anonymous"></script>
<script src="https://cdn.tailwindcss.com/3.3.3"></script>
<div id="app"></div>