3

I'm trying to animate the progress bar (HTML5 <progess> tag). I managed to style the Shadow DOM elements, but I can't animate the background (repeating linear gradient). It works in Firefox, but not in Chrome and Edge.

<ins>By it does not work I mean, the striped background is shown, but it is not animated</ins>

It seems as if the @keyframe animation definition has a different scope outside of the shadow boundary.

<ins>
How can I break the scope of the Shadow DOM boundary?
Or is there a way that I can override the User Agents shadow DOM implementation?
</ins>

<ins>
I would be glad, if there is a No-JS solution
</ins>

.progress {
  position: relative;
}
.progress::before {
  content: attr(title);
  position: absolute;
  z-index: 100;
  width: 100%;
  height: 100%;
  display: -webkit-box;
  display: flex;
  -webkit-box-align: center;
          align-items: center;
  -webkit-box-pack: center;
          justify-content: center;
  text-align: center;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.4;
}

progress[value] {
  display: block;
  width: 100%;
  min-height: 4rem;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border: none;
  border-radius: 10px;
  box-shadow: inset 4px 4px 4px rgba(84, 30, 8, 0.2);
  background-color: rgba(149, 250, 61, 0.1);
  border: 1px solid #ccc;
}
progress[value]::-webkit-progress-inner-element {
  border-radius: 10px;
  overflow: hidden;
}
progress[value]::-webkit-progress-bar {
  border-radius: 10px;
  /* box-shadow: inset 4px 4px 4px rgba(84, 30, 8, 0.2); */
  background-color: transparent;
}
progress[value]::-webkit-progress-value {
  border-radius: 10px 0 0 10px / 10px 0 0 10px;
  box-shadow: inset 2px 2px 2px rgba(84, 30, 8, 0.2);
  background-color: #95fa3c;
  position: relative;
  background-image: repeating-linear-gradient(45deg, transparent 0, transparent 6px, rgba(0, 0, 0, 0.1) 6px, rgba(0, 0, 0, 0.1) 12px);
  background-size-x: 800942px; /* empirical value */
  -webkit-animation: colorrush 3s infinite linear;
          animation: colorrush 3s infinite linear;
}
progress[value]::-moz-progress-bar {
  border-radius: 10px 0 0 10px / 10px 0 0 10px;
  box-shadow: inset 2px 2px 2px rgba(84, 30, 8, 0.2);
  background-color: #95fa3c;
  position: relative;
  background-image: repeating-linear-gradient(45deg, transparent 0, transparent 6px, rgba(0, 0, 0, 0.1) 6px, rgba(0, 0, 0, 0.1) 12px);
  background-size: 800942px; /* empirical value */
  animation: colorrush 3s infinite linear;
}

@-webkit-keyframes colorrush {
  0% {
    background-color: #95fa3c;
    background-position-x: 0;
  }
  50% {
    background-color: #c4eea0;
  }
  100% {
    background-color: #95fa3c;
    background-position-x: 152px; /* empirical value */
  }
}

@keyframes colorrush {
  0% {
    background-color: #95fa3c;
    background-position-x: 0;
  }
  50% {
    background-color: #c4eea0;
  }
  100% {
    background-color: #95fa3c;
    background-position-x: 152px; /* empirical value */
  }
}
html {
  font-size: 62.5%;
  font-family: sans-serif;
}

body {
  font-size: 1.6rem;
}
<div class="progress" title="125 / 150 (83.33%)">
  <progress max="150" value="125">125 / 150</progress>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
yunzen
  • 32,854
  • 11
  • 73
  • 106
  • 1
    Seems like you are out of luck, as this [bug wont fix](https://bugs.chromium.org/p/chromium/issues/detail?id=486195). I've found this link through [this tutorial](https://css-tricks.com/html5-progress-element/) describing your problem. It seems like you said, that the scope of the shadow dom can not access this style definition. One way seems to be to set the animation to the `` element, but I don't know if this will work for your use case. – Nico O Jul 29 '20 at 12:40
  • 1
    @NicoO I found a [discussion](https://github.com/whatwg/html/issues/552) on the CSS standard specification, which seems to say the same: – yunzen Jul 29 '20 at 12:50
  • 1
    @NicoO There is also a [draft specification](https://www.w3.org/TR/2014/WD-css-scoping-1-20140403/#scope-atrule) of a `@scope` rule, which is [not implemented](https://caniuse.com/#feat=style-scoped) in any browser – yunzen Jul 29 '20 at 12:52
  • and it need to be the progres element? you can easily achieve the same with a custom code – Temani Afif Jul 29 '20 at 13:33
  • @TemaniAfif Of course I could, but I love Sematic Web – yunzen Jul 29 '20 at 13:47

2 Answers2

2

Inheritance can fix the issue. You apply the animation on the main element and you use a cascading inherit. Since it won't work with background-color I replaced the animation with a gradient one where I will also animation the position.

I also optimized the code to avoid the empircal value

progress[value] {
  display: block;
  width: 100%;
  min-height: 4rem;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border: none;
  border-radius: 10px;
  box-shadow: inset 4px 4px 4px rgba(84, 30, 8, 0.2);
  background-color:rgba(149, 250, 61, 0.1);
  border: 1px solid #ccc;
  animation: colorrush 5s infinite linear;
}
progress[value]::-webkit-progress-inner-element {
  border-radius: 10px;
  overflow: hidden;
  background-position:inherit;
}
progress[value]::-webkit-progress-bar {
  border-radius: 10px;
  background-color: transparent;
  background-position:inherit;
}
progress[value]::-webkit-progress-value {
  border-radius: 10px 0 0 10px / 10px 0 0 10px;
  box-shadow: inset 2px 2px 2px rgba(84, 30, 8, 0.2);
  background: 
    repeating-linear-gradient(45deg, transparent 0 6px, rgba(0, 0, 0, 0.1) 6px 12px),
    linear-gradient(#95fa3c,#c4eea0,#95fa3c);  
  background-size:
     calc(12px/0.707) 100%, /* 0.707 = cos(45deg)*/
     100% 800%;
  background-position:inherit;
}
progress[value]::-moz-progress-bar {
  border-radius: 10px 0 0 10px / 10px 0 0 10px;
  box-shadow: inset 2px 2px 2px rgba(84, 30, 8, 0.2);
  background:
    repeating-linear-gradient(45deg, transparent 0 6px, rgba(0, 0, 0, 0.1) 6px 12px),
    linear-gradient(#95fa3c,#c4eea0,#95fa3c);   
    background-size:
     calc(12px/0.707) 100%, /* 0.707 = cos(45deg)*/
     100% 800%;
  background-position:inherit;
}


@keyframes colorrush {
  0% {
    background-position:0 0;
  }
  100% {
    /* the 10 multiplier will allow me to use a big duration and be able
       to slow down the color animation
    */
    background-position: calc(10*(12px/0.707)) 100%;
  }
}
html {
  font-size: 62.5%;
  font-family: sans-serif;
}

body {
  font-size: 1.6rem;
}
<progress max="150" value="125">125 / 150</progress>

Related question to understand the math behind the background-size: Animated CSS background pattern, sliding infinitely

Another related one to understand the use of percentage value with background-position: Using percentage values with background-position on a linear-gradient

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
-1

As far as I know, you can't keyframe CSS gradients. What you can do is animate it with JS:

var rgb1 = {
  r: 0,
  g: 0,
  b: 0
}
var rgb2 = {
  r: 0,
  g: 0,
  b: 0
}
var increment1 = {
  r: 1,
  g: 0,
  b: 0
}
var increment2 = {
  r: 0,
  g: 0,
  b: 0
}
setInterval(function() {
  rgb1.r += increment1.r;
  rgb1.g += increment1.g;
  rgb1.b += increment1.b;
  rgb2.r += increment2.r;
  rgb2.g += increment2.g;
  rgb2.b += increment2.b;
  if (rgb1.r > 255) rgb1.r = 0; //rgb is range 0-255
  if (rgb1.g > 255) rgb1.g = 0;
  if (rgb1.b > 255) rgb1.b = 0;
  if (rgb2.r > 255) rgb2.r = 0;
  if (rgb2.g > 255) rgb2.g = 0;
  if (rgb2.b > 255) rgb2.b = 0;
  document.getElementById("anim1").style.backgroundImage = `linear-gradient(to top, rgb(${rgb1.r}, ${rgb1.g}, ${rgb1.b}), rgb(${rgb2.r}, ${rgb2.g}, ${rgb2.b}))`;
}, 10);
div {
  width: 256px;
  height: 256px;
}
input[type=range] {
width: 256px;
}
<div id="anim1"></div>
<input type="range" min="0" max="255" value="1" oninput="increment1.r = parseInt(this.value);document.getElementById('inc1r').innerHTML = 'Increment R1: ' + this.value"> <label id="inc1r">Increment R1: 1</label><br>
<input type="range" min="0" max="255" value="0" oninput="increment1.g = parseInt(this.value);document.getElementById('inc1g').innerHTML = 'Increment G1: ' + this.value"> <label id="inc1g">Increment G1: 0</label><br>
<input type="range" min="0" max="255" value="0" oninput="increment1.b = parseInt(this.value);document.getElementById('inc1b').innerHTML = 'Increment B1: ' + this.value"> <label id="inc1b">Increment B1: 0</label><br>

<input type="range" min="0" max="255" value="0" oninput="increment2.r = parseInt(this.value);document.getElementById('inc2r').innerHTML = 'Increment R2: ' + this.value"> <label id="inc2r">Increment R2: 0</label><br>
<input type="range" min="0" max="255" value="0" oninput="increment2.g = parseInt(this.value);document.getElementById('inc2g').innerHTML = 'Increment G2: ' + this.value"> <label id="inc2g">Increment G2: 0</label><br>
<input type="range" min="0" max="255" value="0" oninput="increment2.b = parseInt(this.value);document.getElementById('inc2b').innerHTML = 'Increment B2: ' + this.value"> <label id="inc2b">Increment B2: 0</label><br>
Axwabo
  • 49
  • 7
  • I can keyframe animate the `background-position` of a gradient, but not the gradient itself. I'm aware of this. But this is not, what I'm trying. The problem lies with the Shadow DOM, not the animation itself – yunzen Jul 29 '20 at 12:45