Workflow comparisons
12 old vs modern workflow CSS techniques, side by side.
Browser compatibility:
CSS Feature Detection with @supports (No Modernizr)
Widely availableOld way
// JS: detect support, add classif (CSS.supports('container-type', 'inline-size')) { document.documentElement.classList.add('has-container-queries');}Modern
@supports (display: grid) { .layout { display: grid; }}CSS Range Style Queries: style(--progress > 50%)
Limited availabilityOld way
/* Multiple discrete checks */@container style(--progress: 0%) { .bar { background: red; }}@container style(--progress: 25%) { .bar { background: orange; }}@container style(--progress: 50%) { .bar { background: yellow; }}/* ... repeat for every threshold *//* Or use JavaScript to set classes *//* based on the numeric value */Modern
@container style( /* [!code ++] */--progress > 75% /* */) { .bar { background: var(--green); }}CSS attr() with Type: Typed HTML Attribute Values
Limited availabilityOld way
/* JS: read data attr and set style */document.querySelectorAll('.bar') .forEach(el => { const pct = el.dataset.pct; el.style.width = pct + '%'; el.style.setProperty( '--pct', pct );});Modern
.bar { width: attr( data-pct type(<percentage>) );}CSS if() Function for Inline Conditional Styles
Limited availabilityOld way
/* Multiple class variants */.btn { background: gray;}.btn.primary { background: blue;}.btn.danger { background: red;}/* Plus JS to manage state */el.classList.toggle( 'primary', isPrimary );/* Duplicated rules per variant */Modern
.btn { background: if( style(--variant: primary): blue; else: gray );}CSS @function for Reusable Logic (No Sass Mixins)
Limited availabilityOld way
// Sass / SCSS@function fluid($min, $max) { @return clamp( /* [!code --] */ $min, calc($min + ($max - $min) * ((100vw - 320px) / 960)), $max ); /* [!code --] */}Modern
@function --fluid( /* */ --min <length>, /* */ --max <length> /* */) returns <length> { result: clamp( /* [!code ++] */ var(--min), /* [!code ++] */ 50vi, /* [!code ++] */ var(--max) /* [!code ++] */ );}CSS Lazy Rendering with content-visibility: auto
Newly availableOld way
// JavaScriptconst observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { renderContent(entry.target); } }); });Modern
.section { content-visibility: auto; contain-intrinsic-size: auto 500px;}CSS @scope: Scoped Styles Without BEM
Newly availableOld way
.card__title { font-size: 1.25rem; margin-bottom: 0.5rem;}.card__body { color: #444;}/* HTML: class="card__title" */Modern
@scope (.card) { .title { font-size: 1.25rem; margin-bottom: 0.5rem; } .body { color: #444; }}/* HTML: class="card", class="title" */CSS @property for Typed Custom Properties
Newly availableOld way
:root { --hue: 0;}.wheel { background: hsl(var(--hue), 80%, 50%); transition: --hue .3s; /* ignored, not interpolable */}Modern
@property --hue { syntax: "<angle>"; inherits: false; initial-value: 0deg;}.wheel { background: hsl(var(--hue), 80%, 50%); transition: --hue .3s;}CSS Dark Mode with color-scheme: light dark
Widely availableOld way
@media (prefers-color-scheme: dark) { input, select, textarea, button { background: #333; color: #eee; } ::-webkit-scrollbar { ... }}Modern
:root { color-scheme: light dark;}Control CSS Specificity with @layer (No !important)
Widely availableOld way
.card .title { font-size: 1rem;}.page .card .title { font-size: 1.25rem;}.page .card .title.special { color: red !important;}// More selectors or !important to winModern
@layer base, components, utilities;@layer utilities { .mt-4 { margin-top: 1rem; }}CSS Custom Properties for Theme Variables
Widely availableOld way
// Sass/LESS: compile-time only$primary: #7c3aed;$spacing: 16px;.btn { background: $primary; padding: $spacing;}Modern
:root { --primary: #7c3aed; --spacing: 16px;}.btn { background: var(--primary);}Native CSS Nesting (No Sass or Less)
Newly availableOld way
// nav.scss, requires Sass compiler.nav { display: flex; gap: 8px; & a { color: blue; }}Modern
/* nav.css, plain CSS, no compiler */.nav { display: flex; gap: 8px; & a { color: #888; text-decoration: none; &:hover { color: white; } }}