I've successfully created a css variable animations, using the experimental @property
feature.
IMPORTANT: this only works in Chrome, Opera or Edge compatibility table
document.addEventListener('DOMContentLoaded', () => {
const rangeInput = document.querySelector('#scale');
const htmlElement = document.querySelector('#html-element');
rangeInput.addEventListener('change', (e) => {
htmlElement.style.setProperty(
'--html-element-scale',
(e.target.value / 100).toFixed(2)
);
});
});
@property --html-element-background-color {
syntax: '<color>';
inherits: false;
initial-value: #00ff00;
}
@property --html-element-rotate {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@property --html-element-scale {
syntax: '<number>';
inherits: false;
initial-value: 1;
}
#html-element {
transform: rotate(var(--html-element-rotate))
scale(var(--html-element-scale));
border: 1px solid #000;
height: 100px;
width: 100px;
background-color: var(--html-element-background-color);
animation: anim 1s ease-in infinite alternate;
}
@keyframes anim {
100% {
--html-element-rotate: 180deg;
--html-element-background-color: #ff00ff;
}
}
<div id="html-element">HTML Element</div>
<div id="range-container">
<input
type="range"
id="scale"
name="scale"
min="0"
max="100"
value="100"
/>
<label for="scale">Scale</label>
</div>
But when wrapping it inside of a web-component, only the css values are changed, but the values of the css var's are not animated. Furthermore the rotation doesn't work, until you change the scale slider or alternatively ad a :host {--html-element-scale: 1}
as default value... which is weird.
const html = `
<style>
@property --web-component-background-color {
syntax: '<color>';
inherits: false;
initial-value: #00ff00;
}
@property --web-component-rotate {
syntax: '<angle>';
inherits: false;
initial-value: 0deg;
}
@property --web-component-scale {
syntax: '<number>';
inherits: false;
initial-value: 1;
}
#web-component {
transform: scale(var(--web-component-scale)) rotate(var(--web-component-rotate));
border: 1px solid #000;
height: 100px;
width: 100px;
background-color: var(--web-component-background-color);
animation: anim 1s ease-in infinite alternate;
}
@keyframes anim {
100% {
--web-component-rotate: 180deg;
--web-component-background-color: #ff00ff;
}
}
</style>
<div id="web-component">Web Component</div>
<div id="range-container">
<input type="range" id="scale" name="scale" min="0" max="100" value="100" />
<label for="scale">Scale</label>
</div>`;
customElements.define(
'web-component',
class WebComponent extends HTMLElement {
shadowRoot = this.attachShadow({ mode: 'open' });
constructor() {
super();
let template = document.createElement('template');
template.innerHTML = html;
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
connectedCallback() {
const rangeInput = this.shadowRoot.querySelector('#scale');
const webComponent = this.shadowRoot.querySelector('#web-component');
rangeInput.addEventListener('change', (e) => {
webComponent.style.setProperty(
'--web-component-scale',
(e.target.value / 100).toFixed(2)
);
});
}
}
);
<web-component></web-component>
Last but not least, if you copy the stylesheet from the web-component into the regular dom, so that the web-component inherits the css, it works again.
The question is, is this a bug due to the experimental stage of the feature or am I doing something wrong with implementing it in a web-component?