skip to Main Content

I’m trying to display a ::before pseudo-element on a <progress> bar like a label with the value attribute. Currently, I have a solution that display the ::before in Brave (v1.61.109), while it doesn’t appear in Safari(17.1.2) and Firefox (121.0). However, I still encounter an issue regarding the consideration of the height of the pseudo-element. I think that naturally the pseudo-element itself has no direct impact on the flow. However, I would like to find an effective workaround.

In addition to displaying the element, I’d like it to be considered in the height of the parent box, specifically .card. I’m looking for a dynamic solution.

Current result on Brave

Here is the HTML code:

<div class="card">
    <p>Item 1</p>
    <p>Item 2</p>
    <progress max="100" value="100"></progress>
</div>

And here is the associated CSS:

.card {
    display: flex;
    flex-direction: column;
    width: 300px;
    background: beige;
}

progress[value] {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    border: none;
    width: 100%;
}

progress {
    position: relative;
}

progress:before {
    content: attr(value) '% some text here';
    font-size: 2rem;
    color: black;
    text-align: center;
    height: 2rem;
    line-height: normal;
}

progress[value]::-webkit-progress-value {
    background-color: green;
    border-radius: .2rem;
}

progress[value]::-moz-progress-bar {
    background-color: green;
    border-radius: .2rem;
}

Does anyone have ideas on how to solve this display ?

2

Answers


  1. Chosen as BEST ANSWER

    Issue with the Use of Pseudo-elements in CSS

    1. The before pseudo-element is positioned before the content inside the tag, but not before the tag's opening. As a result, the flex rule never applies correctly, as it targets the direct children of .car but the :befoer pseudo-element is the first child of <progress>.
    2. Moreover, as stated in the documentation here, it's not possible to use a pseudo-element on an HTML element whose content does not appear in the document tree.
    3. Additionally, content generated by CSS is not accessible to screen readers. It is crucial that CSS-generated content does not impact page understanding.

    The solution, therefore, involves modifying the HTML code as follows:

    <div class="card">
        <p>Item 1</p>
        <p>Item 2</p>
        <label for="progress">% some text here</label>
        <progress id="progress" max="100" value="100">toto</progress>
    </div>
    

    A bit of JavaScript like this:

    const progressElement = document.getElementById('progress');
    const progressLabelElement = document.querySelector('[for=progress]');
    const initialTextContent = progressLabelElement.textContent;
    
    function updateValue() {
        progressLabelElement.textContent = progressElement.value + initialTextContent;
    }
    // this is not needed if the value does not change
    progressElement.addEventListener('input', updateValue);
    
    updateValue()
    
    

  2. I came across your question and the so far the only thing i can think of as a workaround is to use a pinch of javascript to update the progress % instead of the attr() in CSS.

    There are 2 reasons why I chose JS,

    1. It’s simple.
    2. We can side step a couple of problems that occurs using the :before pseudo element. (::before pseudo elements doesn’t seem to work well in Safari and the pseudo element is not considered within the height of the parent element.)

    Now, using a div, <div id="progress-label"></div>, to contain the progress %, we can ensure that the progress % sits within the parent (.card) element since it’s a block-level element and statically positioned. The JS snippet will just update the text content within the div in the DOM upon execution.

    You can try using this template and edit it to meet your needs.
    Do check out the codepen here for a demo.
    https://codepen.io/Jun-Wen-Soh/pen/ZEPQgGR

    HTML

    <div class="card">
      <p>Item 1</p>
      <p>Item 2</p>
      <div class="wrapper">
        <div id="progress-label"></div>
        <label for="progress-bar">Progress:</label>
        <progress id="progress-bar" max="100" value="100"></progress>
      </div>
    </div>
    

    CSS

    .card {
      display: flex;
      flex-direction: column;
      width: 300px;
      background: beige;
      padding: 15px;
    }
    
    .wrapper {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }
    
    progress[value] {
      -webkit-appearance: none;
      -moz-appearance: none;
      appearance: none;
      border: none;
      width: 100%;
    }
    
    progress {
      position: relative;
    }
    
    #progress-label {
      font-size: 2rem;
      color: black;
      text-align: left;
      height: 2rem;
      line-height: normal;
    }
    
    progress[value]::-webkit-progress-value {
      background-color: green;
      border-radius: .2rem;
    }
    
    progress[value]::-moz-progress-bar {
      background-color: green;
      border-radius: .2rem;
    }
    

    JS

    const progressBar = document.getElementById('progress-bar');
    const progressLabel = document.getElementById('progress-label');
    
    progressLabel.textContent = progressBar.value + '% some text here';
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search