A linear progress indicator composed of a continuous solid line and dotted markers to represent individual steps

I am currently working on creating a vertical progress bar with 8 dots displayed on a solid line, where each dot represents a step in the process. Please refer to the attached screenshot (located at the bottom of this question to maintain clarity).

My attempts using HTML and CSS can be viewed in this fiddle (code provided below). However, I have encountered a challenge in displaying the 7 dots on the light-green line without having to add 8 additional div elements.

In terms of functionality, my goal is for JavaScript to check the value of the `progressNow` div, multiply it by 100, and apply that as the CSS height for the `progressNow` class. The issue here is that the dot moves instead of the bar filling up. (Does this explanation make sense?)

This has led me to consider creating an SVG element resembling the shape shown in the screenshot, with a gradient that shifts based on the nth step in the process. While I believe this approach will work, I am curious if there is another, potentially simpler method to achieve my desired outcome.

HTML

<div id="progress">
    <div class="progressBar"></div>
    <div class="progressNow" value="1"></div>
    <div class="progressTotal"></div>
</div>

CSS

#progress {
    position: relative;
}

#progress .progressBar {
    height: 800px;
    width: 6px;
    background: #8fe4bf;
    position: absolute;
}

#progress .progressNow {
    height: 100px;
    width: 6px;
    background: #00b164;
    position: absolute;
}

#progress .progressNow::after {
    content: "";
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #00b164;
    display: block;
    margin-left: -5px;
    position: absolute;
    top: 90px;
}

Desired outcome (assuming the `value` of `progressNow` is `2`)

https://i.sstatic.net/oyQRY.png

Answer №1

My approach mirrors that of @Stewartside, but with the utilization of Flexbox for uniform positioning. Additionally, adjusting the height is made effortless.

ul.progress-bar {
  height: 300px;
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
}
ul.progress-bar::after {
  content: "";
  position: absolute;
  top: 0;
  left: 5px;
  background: #777;
  width: 5px;
  height: 100vh;
}
ul.progress-bar li {
  background: #000;
  border-radius: 100px;
  width: 15px;
  height: 15px;
  z-index: 1;
  position: relative;
}
ul.progress-bar li.stop ~ li {
  background: #777;
}
ul.progress-bar li.stop ~ li::after {
  height: 0;
}
ul.progress-bar li::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 5px;
  background: #000;
  width: 5px;
  height: 100vh;
}
<ul class="progress-bar">
    <li></li>
    <li></li>
    <li></li>
    <li class="stop"></li>
    <li></li>
</ul>

Oddly enough, it appears that the bottom segment is not displaying in the stack snippet, so you can view it on jsfiddle.

Answer №2

Check out this innovative CSS solution using a minimal number of elements. By utilizing a combination of linear-gradients and radial-gradients, you can create both the vertical line and the dots.

The parent element, #progress-bar, generates the lightgreen initial line and circles. These same gradients are applied to the child element, #progress-now, which is absolutely positioned in relation to the parent. The only variation is that the height of #progress-now is determined by the value attribute.

This technique remains effective even when dealing with fractional values. While originally designed for a step tracker, it has versatile applications (just tooting my own horn here! :D).


Here's an alternative version featuring a fill-up animation effect.

window.onload = function() {
  var val = 0, progress = 0;

  function progressBar() {
    val += 0.005;
    progress = (val * 50 > 400) ? 400 : val * 50; /* 50 represents 1/8th of height, 400 signifies total height */
    document.getElementById('progress-now').setAttribute('style', 'height: ' + progress + 'px');
    if (val <= 8) anim = window.requestAnimationFrame(progressBar);
  }
  
  progressBar();
}
#progress-bar {
  position: relative;
  height: 400px;
  width: 200px;
  background: linear-gradient(to bottom, lightgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, lightgreen 25%, transparent 30%);
  background-position: 50% 0%, 50% 15px; /* 15px corresponds to 30% of 50px */
  background-size: 5px 100%, 50px 50px; /* 5px denotes the bar thickness, 50px represents 1/8th of the height */
  background-repeat: no-repeat, repeat-y;
}
#progress-now {
  position: absolute;
  width: 200px;
  background: linear-gradient(to bottom, darkgreen 99.9%, transparent 99.9%), radial-gradient(circle at 50% 50%, darkgreen 25%, transparent 30%);
  background-position: 50% 0%, 50% 15px;
  background-size: 5px 100%, 50px 50px;
  background-repeat: no-repeat, repeat-y;
  z-index: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id='progress-bar'>
  <div id='progress-now'></div>
</div>

Answer №3

Styling with CSS

One approach to styling using CSS involves creating multiple div elements to represent completion status.

.complete {
  width: 5px;
  height: 50px;
  position: relative;
  background: green;
  margin-left: 8px;
}
.complete:after {
  content: '';
  width: 20px;
  height: 20px;
  border-radius: 50%;
  position: absolute;
  bottom: -7.5px;
  left: -8px;
  background: green;
  z-index: 100;
}
.not_complete {
  width: 5px;
  height: 50px;
  background: lightgreen;
  position: relative;
  margin-left: 8px;
}
.not_complete:after {
  content: '';
  width: 20px;
  height: 20px;
  border-radius: 50%;
  position: absolute;
  bottom: -7.5px;
  left: -8px;
  background: lightgreen;
  z-index: 100;
}
<div class="progress">
  <div class="complete"></div>
  <div class="complete"></div>
  <div class="not_complete"></div>
  <div class="not_complete"></div>
  <div class="not_complete"></div>
  <div class="not_complete"></div>
  <div class="not_complete"></div>
  <div class="not_complete"></div>
</div>

Utilizing SVG Elements

An alternative method is using SVG g elements and applying different fills for each state.

When using SVG, ensure that the overall height accommodates all individual elements' heights combined.

<svg width="20" height="445" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <g id="completed">
      <line x1="0" y1="5" x2="0" y2="40" style="stroke:green;stroke-width:3;" />
      <circle style="fill:green; stroke-width:0;" cx="0" cy="50" r="10" />
    </g>
    <g id="not_complete">
      <line x1="0" y1="5" x2="0" y2="40" style="stroke:lightgreen;stroke-width:3;" />
      <circle style="fill:lightgreen; stroke-width:0;" cx="0" cy="50" r="10" />
    </g>
  </defs>

  <use x="10" y="00" xlink:href="#completed" />
  <use x="10" y="55" xlink:href="#completed" />
  <use x="10" y="110" xlink:href="#not_complete" />
  <use x="10" y="165" xlink:href="#not_complete" />
  <use x="10" y="220" xlink:href="#not_complete" />
  <use x="10" y="275" xlink:href="#not_complete" />
  <use x="10" y="330" xlink:href="#not_complete" />
  <use x="10" y="385" xlink:href="#not_complete" />
</svg>

Answer №4

My reply unexpectedly transformed into a solution. It utilizes a hidden <progress> nested within an empty <div> to visualize the desired outcome.

This method lacks versatility as CSS needs to be pre-written for each of the 5/6 options: 0, 20%, 40%, 60%, 80%, 100%

.hide-it {
  position: absolute;
  left: -100%;
}

#myprogress {
  height: 25em;
  position: relative;
  float: left;
  width: 0.5em;
  background: linear-gradient(to bottom, #00B18E 0%, #CFF8F0 0%, #CFF8F0);
  margin: 1em 9em 1em 1em;
  color:#00B18E;
  box-shadow:0 0 15em 1em black;
}
#myprogress:after {
  content: '     Progress 0%';
  position:relative;
  z-index: 1;
  white-space: pre;
  top:-1%;
}
#myprogress:before {
  content: '';
  position: absolute;
  z-index: 1;
  top: -0.2em;
  left: -.25em;
  width: 1em;
  height: 1em;
  border-radius: 50%;
  box-shadow: inset 0 0 0 15px #00B18E;
}

[value="20"] + #myprogress {
  background: linear-gradient(to bottom, #00B18E 20%, #CFF8F0 20%, #CFF8F0);
}

[value="20"] + #myprogress:before {
  box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E;
}
[value="20"] + #myprogress:after {
  content:'     Progress 20%';
  top:19%;
}

[value="40"] + #myprogress {
  background: linear-gradient(to bottom, #00B18E 40%, #CFF8F0 40%, #CFF8F0);
}

[value="40"] + #myprogress:before {
  box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E;
}
[value="40"] + #myprogress:after {
  content:'     Progress 40%';
  top:39%;
}

[value="60"] + #myprogress {
  background: linear-gradient(to bottom, #00B18E 60%, #CFF8F0 60%, #CFF8F0);
}
[value="60"] + #myprogress:before {
  box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E;
}
[value="60"] + #myprogress:after {
  content:'     Progress 60%';
  top:59%;
}
[value="80"] + #myprogress {
  background: linear-gradient(to bottom, #00B18E 80%, #CFF8F0 80%, #CFF8F0);
}
[value="80"] + #myprogress:before {
  box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E, 0 20em 0 0 #00B18E;
}
[value="80"] + #myprogress:after {
  content:'     Progress 80%';
  top:79%;
}
[value="100"] + #myprogress {
  background: linear-gradient(to bottom, #00B18E 100%, #CFF8F0 100%, #CFF8F0);
}
[value="100"] + #myprogress:before {
  box-shadow: inset 0 0 0 15px #00B18E, 0 5em 0 0 #00B18E, 0 10em 0 0 #00B18E, 0 15em 0 0 #00B18E, 0 20em 0 0 #00B18E, 0 25em 0 0 #00B18E;
}
[value="100"] + #myprogress:after {
  content:'     Progress 100%';
  top:99%;
}
<progress class="hide-it" value="0" max="100">
</progress>
  <div id="myprogress"></div>
<progress class="hide-it" value="20" max="100">
</progress>
  <div id="myprogress"></div>
<progress class="hide-it" value="40" max="100">
</progress>
  <div id="myprogress"></div>
<progress class="hide-it" value="60" max="100">
</progress>
  <div id="myprogress"></div>
<progress class="hide-it" value="80" max="100">
</progress>
  <div id="myprogress"></div>
<progress class="hide-it" value="100" max="100">
</progress>
  <div id="myprogress"></div>

Feel free to experiment with this code on CodePen!

Answer №5

To achieve this effect, you can utilize the CSS property box-shadow. This can be done with a single element by using multiple instances of box-shadow, although changing the color dynamically through JavaScript may prove challenging.

It's worth noting that jQuery was employed in this scenario solely to demonstrate the progressive enhancement of the progress bar. Click anywhere on the body to witness the outcome.

$('body').click(function () {
    var x = parseInt($('.progressNow').css('top')) + 10;
    $('.progressNow').css({
        top: x
    })
    if (x > 90 - 800) {      
        $('.circle').css('background','#00b164')
    }
    if(x > 180 -800){
        $('.circle').css('box-shadow','0 90px 0 0 #00b164, 0 180px 0 0 #8fe4bf, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf')
    }
    if(x > 270 -800){
        $('.circle').css('box-shadow','0 90px 0 0 #00b164, 0 180px 0 0 #00b164, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf')
    }    
})
#progress {
    overflow:hidden;
    width:15px;
    padding-left:5px;
    height:800px;
    position: relative;
}
#progress .progressBar {
    height: 800px;
    width: 6px;
    background: #8fe4bf;
    position: absolute;
}
#progress .progressNow {
    height: 800px;
    top:-800px;
    width: 6px;
    background: #00b164;
    position: absolute;
}
.circle{
    content:"";
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #8fe4bf;
    display: block;
    margin-left: -5px;
    position: absolute;
    top: 90px;
    box-shadow:0 90px 0 0 #8fe4bf, 0 180px 0 0 #8fe4bf, 0 270px 0 0 #8fe4bf, 0 360px 0 0 #8fe4bf, 0 450px 0 0 #8fe4bf, 0 540px 0 0 #8fe4bf;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<div id="progress">
    <div class="progressBar"></div>
    <div class="circle"></div>
    <div class="progressNow" value="1"></div>
</div>

Answer №6

Creating a Unique SVG Scroll Bar

Check out this innovative scroll bar design featuring a series of dots arranged in a single path.
The scrolling effect is achieved using a linear gradient that is dynamically modified through JavaScript.
The percentage indicator is implemented as an SVG text element, with its position updated by JavaScript.

(I may have devoted too much time to perfecting this solution)

var start = document.getElementById("gradstart");
var stop = document.getElementById("gradstop");
var max = Math.max(document.body.scrollHeight, document.body.offsetHeight,
  document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
max -= document.documentElement.clientHeight;

var process = document.getElementById("process");

function gradientScroll() {
  var top = (window.pageYOffset || document.documentElement.scrollTop)
  var amount = Math.round((top / max) * 100.0);
  process.setAttribute("y", (amount) + "%");
  process.innerHTML = amount + "%";
  start.setAttribute("offset", amount + "%");
  stop.setAttribute("offset", amount + "%");
}

window.addEventListener("scroll", function() {
  window.requestAnimationFrame(gradientScroll);
});
<svg height="800px" viewBox="0 0 100 700" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient x1="0" x2="0" y1="0%" y2="100%" id="grad">
      <stop id="gradstart" offset="50%" stop-color="firebrick" />
      <stop id="gradstop" offset="50%" stop-color="pink" />
    </linearGradient>
  </defs>
  <path fill="url(#grad)" d="m20,0 0,50 
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       v50
                       a10 10 0 0 0 0,20
                       h5
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-50
                       a10 10 0 0 0 0,-20
                       v-100Z"></path>
  <text id="process" text-anchor="middle" x="60" y="50">Amount</text>
</svg>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Fixing the error message "page attempting to load scripts from an unauthenticated source"

Can you help me troubleshoot an issue on my PHP page with ad scripts? I recently switched my site from HTTP to HTTPS and now the scripts are not appearing. Here is the error I found in the dev console: Failed to load resource: the server responded with ...

Are there any plugins available for integrating E-Commerce into our website?

Are there any web store plugins available that can be seamlessly integrated into an existing website? While I am aware of the abundance of WordPress plugins, as a developer creating my own applications, I prefer not to use their templates. Additionally, s ...

Generate a loop specifically designed to execute the code following the menu in the script

My website has the code snippet below: let txt_com = document.querySelector(".text_user"); let num_com_user = document.querySelector(".massage_for_user"); txt_com.addEventListener("click", function() { if (this.classList.contains("num_com_user")) { ...

Troubleshooting issues with displaying anchor tags using Jquery

I am facing an issue where I am unable to make an anchor tag visible using the .show() method in JQuery or JavaScript. The Conn Window is visible by default, and I am able to hide and display the div, but the same does not apply to the anchor tag. Interest ...

Tips for incorporating user-entered data from a text box into a JavaScript function and utilizing a loop

Although my code is functioning correctly, I am looking to make some adjustments but seem to be struggling to achieve the desired outcome. Essentially, I have two labels and an input field in which the user is prompted to enter a specific number of weeks ...

Using `ngRepeat` on a different array of values for repetition

Working with AngularJS. Here is an example of a JSON object: "skills": { "Charisma": {}, "Dexterity": { "Sk3": [ [ true, true, true, true, false ], 44 ] }, ... And the corresponding HTML: <div class="pan ...

Tips on utilizing CSS modules in React without changing class names

After starting to use css modules in my react project, I quickly realized the struggle of changing classnames to fit the requirements of css modules. For example, if we have a component using regular css: import React from 'react' import ". ...

Tips for incorporating a visible HTML comment in JSX code?

Currently implementing ReactJS and facing a requirement to insert visible HTML comments (visible in the html source) within JSX. Unsure of the correct approach to achieve this. A third party vendor necessitates this comment for processing purposes on the ...

Flexbox columns with equal widths for Internet Explorer

I am currently working on a flexbox column layout that needs to be compatible with IE10. One issue I have encountered is that IE tends to expand a flex child based on its contents, while other browsers keep each flexbox at an equal width. To address this ...

Is it possible to integrate Laravel code into CSS styling?

I am currently working on a CMS and one of the key features is allowing users to customize the design. However, I am facing a challenge in figuring out how to utilize the data retrieved from the database to implement these changes. For example, if a user ...

JQuery .hover triggering unexpectedly without actually hovering

My current code is causing $('#intro').fadeOut to activate even when I am not hovering on $('arena').hover. Are there any other issues in the code that would prevent it from working correctly? $(document).ready(function() { $(&apos ...

Every time I attempt to load and watch my video, I consistently encounter a 405 error

Having some trouble with a button meant to automatically play my video. I attempted using preload = auto without success, resulting in a 405 error. Any suggestions on how to resolve this issue? <div id="vid"> <video id=" ...

Content obscuring dropdown menu

I am currently working on a screen design that resembles the following: return ( <SafeAreaView> <View style={styles.container}> <View style={styles.topContainer}> <View style={styles.searchFieldContainer}> ...

Troubleshooting issue with CSS3 linear-gradient not functioning as expected

After watching a CodePen demo where CSS3 linear-gradient was used to create color effects, I attempted to use the same property on jsFiddle but it wasn't working. Can someone help me troubleshoot this issue? You can view the original CodePen code her ...

Interactions with jQuery and the .load() method

Encountering a new issue with jQuery that I haven't seen before. Here's the code snippet: $(document).ready(function() { $('#browser_cat a').click( function() { $('#browser_cat').hide(); $('#browser_c ...

Themed CSS for Header in HTML Tables

Looking to add some creative CSS theming to the header of a table? Unfortunately, tables don't support this natively. But fear not – there's a workaround! My solution is to insert an element BEHIND the table head and apply the desired styles t ...

Is there a way to prevent reset.css from affecting a specific element?

I am facing this issue because I am receiving stylized text from "Portable Text to React". However, the global style in my index.css file includes a css reset that eliminates all default styling applied to elements of the portable text. Is there a way to ...

HTML header with zero margin

Hi there! I am currently learning the basics of HTML and CSS. I have a question regarding creating a header with no margins on the edges. Any advice would be greatly appreciated as I haven't been able to find a clear solution. Below is my code snippet ...

Flexbox CSS Card Layout Behavior

Looking to achieve a specific design effect without relying on Semantic UI? Check out the codepen link here: https://codepen.io/anon/pen/YxBOax <div class="ui container"> <div class="ui four cards stackable"> <div class="teal card"> ...

Is it possible to make one <td> tag bold in a table if the <tr> contains two <td> tags?

I need to make the first td tag bold and apply this style to the entire table. <table> <tr> <td><strong>Cell A</strong></td> <td>Cell B</td> </tr> </table> ...