Back Original

Modern CSS Code Snippets: Stop writing CSS like it's 2015

Range style queries without multiple blocks

Old /* Multiple style() blocks */

see modern →

Sticky & snapped element styling without JavaScript

Old window.addEventListener(

Modern @container scroll-state(

see modern →

Typed attribute values without JavaScript

Old // JS reading dataset

see modern →

Inline conditional styles without JavaScript

Old // JavaScript toggling

see modern →

Reusable CSS logic without Sass mixins

Old // Sass function

Modern @function --fluid(

see modern →

Corner shapes beyond rounded borders

Old .card {

see modern →

Responsive clip paths without SVG

Old .shape {

see modern →

Scroll spy without IntersectionObserver

Old const observer = new

Modern nav a:target-current {

see modern →

Filling available space without calc workarounds

Old .full {

see modern →

Staggered animations without nth-child hacks

Old li:nth-child(1) { --i: 0; }

see modern →

Carousel navigation without a JavaScript library

Old // Swiper.js or Slick carousel

Modern .carousel::scroll-button(right) {

see modern →

Vertical text centering without padding hacks

Old .btn {

see modern →

Hover tooltips without JavaScript events

Old // JS: mouseenter + mouseleave

Modern <button interestfor="tip">Hover me</button>

see modern →

Modal controls without onclick handlers

Old <button onclick="

Modern <button commandfor="dlg"

see modern →

Dialog light dismiss without click-outside listeners

Old // JS: listen for click on ::backdrop

Modern <dialog closedby="any">

see modern →

Customizable selects without a JavaScript library

Old // Select2 or Choices.js

see modern →

Old .hero {

see modern →

Color variants without Sass functions

Old /* Sass: lighten($brand, 20%), darken($brand, 10%) */

see modern →

Multiline text truncation without JavaScript

Old /* JS: slice text by chars/words, add "..." */

see modern →

Drop caps without float hacks

Old .drop-cap::first-letter {

Modern .drop-cap::first-letter {

see modern →

Positioning shorthand without four properties

Old .overlay {

see modern →

Lazy rendering without IntersectionObserver

Old // JS IntersectionObserver

see modern →

Dropdown menus without JavaScript toggles

Old .menu { display: none; }

Modern button[popovertarget=menu] { }

see modern →

Tooltip positioning without JavaScript

Old /* Popper.js / Floating UI: compute rect,

Modern .trigger { anchor-name: --tip; }

see modern →

Scoped styles without BEM naming

Old // BEM: .card__title, .card__body

see modern →

Typed custom properties without JavaScript

Old // --hue was a string, no animation

see modern →

Independent transforms without the shorthand

Old .icon { transform: translateX(10px) rotate(45deg) scale(1.2); }

see modern →

Animating display none without workarounds

Old // wait for transitionend then display:none

Modern .panel { transition: opacity .2s, overlay .2s;

see modern →

Entry animations without JavaScript timing

Old // add class after paint

Modern .card { transition: opacity .3s, transform .3s; }

see modern →

Page transitions without a framework

Old // Barba.js or React Transition Group

Modern document.startViewTransition(() => updateDOM());

see modern →

Scroll snapping without a carousel library

Old // Slick, Swiper, or scroll/touch JS

Modern .carousel { scroll-snap-type: x mandatory; }

see modern →

Balanced headlines without manual line breaks

Old // manual <br> or Balance-Text.js

see modern →

Font loading without invisible text

Old @font-face { ... }

see modern →

Multiple font weights without multiple files

Old @font-face { font-weight: 400; }

see modern →

Dark mode defaults without extra CSS

Old @media (prefers-color-scheme: dark) {

see modern →

Dark mode colors without duplicating values

Old @media (prefers-color-scheme: dark) {

Modern color: light-dark(#111, #eee);

see modern →

Low-specificity resets without complicated selectors

Old .reset ul, .reset ol { ... }

see modern →

Direction-aware layouts without left and right

Old margin-left: 1rem;

Modern margin-inline-start: 1rem;

see modern →

Naming grid areas without line numbers

Old float: left; /* clearfix, margins */

see modern →

Aligning nested grids without duplicating tracks

Old .child-grid {

see modern →

Modal dialogs without a JavaScript library

Old .overlay { position: fixed; z-index: 999; }

see modern →

Styling form controls without rebuilding them

Old appearance: none;

Modern input[type="checkbox"],

see modern →

Grouping selectors without repetition

Old .card h1, .card h2, .card h3, .card h4 {

Modern .card :is(h1, h2, h3, h4) {

see modern →

Focus styles without annoying mouse users

Old :focus { outline: 2px solid blue; }

see modern →

Controlling specificity without !important

Old .card .title { ... }

Modern @layer base, components, utilities;

see modern →

Theme variables without a preprocessor

Old // Sass: $primary: #7c3aed;

see modern →

Fluid typography without media queries

Old h1 { font-size: 1rem; }

see modern →

Spacing elements without margin hacks

Old .grid > * { margin-right: 16px; }

see modern →

Aspect ratios without the padding hack

Old .wrapper { padding-top: 56.25%; position: relative; }

see modern →

Sticky headers without JavaScript scroll listeners

Old // JS: scroll listener + getBoundingClientRect

see modern →

Scroll-linked animations without a library

Old // JS + IntersectionObserver

Modern animation-timeline: view();

see modern →

Nesting selectors without Sass or Less

Old // requires Sass compiler

see modern →

Responsive components without media queries

Old @media (max-width: 768px) {

Modern @container (width < 400px) {

see modern →

Mixing colors without a preprocessor

Old // Sass required

Modern background: color-mix(

see modern →

Selecting parent elements without JavaScript

Old // JavaScript required

see modern →

Centering elements without the transform hack

Old position: absolute;

see modern →