Workflow comparisons
12 old vs modern workflow CSS techniques, side by side.
Browser compatibility:
Workflow
Beginner
CSS feature detection without JavaScript
Old
// Modernizr or manual check
if (CSS.supports('display', 'grid')) {
el.classList.add('grid');
}
if (CSS.supports('display', 'grid')) {
el.classList.add('grid');
}
Modern
@supports (display: grid) {
.layout { display: grid; }
}
/* no JS required */
see modern →
.layout { display: grid; }
}
/* no JS required */
Workflow
Advanced
Range style queries without multiple blocks
Old
/* Multiple style() blocks */
@container style(--p: 51%) {}
@container style(--p: 52%) {}
/* ...for each value */
@container style(--p: 51%) {}
@container style(--p: 52%) {}
/* ...for each value */
Modern
@container style(
--progress > 50%
) {
.bar { ... }
}
see modern →
--progress > 50%
) {
.bar { ... }
}
Workflow
Intermediate
Typed attribute values without JavaScript
Old
// JS reading dataset
el.style.width =
el.dataset.pct + '%';
el.style.width =
el.dataset.pct + '%';
Modern
.bar {
width: attr(
data-pct type(<percentage>)
);
}
see modern →
width: attr(
data-pct type(<percentage>)
);
}
Workflow
Intermediate
Inline conditional styles without JavaScript
Old
// JavaScript toggling
el.classList.toggle(
'primary', isPrimary
);
el.classList.toggle(
'primary', isPrimary
);
Modern
.btn {
background: if(
style(--variant: primary):
blue; else: gray
);
}
see modern →
background: if(
style(--variant: primary):
blue; else: gray
);
}
Workflow
Intermediate
Reusable CSS logic without Sass mixins
Old
// Sass function
@function fluid($min, $max) {
@return clamp(...);
}
@function fluid($min, $max) {
@return clamp(...);
}
Modern
@function --fluid(
--min, --max
) {
@return clamp(...);
}
see modern →
--min, --max
) {
@return clamp(...);
}
Workflow
Intermediate
Lazy rendering without IntersectionObserver
Old
// JS IntersectionObserver
new IntersectionObserver(
(entries) => { /* render */ }
).observe(el);
new IntersectionObserver(
(entries) => { /* render */ }
).observe(el);
Modern
.section {
content-visibility: auto;
contain-intrinsic-size: auto 500px;
}
see modern →
content-visibility: auto;
contain-intrinsic-size: auto 500px;
}
Workflow
Advanced
Scoped styles without BEM naming
Old
// BEM: .card__title, .card__body
.card__title { … }
.card__body { … }
// or CSS Modules / styled-components */
.card__title { … }
.card__body { … }
// or CSS Modules / styled-components */
Modern
@scope (.card) {
.title { font-size: 1.25rem; }
.body { color: #444; }
}
/* .title only inside .card */
see modern →
.title { font-size: 1.25rem; }
.body { color: #444; }
}
/* .title only inside .card */
Workflow
Advanced
Typed custom properties without JavaScript
Old
// --hue was a string, no animation
:root { --hue: 0; }
hsl(var(--hue), …) /* no interpolation */
:root { --hue: 0; }
hsl(var(--hue), …) /* no interpolation */
Modern
@property --hue {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
/* animatable, validated */
see modern →
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
/* animatable, validated */
Workflow
Beginner
Dark mode defaults without extra CSS
Old
@media (prefers-color-scheme: dark) {
input, select, textarea { ... }
}
input, select, textarea { ... }
}
Modern
:root {
color-scheme: light dark;
}
see modern →
color-scheme: light dark;
}
Workflow
Intermediate
Controlling specificity without !important
Old
.card .title { ... }
.page .card .title { ... }
.page .card .title.special { color: red !important; }
.page .card .title { ... }
.page .card .title.special { color: red !important; }
Modern
@layer base, components, utilities;
@layer utilities { .mt-4 { margin-top: 1rem; } }
see modern →
@layer utilities { .mt-4 { margin-top: 1rem; } }
Workflow
Beginner
Theme variables without a preprocessor
Old
// Sass: $primary: #7c3aed;
// Compiles to static #7c3aed
.btn { background: $primary; }
// Compiles to static #7c3aed
.btn { background: $primary; }
Modern
:root {
--primary: #7c3aed;
}
.btn { background: var(--primary); }
see modern →
--primary: #7c3aed;
}
.btn { background: var(--primary); }
Workflow
Beginner
Nesting selectors without Sass or Less
Old
// requires Sass compiler
.nav {
& a { color: #888; }
}
.nav {
& a { color: #888; }
}
Modern
.nav {
& a { color: #888; }
}
/* plain .css, no build */
see modern →
& a { color: #888; }
}
/* plain .css, no build */