32 min read

25 CSS Tricks Every Developer Should Know in 2026

Modern CSS is unrecognizable from the float-and-clearfix era. These 25 techniques will modernize your stylesheets and eliminate JavaScript hacks you no longer need.

All 25 Tricks

Introduction

CSS in 2026 is a fundamentally different language from what most developers learned. Features that required JavaScript, preprocessors, or hacky workarounds are now native. This article covers 25 techniques that range from "you should be using this today" to "this will change how you think about CSS." Every trick includes a code example, browser support status, and practical use cases.

#01

CSS Nesting

Native CSS nesting is here, and it means Sass's primary advantage has evaporated. You can nest selectors directly in CSS using the & parent reference. This makes styles more readable, co-locates related rules, and reduces repetition. The updated spec allows bare nesting without & for element selectors, making it feel natural.

CSS
.card {
  background: #111118;
  border-radius: 12px;
  padding: 2rem;

  /* Nest child elements directly */
  h2 {
    font-size: 1.5rem;
    margin-bottom: 0.5rem;
  }

  p {
    color: #94a3b8;
  }

  /* Use & for pseudo-classes and modifiers */
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(0,0,0,0.3);
  }

  /* BEM-style with & */
  &--featured {
    border: 2px solid #6366f1;
  }

  /* Media queries nest too */
  @media (width < 768px) {
    padding: 1rem;
  }
}
Chrome 120+ Firefox 117+ Safari 17.2+ Edge 120+
#02

The :has() Parent Selector

The :has() selector is the most powerful CSS addition in a decade. It lets you style a parent based on its children, or any element based on its siblings. Developers begged for a "parent selector" for 20 years, and :has() delivers that and far more. It eliminates entire categories of JavaScript that existed solely for conditional styling.

CSS
/* Style a card differently if it contains an image */
.card:has(img) {
  grid-template-rows: 200px auto;
}

/* Form group with an invalid input */
.form-group:has(:invalid) {
  border-color: #ef4444;
}

/* Style label when its sibling input is focused */
label:has(+ input:focus) {
  color: #6366f1;
  font-weight: 600;
}

/* Page-level: hide header when dialog is open */
body:has(dialog[open]) header {
  filter: blur(4px);
}

/* Select previous sibling (impossible before :has) */
li:has(+ li:hover) {
  opacity: 0.7;
}

/* Quantity queries: style lists with >3 items */
ul:has(li:nth-child(4)) {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
Chrome 105+ Firefox 121+ Safari 15.4+ Edge 105+
#03

Container Queries

Container queries let components respond to their container's size rather than the viewport. This is the feature that makes truly reusable, context-independent components possible. A card component can have a horizontal layout in a wide sidebar and a vertical layout in a narrow one, without the parent knowing or caring.

CSS
/* Define a containment context */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Default: vertical layout */
.card {
  display: grid;
  gap: 1rem;
}

/* When container is >400px: horizontal layout */
@container card (min-width: 400px) {
  .card {
    grid-template-columns: 150px 1fr;
    align-items: center;
  }
}

/* When container is >600px: show extra content */
@container card (min-width: 600px) {
  .card-meta {
    display: flex;
  }
}

/* Container query units */
.card-title {
  font-size: clamp(1rem, 3cqi, 2rem);
}
Chrome 105+ Firefox 110+ Safari 16+ Edge 105+
#04

View Transitions API

View Transitions let you animate between page states (or full page navigations in MPAs) with cinematic cross-fade effects. The browser captures a snapshot of the old state, renders the new state, then animates between them. This enables app-like transitions without a single framework dependency. You can target specific elements for hero-style animations.

CSS
/* Enable view transitions for MPA navigations */
@view-transition {
  navigation: auto;
}

/* Default cross-fade duration */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
}

/* Give a specific element a unique transition name */
.hero-image {
  view-transition-name: hero;
}

/* Customize the hero transition */
::view-transition-old(hero) {
  animation: 0.4s ease-out both fade-out;
}

::view-transition-new(hero) {
  animation: 0.4s ease-in both fade-in;
}

@keyframes fade-out {
  to { opacity: 0; transform: scale(0.95); }
}

@keyframes fade-in {
  from { opacity: 0; transform: scale(1.05); }
}
Chrome 111+ Firefox (behind flag) Safari 18+ Edge 111+
#05

Scroll-Driven Animations

Scroll-driven animations link CSS animations to scroll progress -- no JavaScript, no IntersectionObserver, no scroll event listeners. You can create progress bars that fill as you read, elements that animate as they enter the viewport, and parallax effects, all in pure CSS. The animation-timeline property connects an animation to a scroll or view timeline.

CSS
/* Reading progress bar */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  background: linear-gradient(90deg, #6366f1, #a855f7);
  transform-origin: left;
  animation: grow-progress auto linear;
  animation-timeline: scroll();
}

@keyframes grow-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/* Fade in elements as they scroll into view */
.reveal {
  animation: fade-up auto ease-out both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
Chrome 115+ Firefox (behind flag) Safari 18+ Edge 115+
#06

Subgrid

Subgrid solves the "aligning nested grid children" problem that has frustrated developers since CSS Grid launched. When a child uses subgrid, its tracks inherit from the parent grid, meaning all cards in a row can align their titles, descriptions, and buttons to the same grid lines regardless of content length.

CSS
.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* Define shared row tracks for card internals */
  grid-template-rows: subgrid;
  gap: 2rem;
}

.card {
  display: grid;
  /* Inherit parent's row tracks */
  grid-row: span 4;
  grid-template-rows: subgrid;
  gap: 0.5rem;
}

/* Now .card > img, h3, p, button all align
   across all cards in the same row */
Chrome 117+ Firefox 71+ Safari 16+ Edge 117+
#07

Fluid Typography with clamp()

The clamp() function creates fluid, responsive values without breakpoints. For typography, it means font sizes that smoothly scale between a minimum and maximum based on viewport width. No more five different font-size declarations in media queries.

CSS
/* Fluid heading: min 2rem, preferred 5vw, max 4rem */
h1 {
  font-size: clamp(2rem, 5vw, 4rem);
}

/* Fluid body text */
body {
  font-size: clamp(1rem, 1rem + 0.25vw, 1.125rem);
}

/* Fluid spacing */
.section {
  padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 3rem);
}

/* Fluid gap */
.grid {
  gap: clamp(1rem, 2vw, 3rem);
}
All modern browsers
#08

Gradient Text

Creating gradient-colored text is a three-line trick that produces eye-catching headings. Apply a gradient background, clip it to the text shape, and make the original text fill transparent.

CSS
.gradient-text {
  background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* Animated gradient text */
.animated-gradient-text {
  background: linear-gradient(90deg,
    #6366f1, #a855f7, #ec4899,
    #6366f1, #a855f7);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  animation: gradient-shift 3s linear infinite;
}

@keyframes gradient-shift {
  to { background-position: 200% center; }
}
Live Demo Gradient Text Effect
🛠
NexTool Gradient Generator
Use the NexTool Gradient Generator to visually create CSS gradients for text, backgrounds, and borders with copy-paste output.
All modern browsers
#09

Glassmorphism

The frosted-glass effect remains one of the most elegant UI patterns. It uses backdrop-filter: blur() combined with a semi-transparent background. Ideal for navigation bars, modals, and floating cards that need to feel layered without fully obscuring the content behind them.

CSS
.glass {
  background: rgba(17, 17, 24, 0.6);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 16px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

/* Subtle inner glow for depth */
.glass-glow {
  background: rgba(17, 17, 24, 0.5);
  backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    0 20px 50px rgba(0, 0, 0, 0.4);
}
Live Demo
Frosted Glass Panel
All modern browsers
#10

Logical Properties

Logical properties replace physical direction properties (margin-left, padding-right) with flow-relative ones (margin-inline-start, padding-inline-end). This means your layouts automatically work in RTL languages without any additional CSS. Even if you only build LTR sites, logical properties are more semantic and are the recommended approach going forward.

CSS
/* Instead of margin-left/right, use inline */
.card {
  margin-inline: auto;         /* = margin-left + margin-right */
  padding-block: 2rem;         /* = padding-top + padding-bottom */
  padding-inline: 1.5rem;      /* = padding-left + padding-right */
  max-inline-size: 800px;      /* = max-width */
  border-block-end: 1px solid; /* = border-bottom */
}

/* Logical border-radius */
.tag {
  border-start-start-radius: 8px; /* = border-top-left-radius (in LTR) */
  border-end-end-radius: 8px;     /* = border-bottom-right-radius (in LTR) */
}

/* Logical positioning */
.close-btn {
  position: absolute;
  inset-block-start: 1rem;     /* = top */
  inset-inline-end: 1rem;      /* = right (in LTR) */
}
All modern browsers
#11

Custom Scrollbars

The new scrollbar-width and scrollbar-color properties provide a standardized way to customize scrollbars. They work alongside the older WebKit pseudo-elements for fine-grained control. Thin, dark scrollbars that match your theme make the difference between a polished and a default-looking application.

CSS
/* Standard approach (Firefox, Chrome 121+) */
* {
  scrollbar-width: thin;
  scrollbar-color: #222230 transparent;
}

/* WebKit approach (wider support) */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: #222230;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: #6366f1;
}
Chrome 121+ Firefox 64+ Safari 18+ Edge 121+
#12

accent-color

One line of CSS to theme all native form controls. The accent-color property changes the color of checkboxes, radio buttons, range sliders, and progress bars to match your brand. This eliminates the need for custom-styled replacements in most cases.

CSS
/* Theme all form controls at once */
:root {
  accent-color: #6366f1;
}

/* Or per-element */
input[type="checkbox"] {
  accent-color: #a855f7;
}

input[type="range"] {
  accent-color: #ec4899;
}

progress {
  accent-color: #22c55e;
}
All modern browsers
#13

Scroll Snap

CSS Scroll Snap creates smooth, precise scrolling that "snaps" to defined points. Build carousels, full-page scroll experiences, and horizontally-scrolling galleries without any JavaScript library.

CSS
/* Horizontal carousel */
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 1rem;
  scroll-padding-inline: 1rem;
  /* Hide scrollbar but keep functionality */
  scrollbar-width: none;
}

.carousel::-webkit-scrollbar {
  display: none;
}

.carousel-item {
  scroll-snap-align: start;
  flex-shrink: 0;
  width: 80%;
}

/* Full-page vertical scroll snap */
.fullpage {
  scroll-snap-type: y mandatory;
  overflow-y: scroll;
  height: 100vh;
}

.fullpage-section {
  scroll-snap-align: start;
  height: 100vh;
}
All modern browsers
#14

aspect-ratio

The aspect-ratio property replaces the old padding-top hack for maintaining aspect ratios. One line of CSS ensures elements maintain their proportions regardless of container width.

CSS
.video-embed {
  aspect-ratio: 16 / 9;
  width: 100%;
}

.avatar {
  aspect-ratio: 1;  /* Perfect square */
  border-radius: 50%;
  object-fit: cover;
}

.card-image {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
}
All modern browsers
#15

Advanced Custom Properties Tricks

CSS custom properties (variables) are far more powerful than simple value storage. They cascade, respond to media queries, can be manipulated via JavaScript, and enable theming patterns that preprocessor variables cannot match.

CSS
/* Space-toggle trick: boolean-like custom properties */
.card {
  --is-dark: ;  /* empty = "off" */
  background: var(--is-dark, #fff) var(--is-dark #111);
}

/* Type-safe with @property */
@property --hue {
  syntax: "<number>";
  inherits: true;
  initial-value: 240;
}

/* Theme switching with custom properties */
:root {
  --bg: #050508;
  --text: #e2e8f0;
}

:root[data-theme="light"] {
  --bg: #ffffff;
  --text: #1a1a2e;
}

/* Respond to system preference */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --bg: #ffffff;
    --text: #1a1a2e;
  }
}
All modern browsers
#16

@layer Cascade Control

@layer lets you explicitly control the cascade order of your styles. No more specificity wars between your reset, base styles, component library, and overrides. Define the layer order once, and CSS respects it regardless of selector specificity within each layer.

CSS
/* Declare layer order (later = higher priority) */
@layer reset, base, components, utilities, overrides;

@layer reset {
  * { margin: 0; padding: 0; box-sizing: border-box; }
}

@layer base {
  body {
    font-family: 'Inter', sans-serif;
    color: var(--text);
  }
}

@layer components {
  .btn {
    padding: 12px 24px;
    border-radius: 8px;
  }
}

@layer utilities {
  .hidden { display: none !important; }
  .sr-only { position: absolute; clip: rect(0 0 0 0); }
}
All modern browsers
#17

color-mix()

color-mix() blends two colors in a specified color space. It is incredibly useful for generating hover states, opacity variants, and color scales from a single brand color, all in CSS with no preprocessor.

CSS
.btn {
  --brand: #6366f1;
  background: var(--brand);

  &:hover {
    /* 20% lighter */
    background: color-mix(in srgb, var(--brand), white 20%);
  }

  &:active {
    /* 20% darker */
    background: color-mix(in srgb, var(--brand), black 20%);
  }
}

/* Semi-transparent backgrounds from any color */
.overlay {
  background: color-mix(in srgb, #6366f1, transparent 85%);
}

/* Mix in oklch for perceptual uniformity */
.gradient-mid {
  color: color-mix(in oklch, #6366f1, #ec4899);
}
Chrome 111+ Firefox 113+ Safari 16.2+ Edge 111+
#18

@scope

@scope limits where styles apply with both an upper bound (the scope root) and an optional lower bound (the scope limit). This is native CSS component scoping without Shadow DOM or CSS Modules.

CSS
/* Style .card internals without affecting nested cards */
@scope (.card) to (.card) {
  h2 {
    font-size: 1.5rem;
    color: #e2e8f0;
  }

  p {
    color: #94a3b8;
  }
}

/* Scope to a specific component */
@scope (.sidebar) {
  a {
    color: #a855f7;
    font-weight: 500;
  }
}
Chrome 118+ Firefox (behind flag) Safari 17.4+ Edge 118+
#19

Popover API Styling

The Popover API provides native popover behavior (click to show, click outside to dismiss, Escape to close) with zero JavaScript. Combined with ::backdrop styling and @starting-style, you can create fully animated popovers and tooltips in pure HTML and CSS.

HTML + CSS
<!-- HTML -->
<button popovertarget="menu">Open Menu</button>
<div id="menu" popover>
  <p>Popover content here</p>
</div>

/* CSS */
[popover] {
  background: #111118;
  border: 1px solid #1e1e2e;
  border-radius: 12px;
  padding: 1.5rem;
  color: #e2e8f0;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);

  /* Animate entry */
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.2s, transform 0.2s,
              display 0.2s allow-discrete;
}

/* Starting state for entry animation */
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scale(0.95);
  }
}
Chrome 114+ Firefox 125+ Safari 17+ Edge 114+
#20

CSS Anchor Positioning

Anchor positioning is the CSS-native solution for tooltips, dropdowns, and popovers that position themselves relative to a trigger element. No more absolute positioning calculations in JavaScript. Define an anchor, and position another element relative to it, with automatic fallback positions when space is limited.

CSS
/* Define an anchor */
.trigger {
  anchor-name: --my-trigger;
}

/* Position relative to anchor */
.tooltip {
  position: fixed;
  position-anchor: --my-trigger;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 8px;

  /* Fallback if no space below */
  position-try-fallbacks: flip-block;
}
Chrome 125+ Firefox (not yet) Safari (partial) Edge 125+
#21

text-wrap: balance and pretty

text-wrap: balance distributes text evenly across lines in headings, preventing the "one orphaned word on the last line" problem. text-wrap: pretty does the same for body text with a focus on preventing typographic widows. These two properties eliminate a class of tedious manual typographic fixes.

CSS
/* Balance headings (max ~6 lines) */
h1, h2, h3 {
  text-wrap: balance;
}

/* Prevent widows in paragraphs */
p {
  text-wrap: pretty;
}
Chrome 114+ Firefox 121+ Safari 17.5+ Edge 114+
#22

@starting-style for Entry Animations

@starting-style defines the initial state of an element when it first appears in the DOM or when it transitions from display: none. This makes it possible to animate elements entering the page using only CSS transitions, eliminating the need for JavaScript-based animation libraries for entry effects.

CSS
dialog {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.3s, transform 0.3s,
              display 0.3s allow-discrete,
              overlay 0.3s allow-discrete;
}

/* When dialog first opens, start from these values */
@starting-style {
  dialog[open] {
    opacity: 0;
    transform: translateY(-20px);
  }
}

/* Closing state */
dialog:not([open]) {
  opacity: 0;
  transform: translateY(20px);
}
Chrome 117+ Firefox 129+ Safari 17.5+ Edge 117+
#23

Smooth Scrolling (Done Right)

Smooth scrolling is simple, but doing it correctly means respecting users who have motion sensitivity. Always pair scroll-behavior: smooth with a prefers-reduced-motion check.

CSS
/* Only smooth-scroll for users who want it */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Also respect it for animations */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
All modern browsers
#24

Dynamic Viewport Units (dvh, svh, lvh)

The classic 100vh bug on mobile (where it does not account for the browser chrome) is solved by dynamic viewport units. dvh changes as the browser chrome shows/hides, svh is the smallest possible viewport (when all browser UI is visible), and lvh is the largest (when browser UI is hidden).

CSS
/* Full-height hero that actually works on mobile */
.hero {
  min-height: 100dvh;  /* dynamic: adapts to browser chrome */
}

/* Stable full-height (smallest viewport) */
.sticky-footer-layout {
  min-height: 100svh;
}

/* Fallback for older browsers */
.hero {
  min-height: 100vh;    /* fallback */
  min-height: 100dvh;   /* modern browsers use this */
}
All modern browsers
#25

@property for Typed Custom Properties

@property registers custom properties with a type, initial value, and inheritance behavior. This enables animations of custom properties (impossible with untyped variables), type checking that catches errors, and default values that work even before JavaScript loads. The killer use case: animating gradient stops and complex values that CSS normally cannot interpolate.

CSS
/* Register a typed custom property */
@property --gradient-angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

/* Now you can ANIMATE gradient angles! */
.rotating-gradient {
  background: linear-gradient(
    var(--gradient-angle),
    #6366f1, #a855f7, #ec4899
  );
  animation: rotate-gradient 3s linear infinite;
}

@keyframes rotate-gradient {
  to { --gradient-angle: 360deg; }
}

/* Animate color stops */
@property --stop-1 {
  syntax: "<color>";
  inherits: false;
  initial-value: #6366f1;
}

.color-shift {
  background: linear-gradient(var(--stop-1), #ec4899);
  transition: --stop-1 0.5s;
}

.color-shift:hover {
  --stop-1: #22c55e;
}
Live Demo
Chrome 85+ Firefox 128+ Safari 15.4+ Edge 85+

Putting It All Together

Modern CSS is powerful enough to build interactive, animated, responsive interfaces that previously required JavaScript libraries, preprocessors, and build tools. The 25 techniques in this article represent the current state of the art: features that are either universally supported or close to it across all modern browsers.

The key insight is that CSS has become a real programming language with types (@property), scope (@scope, @layer), conditionals (:has(), container queries), and animation primitives that rival dedicated animation libraries. The developers who invest in understanding these native capabilities will write less code, ship fewer bugs, and build faster interfaces.

🛠
NexTool CSS Tools
Experiment with many of these CSS techniques using NexTool's free CSS tools -- gradient generator, flexbox playground, shadow builder, and more. All browser-based, no signup needed.

Level Up Your CSS Workflow

Generate gradients, test flexbox layouts, and build shadows visually with NexTool's free developer tools. Or grab the Ultimate Bundle for premium CSS resources.

Try Free CSS Tools Get Ultimate Bundle