Selectors comparisons

9 old vs modern selectors CSS techniques, side by side.

Browser compatibility:

CSS Floating Labels with :placeholder-shown

Widely available
Old way
/* .filled class toggled by JS on every keystroke */.field.filled label,.field input:focus + label   {  translate: 0 -1.4rem;  font-size: .75rem;}// JS: toggle .filled on input input.addEventListener('input', () => {   input.closest('.field').classList.toggle('filled', input.value.length > 0); });
Modern
input:not(:placeholder-shown) + label,input:focus + label   {  translate: 0 -1.4rem;  font-size: .75rem;}/* no .filled class, no JS */

CSS :playing, :paused, :muted Pseudo-Classes for Media

Limited availability
Old way
const video = document.querySelector('video');const wrap = video.closest('.player');video.addEventListener('play', () => wrap.classList.add('is-playing'));video.addEventListener('pause', () => wrap.classList.remove('is-playing'));video.addEventListener('waiting', () => wrap.classList.add('is-buffering'));video.addEventListener('playing', () => wrap.classList.remove('is-buffering'));video.addEventListener('volumechange', () => {  wrap.classList.toggle('is-muted', video.muted);});
Modern
video:paused + .controls .play-icon { display: block; }video:playing + .controls .pause-icon { display: block; }video:buffering + .controls .spinner { display: block; }video:muted + .controls .muted-badge { display: block; }.player:has(video:playing) {  outline: 2px solid currentColor;}

CSS Custom Highlight API: ::highlight() Pseudo-Element

Newly available
Old way
function highlight(el, term)   {  el.innerHTML = el.innerHTML    .replace(       new RegExp(term, 'gi'),       '<mark>$&</mark>'     );}
Modern
/* CSS */::highlight(search)   {  background: yellow;}/* JS — no DOM changes */const range = new Range();range.setStart(node, startOffset);range.setEnd(node, endOffset);CSS.highlights.set(   'search', new Highlight(range));

CSS Form Validation Styles with :user-invalid

Newly available
Old way
/* only style after .touched class is added by JS */input.touched:invalid   {  border-color: red;}input.touched:valid   {  border-color: green;}// JS: add .touched class on blur input.addEventListener('blur', () => {   input.classList.add('touched'); });
Modern
input:user-invalid   {  border-color: red;}input:user-valid   {  border-color: green;}/* no JS, no .touched class */

CSS Scroll Spy with :target-current (No IntersectionObserver)

Limited availability
Old way
/* JS IntersectionObserver approach */const observer = new IntersectionObserver(   (entries) =>  {  entries.forEach(entry =>  {    const link = document         .querySelector(`a[href="# // [!code --]        ${entry.target.id}"]`);       link.classList.toggle(         'active',        entry.isIntersecting);  });},   {  threshold: 0.5});document.querySelectorAll('section')   .forEach(s =>    observer.observe(s));
Modern
.scroller   {  overflow-y: auto;}nav a:target-current   {  color: var(--accent);}

Low-Specificity CSS Reset with :where()

Widely available
Old way
ul, ol   {  margin: 0;  padding-left: 1.5rem;}/* Specificity (0,0,2). Component .list { padding: 0 } loses. */
Modern
:where(ul, ol)   {  margin: 0;  padding-inline-start: 1.5rem;}/* Specificity 0. .list { padding: 0 } wins. */

Group CSS Selectors with :is() and :where()

Widely available
Old way
.card h1, .card h2, .card h3, .card h4   {  margin-bottom: 0.5em;}// Same prefix repeated for every selector
Modern
.card :is(h1, h2, h3, h4)   {  margin-bottom: 0.5em;}

CSS Focus Styles with :focus-visible

Widely available
Old way
button:focus   {  outline: 2px solid blue;}// Outline appears on mouse click. Often removed with outline: none.
Modern
button:focus-visible   {  outline: 2px solid var(--focus-color);}

CSS :has() Selector: Select Parent by Child

Newly available
Old way
// Watch for changes, find parentdocument.querySelectorAll('input')  .forEach(input => {    input.addEventListener('invalid', () => {      input.closest('.form-group')        .classList.add('has-error');    });  });
Modern
.form-group:has(input:invalid)   {  border-color: red;  background: #fff0f0;}

Other categories

ESC