Workflow comparisons

12 old vs modern workflow CSS techniques, side by side.

Browser compatibility:

CSS feature detection without JavaScript

Widely available
Old
// 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;  }}

Range style queries without multiple blocks

Limited availability
Old
/* 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);  }}

Typed attribute values without JavaScript

Limited availability
Old
/* 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>)   );}

Inline conditional styles without JavaScript

Limited availability
Old
/* 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   );}

Reusable CSS logic without Sass mixins

Limited availability
Old
// Sass / SCSS@function fluid($min, $max) {  @return clamp( /* [!code --] */    $min,    calc($min + ($max - $min) * ((100vw - 320px) / 960)),    $max  ); /* [!code --] */}
Modern
@function --fluid( /* */--min, --max /* */)   {  @return clamp( /* [!code ++] */  var(--min), /* [!code ++] */  50vi, /* [!code ++] */  var(--max) /* [!code ++] */  );}

Lazy rendering without IntersectionObserver

Newly available
Old
// JavaScriptconst observer = new IntersectionObserver(  (entries) => {    entries.forEach(entry => {      if (entry.isIntersecting) {        renderContent(entry.target);      }    });  });
Modern
.section   {  content-visibility: auto;  contain-intrinsic-size: auto 500px;}

Scoped styles without BEM naming

Newly available
Old
.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" */

Typed custom properties without JavaScript

Newly available
Old
: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;}

Dark mode defaults without extra CSS

Widely available
Old
@media (prefers-color-scheme: dark)   {  input, select, textarea, button   {    background: #333;    color: #eee;  }  ::-webkit-scrollbar   {    ...  }}
Modern
:root   {  color-scheme: light dark;}

Controlling specificity without !important

Widely available
Old
.card .title   {  font-size: 1rem;}.page .card .title   {  font-size: 1.25rem;}.page .card .title.special   {  color: red !important;}// More selectors or !important to win
Modern
@layer base, components, utilities;@layer utilities   {  .mt-4   {    margin-top: 1rem;  }}

Theme variables without a preprocessor

Widely available
Old
// Sass/LESS: compile-time only$primary: #7c3aed;$spacing: 16px;.btn {  background: $primary;  padding: $spacing;}
Modern
:root   {  --primary: #7c3aed;  --spacing: 16px;}.btn   {  background: var(--primary);}

Nesting selectors without Sass or Less

Newly available
Old
// 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;    }  }}

Other categories

ESC