CSS Houdini is a W3C effort to define lower-level CSS DOM APIs for authors to understand, recreate, and extend high-level CSS authoring features.
definition: wiki.mozilla.org/CSS/Houdini
or
.app__header {
background-image: linear-gradient(#e66465, #9198e5);
}
Having individual access to the variables means
we can update the gradient dynamically!
:root {
--gradientStart: #e66465;
--gradientEnd: #9198e5;
}
.app__header {
background-image: linear-gradient(
var(--gradientStart),
var(--gradientEnd)
);
}
document.addEventListener('mousemove', (e) => {
// Translate mouseX into degrees for HSL
const xDegree = getXDegree(e.clientX) // 0 - 360
// Update custom properties
setRootStyle('--grad-start', xDegree) // => 180
setRootStyle('--grad-end', xDegree + 120) // => 300
})
Magic Animated Gradient
<html style="--grad-start: 180; --grad-end: 300">
body {
// The H in HSL is a value in the range 0...360
background: linear-gradient(
hsl(var(--grad-start), 100%, 75%),
hsl(var(--grad-end), 100%, 50%)
)
}
CSS
JS
HTML
Codepen: Magic Gradient
p {
&::before {
content: 'hsl(' var(--grad-start) ', 100%, 75%)';
}
&::after {
content: 'hsl(' var(--grad-end) ', 100%, 50%)';
}
}
Magic Gradient: Pseudo-content & Custom Props
p {
// Map custom-idents hueStart & hueEnd to Custom Props
counter-reset:
hueStart var(--grad-start)
hueEnd var(--grad-end);
// Use the value returned by counter(custom-ident)
&::before {
content: 'hsl(' counter(hueStart) ', 100%, 75%)';
}
&::after {
content: 'hsl(' counter(hueEnd) ', 100%, 50%)';
}
}
Magic Gradient: Pseudo-content & Custom Props
🤯
telling machines what to do, letting them figure out to how to do it
This won't work
😭
:root {
--gradientStart: #61BFD9;
--gradientEnd: #0551B4;
}
@keyframes interpolate {
from {
--gradientStart: #61BFD9;
--gradientEnd: #0551B4;
}
to {
--gradientStart: #15AF88;
--gradientEnd: #6C429A;
}
}
.app__header {
animation: interpolate 3s ease-in-out;
}
The browser knows how to turn one number into another
But if the variable type isn't understood as a number…
¯\_(ツ)_/¯
visit: houdini.glitch.me
visit: houdini.glitch.me
visit: houdini.glitch.me
Houdini Ingredients
el.attributeStyleMap.set('opacity', CSS.number(.5));
el.attributeStyleMap.get('opacity');
// CSSUnitValue {
value: 0.5,
unit: 'number'
};
attributeStyleMap
el.style.opacity = 0.3;
typeof el.style.opacity === 'string'
Current CSSOM
Typed OM
const cs = document.querySelector('.content').computedStyleMap();
cs.get('background-position').x;
// CSSUnitValue {
// value: 50,
// unit: 'percent',
// }
cs.get('background-position').y;
// CSSMathSum {
// operator: 'sum',
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -10, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
// },
// }
.content {
background-position: center bottom 10px;
}
sample.css
sample.js
computedStyleMap
CSS.registerProperty({
name: "--gradientStart",
syntax: "<color>",
initialValue: "#61BFD9",
inherits: true
});
@property --gradientStart {
syntax: "<color>";
initialValue: "#61BFD9";
inherits: true;
};
Proposed CSS syntax
Current JS syntax
Polyfill: PostCSS Register Property
@snugug
visit: extra.css
h1 {
--extra-confettiNumber: 30;
--extra-confettiLengthVariance: 15;
--extra-confettiWeightVariance: 4;
background: paint(extra-confetti);
}
watch: Custom Layout Walkthrough
Part 4:
visit: ishoudinireadyyet.com
visit: ishoudinireadyyet.com
Part 5:
Explainer: Houdini Spellbook
Aggregator: Awesome-CSS-Houdini
Examples: css-houdini.rocks
examples: Google Chrome Labs
in-depth articles: developers.google.com
Part 6:
Shareable, reusable, drop-in code: extra.css
<header class="app__header">
<figure class="header__jeffs">
<img
src="/img/jeff1.jpg"
alt="Jeff YEAH"
data-grad-start="#61bfd9"
data-grad-end="#0551b4"
/>
<img
src="/img/jeff2.jpg"
alt="Jeff again"
data-grad-start="#15AF88"
data-grad-end="#6C429A"
/>
<img
src="/img/jeff3.jpg"
alt="It's all Jeff"
data-grad-start="#EEA031"
data-grad-end="#930560"
/>
</figure>
</header>
2014-2019
What Houdini really means
@oliverturner