Updated for 2026 Stop writing CSS like it's 2015. Modern CSS code snippets, side by side with the old hacks they replace. Every technique you still Google has a clean, native replacement now.
Updated for 2026 Stop writing CSS like it's 2015. Modern CSS code snippets, side by side with the old hacks they replace. Every technique you still Google has a clean, native replacement now.
Old .child { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); }
Modern .parent { display: grid; place-items: center; } /* child needs nothing. */
All comparisons 20 snippets Browser compatibility: All Newly available Widely available Limited
Color Intermediate Perceptually uniform colors with oklch
Old --brand: #4f46e5; --brand-light: #818cf8; --brand-dark: #3730a3; /* guess-and-check each shade */
Modern --brand: oklch(0.55 0.2 264); --brand-light: oklch(0.75 0.2 264); --brand-dark: oklch(0.35 0.2 264); /* only L changes, same perceived hue */
see modern → 90%
Color Intermediate Frosted glass effect without opacity hacks
Old .card::before { content: ''; background-image: url(bg.jpg); filter: blur(12px); z-index: -1; }
Modern .glass { backdrop-filter: blur(12px); background: rgba(255,255,255,.1); }
see modern → 96%
Layout Beginner Preventing layout shift from scrollbar appearance
Old body { overflow-y: scroll; } /* or hardcode the scrollbar width */ body { padding-right: 17px; }
Modern body { scrollbar-gutter: stable; } /* scrollbar space always reserved */
see modern → 90%
Layout Beginner Preventing scroll chaining without JavaScript
Old // JS: block page scroll when inside modal modal.addEventListener('wheel', e => e.preventDefault(), { passive: false })
Modern .modal-content { overflow-y: auto; overscroll-behavior: contain; } /* page stays still */
see modern → 96%
Layout Beginner Responsive images without the background-image hack
Old .card-image { background-image: url(...); background-size: cover; background-position: center; }
Modern img { object-fit: cover; width: 100%; height: 200px; }
see modern → 96%
Selector Beginner Form validation styles without JavaScript
Old // JS: add .touched on blur el.addEventListener('blur', () => el.classList.add('touched')) /* .touched:invalid { color: red } */
Modern input:user-invalid { border-color: red; } input:user-valid { border-color: green; }
see modern → 85%
Layout Beginner Auto-growing textarea without JavaScript
Old // JS: resize on every keystroke el.addEventListener('input', () => { el.style.height = 'auto'; el.style.height = el.scrollHeight + 'px'; })
Modern textarea { field-sizing: content; min-height: 3lh; } /* grows with content, no JS */
see modern → 73%
Animation Beginner Smooth height auto animations without JavaScript
Old // measure, set px, then snap to auto el.style.height = el.scrollHeight + 'px'; el.addEventListener('transitionend', ...)
Modern :root { interpolate-size: allow-keywords; } .accordion { height: 0; overflow: hidden; transition: height .3s ease; } .accordion.open { height: auto; }
see modern → 69%
Workflow Advanced Range style queries without multiple blocks
Old /* Multiple style() blocks */ @container style(--p: 51%) {} @container style(--p: 52%) {} /* ...for each value */
Modern @container style( --progress > 50% ) { .bar { ... } }
see modern → 88%
Animation Intermediate Sticky & snapped element styling without JavaScript
Old window.addEventListener( 'scroll', () => { /* check position */ });
Modern @container scroll-state( stuck: top ) { .header { ... } }
see modern → 50%
Workflow Intermediate Typed attribute values without JavaScript
Old // JS reading dataset el.style.width = el.dataset.pct + '%';
Modern .bar { width: attr( data-pct type(<percentage>) ); }
see modern → 42%
Workflow Intermediate Inline conditional styles without JavaScript
Old // JavaScript toggling el.classList.toggle( 'primary', isPrimary );
Modern .btn { background: if( style(--variant: primary): blue; else: gray ); }
see modern → 35%
Workflow Intermediate Reusable CSS logic without Sass mixins
Old // Sass function @function fluid($min, $max) { @return clamp(...); }
Modern @function --fluid( --min, --max ) { @return clamp(...); }
see modern → 67%
Layout Beginner Corner shapes beyond rounded borders
Old .card { clip-path: polygon( ... /* 20+ points */ ); }
Modern .card { border-radius: 2em; corner-shape: squircle; }
see modern → 67%
Animation Advanced Responsive clip paths without SVG
Old .shape { clip-path: path( 'M0 200 L100 0...' ); }
Modern .shape { clip-path: shape( from 0% 100%, ... ); }
see modern → 96%
Selector Intermediate Scroll spy without IntersectionObserver
Old const observer = new IntersectionObserver(cb); /* 15+ lines of JS */
Modern nav a:target-current { color: var(--accent); }
see modern → 48%
Layout Beginner Filling available space without calc workarounds
Old .full { width: calc(100% - 40px); /* or width: 100% and overflow */ }
Modern .full { width: stretch; } /* fills container, keeps margins */
see modern → 90%
Animation Intermediate Staggered animations without nth-child hacks
Old li:nth-child(1) { --i: 0; } li:nth-child(2) { --i: 1; } li:nth-child(3) { --i: 2; } /* repeat for every item… */
Modern li { transition-delay: calc(0.1s * (sibling-index() - 1)); }
see modern → 70%
Layout Advanced Carousel navigation without a JavaScript library
Old // Swiper.js or Slick carousel new Swiper('.carousel', { navigation: { /* … */ }, pagination: { /* … */ }});
Modern .carousel::scroll-button(right) { content: "➡"; } .carousel li::scroll-marker { content: ''; }
see modern → 72%
Typography Beginner Vertical text centering without padding hacks
Old .btn { padding: 10px 20px; /* looks off-center, tweak top/bottom */ padding-top: 8px; /* hack */ }
Modern h1, button { text-box: trim-both cap alphabetic; } /* true optical centering */
see modern → 79%
Layout Intermediate Hover tooltips without JavaScript events
Old // JS: mouseenter + mouseleave btn.addEventListener('mouseenter', () => showTooltip()) /* + focus, blur, positioning */
Modern <button interestfor="tip">Hover me</button> <div id="tip" popover=hint> Tooltip content </div>
see modern → 86%
Layout Beginner Modal controls without onclick handlers
Old <button onclick=" document.querySelector('#dlg') .showModal()">Open</button>
Modern <button commandfor="dlg" command="show-modal">Open</button> <dialog id="dlg">...</dialog>
see modern → 72%
Layout Beginner Dialog light dismiss without click-outside listeners
Old // JS: listen for click on ::backdrop dialog.addEventListener('click', (e) => { /* check bounds */ })
Modern <dialog closedby="any"> Click outside to close </dialog> /* no JS listeners */
see modern → 72%
Layout Intermediate Customizable selects without a JavaScript library
Old // Select2 or Choices.js new Choices('#my-select'); /* rebuilds entire DOM */
Modern select, select ::picker(select) { appearance: base-select; }
see modern → 96%
Color Intermediate Vivid colors beyond sRGB
Old .hero { color: rgb(200, 80, 50); } /* sRGB only, washed on P3 */
Modern .hero { color: oklch(0.7 0.25 29); } /* or color(display-p3 1 0.2 0.1) */
see modern → 90%
Color Advanced Color variants without Sass functions
Old /* Sass: lighten($brand, 20%), darken($brand, 10%) */ .btn { background: #e0e0e0; }
Modern .btn { background: oklch(from var(--brand) calc(l + 0.2) c h); }
see modern → 87%
Typography Beginner Multiline text truncation without JavaScript
Old /* JS: slice text by chars/words, add "..." */ .card-title { overflow: hidden; }
Modern .card-title { display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; }
see modern → 96%
Typography Beginner Drop caps without float hacks
Old .drop-cap::first-letter { float: left; font-size: 3em; line-height: 1; }
Modern .drop-cap::first-letter { initial-letter: 3; }
see modern → 91%
Layout Beginner Positioning shorthand without four properties
Old .overlay { top: 0; right: 0; bottom: 0; left: 0; }
Modern .overlay { position: absolute; inset: 0; }
see modern → 93%
Workflow Intermediate Lazy rendering without IntersectionObserver
Old // JS IntersectionObserver new IntersectionObserver( (entries) => { /* render */ } ).observe(el);
Modern .section { content-visibility: auto; contain-intrinsic-size: auto 500px; }
see modern → 93%
Layout Beginner Dropdown menus without JavaScript toggles
Old .menu { display: none; } .menu.open { display: block; } /* + JS: click, clickOutside, ESC, aria */
Modern button[popovertarget=menu] { } #menu[popover] { position: absolute; }
see modern → 86%
Layout Advanced Tooltip positioning without JavaScript
Old /* Popper.js / Floating UI: compute rect, position: fixed, update on scroll */ .tooltip { position: fixed; }
Modern .trigger { anchor-name: --tip; } .tooltip { position-anchor: --tip; top: anchor(bottom); }
see modern → 77%
Workflow Advanced Scoped styles without BEM naming
Old // BEM: .card__title, .card__body .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 → 84%
Workflow Advanced Typed custom properties without JavaScript
Old // --hue was a string, no animation :root { --hue: 0; } hsl(var(--hue), …) /* no interpolation */
Modern @property --hue { syntax: "<angle>"; inherits: false; initial-value: 0deg; } /* animatable, validated */
see modern → 92%
Animation Beginner Independent transforms without the shorthand
Old .icon { transform: translateX(10px) rotate(45deg) scale(1.2); } /* change one = rewrite all */
Modern .icon { translate: 10px 0; rotate: 45deg; scale: 1.2; } /* animate any one without touching the rest */
see modern → 92%
Animation Intermediate Animating display none without workarounds
Old // wait for transitionend then display:none el.addEventListener('transitionend', …) visibility + opacity + pointer-events
Modern .panel { transition: opacity .2s, overlay .2s; transition-behavior: allow-discrete; } .panel.hidden { opacity: 0; display: none; } /* no JS wait or visibility hack */
see modern → 85%
Animation Intermediate Entry animations without JavaScript timing
Old // add class after paint requestAnimationFrame(() => { el.classList.add('visible'); });
Modern .card { transition: opacity .3s, transform .3s; } .card { @starting-style { opacity: 0; transform: translateY(10px); } } /* no rAF/setTimeout */
see modern → 85%
Animation Advanced Page transitions without a framework
Old // Barba.js or React Transition Group Barba.init({ … }) transition hooks + duration state
Modern document.startViewTransition(() => updateDOM()); .hero { view-transition-name: hero; } /* no Barba, no React TG */
see modern → 89%
Layout Intermediate Scroll snapping without a carousel library
Old // Slick, Swiper, or scroll/touch JS $('.carousel').slick({ … }) touchstart / scroll handlers
Modern .carousel { scroll-snap-type: x mandatory; } .carousel > * { scroll-snap-align: start; } /* no lib, no touch handlers */
see modern → 96%
Typography Beginner Balanced headlines without manual line breaks
Old // manual <br> or Balance-Text.js h1 { text-align: center; } .balance-text /* JS lib */
Modern h1, h2 { text-wrap: balance; } /* no br or JS */
see modern → 87%
Typography Beginner Font loading without invisible text
Old @font-face { ... } /* Default: invisible text until load */
Modern @font-face { font-family: "MyFont"; font-display: swap; }
see modern → 96%
Typography Intermediate Multiple font weights without multiple files
Old @font-face { font-weight: 400; } @font-face { font-weight: 700; } /* 4+ files */
Modern @font-face { font-family: "MyVar"; src: url("MyVar.woff2"); font-weight: 100 900; }
see modern → 96%
Workflow Beginner Dark mode defaults without extra CSS
Old @media (prefers-color-scheme: dark) { input, select, textarea { ... } }
Modern :root { color-scheme: light dark; }
see modern → 93%
Color Intermediate Dark mode colors without duplicating values
Old @media (prefers-color-scheme: dark) { color: #eee; }
Modern color: light-dark(#111, #eee); color-scheme: light dark;
see modern → 83%
Selector Intermediate Low-specificity resets without complicated selectors
Old .reset ul, .reset ol { ... } /* or (0,0,1) specificity, still wins */
Modern :where(ul, ol) { margin: 0; padding-inline-start: 1.5rem; }
see modern → 96%
Layout Intermediate Direction-aware layouts without left and right
Old margin-left: 1rem; padding-right: 1rem; [dir="rtl"] .box { margin-right: ... }
Modern margin-inline-start: 1rem; padding-inline-end: 1rem; border-block-start: 1px solid;
see modern → 96%
Layout Beginner Naming grid areas without line numbers
Old float: left; /* clearfix, margins */ grid-column: 1 / 3; grid-row: 2;
Modern .layout { display: grid; grid-template-areas: "header header" "sidebar main" "footer footer"; }
see modern → 96%
Layout Advanced Aligning nested grids without duplicating tracks
Old .child-grid { grid-template-columns: 1fr 1fr 1fr; /* duplicate parent tracks */ }
Modern .child-grid { display: grid; grid-template-columns: subgrid; }
see modern → 88%
Layout Intermediate Modal dialogs without a JavaScript library
Old .overlay { position: fixed; z-index: 999; } /* + JS: open/close, ESC, focus trap */
Modern dialog { padding: 1rem; } dialog::backdrop { background: rgb(0 0 0 / .5); }
see modern → 96%
Color Beginner Styling form controls without rebuilding them
Old appearance: none; // + 20+ lines of custom box/border/background
Modern input[type="checkbox"], input[type="radio"] { accent-color: #7c3aed; }
see modern → 93%
Selector Beginner Grouping selectors without repetition
Old .card h1, .card h2, .card h3, .card h4 { margin-bottom: 0.5em; }
Modern .card :is(h1, h2, h3, h4) { margin-bottom: 0.5em; }
see modern → 96%
Selector Beginner Focus styles without annoying mouse users
Old :focus { outline: 2px solid blue; } // Shows on mouse click too, or people remove it (a11y fail)
Modern :focus-visible { outline: 2px solid var(--focus-color); }
see modern → 95%
Workflow Intermediate Controlling specificity without !important
Old .card .title { ... } .page .card .title { ... } .page .card .title.special { color: red !important; }
Modern @layer base, components, utilities; @layer utilities { .mt-4 { margin-top: 1rem; } }
see modern → 95%
Workflow Beginner Theme variables without a preprocessor
Old // Sass: $primary: #7c3aed; // Compiles to static #7c3aed .btn { background: $primary; }
Modern :root { --primary: #7c3aed; } .btn { background: var(--primary); }
see modern → 96%
Typography Intermediate Fluid typography without media queries
Old h1 { font-size: 1rem; } @media (min-width: 600px) { h1 { font-size: 1.5rem; } } @media (min-width: 900px) { h1 { font-size: 2rem; } }
Modern h1 { font-size: clamp(1rem, 2.5vw, 2rem); }
see modern → 95%
Layout Beginner Spacing elements without margin hacks
Old .grid > * { margin-right: 16px; } .grid > *:last-child { margin-right: 0; }
Modern .grid { display: flex; gap: 16px; }
see modern → 95%
Layout Beginner Aspect ratios without the padding hack
Old .wrapper { padding-top: 56.25%; position: relative; } .inner { position: absolute; inset: 0; }
Modern .video-wrapper { aspect-ratio: 16 / 9; }
see modern → 93%
Layout Beginner Sticky headers without JavaScript scroll listeners
Old // JS: scroll listener + getBoundingClientRect // then add/remove .fixed class .header.fixed { position: fixed; }
Modern .header { position: sticky; top: 0; }
see modern → 96%
Animation Advanced Scroll-linked animations without a library
Old // JS + IntersectionObserver observer.observe(el) el.style.opacity = …
Modern animation-timeline: view(); animation-range: entry; /* pure CSS, GPU-accelerated */
see modern → 78%
Workflow Beginner Nesting selectors without Sass or Less
Old // requires Sass compiler .nav { & a { color: #888; } }
Modern .nav { & a { color: #888; } } /* plain .css, no build */
see modern → 91%
Layout Intermediate Responsive components without media queries
Old @media (max-width: 768px) { .card { … } } /* viewport, not container */
Modern @container (width < 400px) { .card { flex-direction: column; } }
see modern → 93%
Colors Intermediate Mixing colors without a preprocessor
Old // Sass required $blend: mix( $blue, $pink, 60%);
Modern background: color-mix( in oklch, #3b82f6, #ec4899);
see modern → 89%
Selectors Intermediate Selecting parent elements without JavaScript
Old // JavaScript required el.closest('.parent') .classList.add(…)
Modern .card:has(img) { grid-template: auto 1fr; }
see modern → 94%
Layout Beginner Centering elements without the transform hack
Old position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
Modern .parent { display: grid; place-items: center; }
see modern → 96%
64 Snippets 26 CSS Features Tracked 3 Articles 0 Dependencies New CSS drops every month. Get one old → modern comparison in your inbox every week. Subscribe Menu modern.css ✕ Search…

Comments
Please log in or register to join the discussion