DEV Community

Cover image for Building Interactive Skill Progress Bars with HTML, CSS, and JavaScript
Matt Adil
Matt Adil

Posted on

Building Interactive Skill Progress Bars with HTML, CSS, and JavaScript

skills bar

Introduction:

Skill progress bars are a common feature in portfolios and resumes, allowing users to visually showcase their expertise in various areas. In this article, we'll explore how to create dynamic and interactive skill progress bars using HTML, CSS, and JavaScript.

HTML Structure:

We begin by defining the HTML structure for our skill progress bars. We use semantic HTML elements such as '<section>', '<h3>', '<div>', and '<p>' to organize the content. Each skill is represented by a container with a name, a progress bar, and a percentage indicator.



<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap" rel="stylesheet"> <!-- importing "Poppins" font family from google font -->
<title>Skill Bar</title>
</head>
<body>
<section class="skills" id="skills">
<h3 class="skills-header">My Skills</h3>
<div class="skills-container">
<div class="skill-container">
<p>HTML</p>
<div class="percentage html-percentage">90%</div>
<div class="bar">
<span class="html"></span>
</div>
</div>

        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"skill-container"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;p&gt;</span>CSS<span class="nt">&lt;/p&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"percentage css-percentage"</span><span class="nt">&gt;</span>90%<span class="nt">&lt;/div&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"bar"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"css"</span><span class="nt">&gt;&lt;/span&gt;</span>
            <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>

        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"skill-container"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;p&gt;</span>JavaScript<span class="nt">&lt;/p&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"percentage javascript-percentage"</span><span class="nt">&gt;</span>80%<span class="nt">&lt;/div&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"bar"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"javascript"</span><span class="nt">&gt;&lt;/span&gt;</span>
            <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>

        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"skill-container"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;p&gt;</span>React.JS<span class="nt">&lt;/p&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"percentage react-percentage"</span><span class="nt">&gt;</span>70%<span class="nt">&lt;/div&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"bar"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"react"</span><span class="nt">&gt;&lt;/span&gt;</span>
            <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>

        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"skill-container"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;p&gt;</span>Node.JS<span class="nt">&lt;/p&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"percentage node-percentage"</span><span class="nt">&gt;</span>50%<span class="nt">&lt;/div&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"bar"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"node"</span><span class="nt">&gt;&lt;/span&gt;</span>
            <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/section&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"./script.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
Enter fullscreen mode Exit fullscreen mode

</body>
</html>

Enter fullscreen mode Exit fullscreen mode




CSS Styling:

Next, we style our skill progress bars using CSS. We define styles for the overall layout, skill containers, progress bars, and percentage indicators. We also incorporate animations to enhance the visual appeal of the bars.



*,
::before,
::after{
padding: 0;
margin: 0;
box-sizing: border-box;
}

body{
width:100%;
height:100vh;
background:#111222 ;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding:20px;
}

.skills{
width: 600px;
min-width:300px;
max-width: 100%;
color:#fff;
display:flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.skills-header{
font-family: "Poppins", sans-serif;
font-size: 30px;
width:100%;
text-align: center;
margin-bottom: 10px;
box-sizing: border-box;
}

.skills-container{
max-width: 100%;
width:100%;
display:flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border-radius: 10px;
padding:20px;
margin:0;
box-sizing: border-box;
}

.skill-container{
width:100%;
margin:10px 0;
padding: 10px 0;
position:relative;
}

.skill-container p{
margin: 5px;
font-family: "Poppins", sans-serif;
font-size: 16px;
}

.bar{
width:100%;
background: #475472;
display:block;
height: 20px;
border:none;
border-radius: 100vw;
overflow: hidden;
transition: all .3s cubic-bezier(.25, 8, .25, 1);
}

.bar span{
height:20px;
float: left;
background: #5271ff;
border:none;
border-radius: 100vw;
}

/* adding animation to skill bar */

.html.animate, .css.animate{
width: 90%;
animation: html_css 3s;
}

.javascript.animate{
width:80%;
animation: javascript 3s;
}

.react.animate{
width:70%;
animation: react 3s;
}

.node.animate{
width:50%;
animation: node 3s;
}

@keyframes html_css{
0%{
width:0%
};

<span class="err">100</span><span class="o">%</span><span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span><span class="m">90%</span>
<span class="p">}</span>
Enter fullscreen mode Exit fullscreen mode

}

@keyframes javascript{
0%{
width:0%
};

<span class="err">100</span><span class="o">%</span><span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span><span class="m">80%</span>
<span class="p">}</span>
Enter fullscreen mode Exit fullscreen mode

}

@keyframes react{
0%{
width:0%
};

<span class="err">100</span><span class="o">%</span><span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span><span class="m">70%</span>
<span class="p">}</span>
Enter fullscreen mode Exit fullscreen mode

}

@keyframes node{
0%{
width:0%
};

<span class="err">100</span><span class="o">%</span><span class="p">{</span>
    <span class="nl">width</span><span class="p">:</span><span class="m">50%</span>
<span class="p">}</span>
Enter fullscreen mode Exit fullscreen mode

}

.percentage{
content: "";
position: absolute;
top:12px;
width:25px;
height:20px;
background: #5271ff;
display:flex;
align-items: center;
justify-content: center;
font-size: 10px;
border-radius: 2px;
z-index: 10;
transition: all .3s ease-in-out;
}

.percentage::after{
content: "";
position: absolute;
top:15px;
left:50%;
transform: translateX(-50%) rotate(45deg);
margin: auto;
width:10px;
height:10px;
background: #5271ff;
z-index:-1;
}

/* adding animation to .percentage */

.html-percentage.animate,
.css-percentage.animate{
left:90%;
animation: html_css_percentage 3s;
}

.javascript-percentage.animate{
left:80%;
animation: javascript_percentage 3s;
}

.react-percentage.animate{
left:70%;
animation: react_percentage 3s;
}

.node-percentage.animate{
left:70%;
animation: react_percentage 3s;
}

@keyframes html_css_percentage{
0%{
left:0
};
100%{
left:90%;
}
}

@keyframes javascript_percentage{
0%{
left:0
};
100%{
left:80%;
}
}

@keyframes react_percentage{
0%{
left:0
};
100%{
left:70%;
}
}

@keyframes node_percentage{
0%{
left:0
};
100%{
left:50%;
}
}

Enter fullscreen mode Exit fullscreen mode




JavaScript Interaction:

To make our skill progress bars interactive, we use JavaScript. We employ the Intersection Observer API to trigger animations when the bars come into view. Additionally, we dynamically calculate and set the position of the percentage indicators based on the width of the bars.



const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
spans.forEach((span, index) => {
const percentage = percentages[index];
if (entry.isIntersecting) {
span.classList.add('animate');
percentage.classList.add('animate');
} else {
span.classList.remove('animate');
percentage.classList.remove('animate');
}
});
});
});

const skillContainer = document.querySelector('.skills-container');
const spans = document.querySelectorAll('.bar span');
const percentages = document.querySelectorAll('.percentage');
observer.observe(skillContainer);

function updatePercentagePositions() {
const barWidth = document.querySelector('.bar').clientWidth;
percentages.forEach(percentage => {
const widthPercentage = parseFloat(percentage.textContent) / 100;
percentage.style.left = </span><span class="p">${</span><span class="nx">barWidth</span> <span class="o">*</span> <span class="nx">widthPercentage</span> <span class="o">-</span> <span class="mf">12.5</span><span class="p">}</span><span class="s2">px;
});
}

updatePercentagePositions(); // Call initially

Enter fullscreen mode Exit fullscreen mode




Conclusion:

In this tutorial, we've learned how to create interactive skill progress bars using HTML, CSS, and JavaScript. By combining these technologies, we can build visually appealing and engaging elements for showcasing our skills on websites and portfolios.

Check out the live demo and code:

CodePen: Link to CodePen

GitHub Repo: Link to GitHub Repo

Top comments (6)

Collapse
 
dannyengelman profile image
Danny Engelman

Wrap it in a native <skill-progress> Web Component and we can use it without building it

customElements.define("skill-progress" , class extends HTMLElement {
 .. your code..
})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mattbug3 profile image
Matt Adil

Thanks for the suggestion! I actually just whipped up the skill progress bar using plain old HTML, CSS, and JavaScript. But I totally get what you’re saying about making it into a cool component for easier use. That could be a neat project for down the line. Appreciate the idea!

Collapse
 
efpage profile image
Eckehard • Edited

Your post reminds me how stupid HTML actually is. It forces us to repeat the same task manually again and again. You can´t even put things togeter in a function-like structure. We are using AI and computers, that can do billions of operations in a second, but HTML forces us to stay in the stone age.

Image description

A skill tableau (without that fancy animation) should only have a few lines of code like this

function tableauElement(el){
  let s = Object.keys(el)[0]
  span(s); br()
  slider().value = el[s]; br(2)
}

[{HTML:80},{CSS:70},{Javascript:60},{ReactJS:50},{NodeJS:30}].map( s => tableauElement(s) )
Enter fullscreen mode Exit fullscreen mode

It´s possible, see this example

Collapse
 
dannyengelman profile image
Danny Engelman • Edited

I totally disagree. HTML is Cool.
Your "solution" requires programming skills.

Web Components make HTML fully semantic

Creating a pie-chart with HTML. Can be learned in 5 minutes.

See my DEV.to blogpost: "A Pie Chart in 2021"

Thread Thread
 
efpage profile image
Eckehard

It is not a bad thing to have some skills, even for a web designer. The Javascript provided in the example is far more complex than what i have provided in my example. And to write webcompontents you will need even more Javascript-skills.

I would love to use HTML if it would allow me to build pages in an efficient way. But the example shows impressively, that it needs 44 lines of HTML to do the same, I achieved in one line of code. This is simply waist of time and brainpower. My Javascript is pretty simple, while the example shown is fairly complex and hard to read. This needs much more skills than just some simple lines of code.

We should avoid generalizations like "you should not need any programming skills to build a web page" as they are in most cases not true. The only thing we should avoid ist to do things manually that a computer can do better.

Thread Thread
 
dannyengelman profile image
Danny Engelman • Edited

You are thinking as a developer; for you Code comes easy.
It is not about the Web Component JavaScript; that is created once by an experienced developer, just as Frameworks and Libraries are created.
The power of Web Components is about only writing HTML