CSS & HTML

CSS Tricks You Might Be Tempted to Use, and What to Use Instead

By Vail Joy Updated 8 min read

CSS Tricks You Might Be Tempted to Use, and What to Use Instead

Many CSS tricks that were experimental a few years ago are now built into the language, and others have quietly died. The question has changed from “does my browser support this?” to “does this still solve a problem, or am I carrying a habit?” This guide goes through the tricks designers are most often tempted to reach for, says which ones to use freely, which to use with care, and which to delete.

The short version: use clamp(), :is(), :where(), :has() and custom properties without hesitation. Drop vendor-prefix hacks, calc() fallbacks and IE workarounds. Keep a few real cautions: viewport-unit text, blend modes on large areas, and non-standard properties like font smoothing.

How to decide in a few seconds

Before adding any trick, check its Baseline status on MDN. “Widely available” means it has worked in all core browsers for at least 30 months, so use it. “Newly available” works everywhere current, so use it with a sensible fallback if your audience is conservative. “Limited availability” means treat it as progressive enhancement only.

Trick Status Verdict
:is() / :where() (old :matches(), :any()) Widely available since January 2021 Use freely
:has() Widely available since December 2023 Use freely
clamp() for fluid type Widely available since July 2020 Use, with a rem-based maximum
font-size-adjust Newly available (Baseline low, July 2024) Use for fallback fonts
mix-blend-mode Widely available since January 2020 Use, isolate it, keep areas small
CSS anchor positioning Newly available (Firefox 147 completed it) Use for tooltips and menus, with fallback
text-wrap: balance Newly available (Baseline low, May 2024) Use for headings
Native CSS nesting Widely available since June 2026 Use freely
@layer Widely available since September 2024 Use freely
View transitions (same-document) Newly available (Baseline low, October 2025) Enhancement only
Scroll-driven animations Limited availability Enhancement only, with a static default
text-size-adjust Limited availability Rarely needed
-webkit-font-smoothing Non-standard, macOS only Skip

Tricks that graduated: stop hesitating

Selectors: :is(), :where() and :has()

A long pile of nested selectors now collapses into one rule. :is() takes the specificity of its most specific argument. :where() always contributes zero, which makes it ideal for resets and defaults that components can override without a specificity fight.

/* Before: sixteen combinations */
:is(section, article, aside, nav) :is(section, article, aside, nav) h1 {
  font-size: 1.25rem;
}

/* Zero-specificity default that any class can override */
:where(.prose) a {
  text-decoration-thickness: 0.08em;
}

Both lists are forgiving: an unsupported selector inside is dropped instead of invalidating the whole rule. :has() is the bigger shift. It lets a parent react to its children, which used to need JavaScript.

.field:has(input:user-invalid) input {
  border-color: #c62828;
}

.card:has(img) {
  grid-template-columns: 12rem 1fr;
}

One caution from MDN: outside a forgiving list, a browser that does not understand :has() drops the entire rule. For a cosmetic enhancement that is fine. For layout the page depends on, make sure the default styles work without it. Our guide to the power of CSS selectors covers the rest of the toolbox.

Custom properties and calc()

CSS variables are everywhere, and the old advice still holds: use them. One classic mistake is still valid. A variable can only replace a value, never a property name, and var(--gap)px does not work. Store the unit in the variable, or multiply: calc(var(--gap) * 1px).

The long list of calc() bugs in IE11 and old Safari no longer matters. You do not need a width: 98% fallback before calc(100% - 20px). Use min(), max() and clamp() alongside it.

Grid versus flexbox

Grid is fully supported and not “experimental” in any sense. The rule of thumb still stands: use flexbox when content should decide how a single row or column flows, and grid when you want to define the layout and place items into it. They combine well. repeat(auto-fit, minmax(15rem, 1fr)) gives a responsive grid with no media queries. See Master CSS Flexbox in 5 Simple Steps for the flex side.

object-fit, clip-path and @supports

object-fit: cover and object-position are standard practice for images and video in fixed boxes. Pair them with aspect-ratio instead of padding hacks. clip-path with polygon(), circle() and inset() works in all current browsers; the old experimental flags and external-SVG caveats no longer apply.

@supports remains the right tool when you adopt something newer. Use it to wrap an enhancement rather than to rescue a feature that is already universal:

.tooltip { /* works everywhere: simple placement */
  position: absolute;
  top: 100%;
  left: 0;
}

@supports (position-area: bottom) {
  .trigger { anchor-name: --tip; }
  .tooltip {
    position: fixed;
    position-anchor: --tip;
    position-area: bottom;
    position-try-fallbacks: flip-block;
  }
}

That block uses CSS anchor positioning. Chrome and Edge 125, Safari 26 and Firefox 147 support it, according to web.dev’s January 2026 roundup, which makes the core feature Baseline newly available. Some newer sub-features such as position-visibility and anchor-size() arrived later, so test those separately.

Tricks to keep, with a caution

Viewport units in text and layout

Older guides recommended sizing text purely in vw; don’t. Text sized purely in vw ignores the user’s browser font-size setting and can fail zoom requirements. Use clamp() with a rem term in the middle value so the size still responds to user preferences:

h1 {
  font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
}

The underlying rule is WCAG 1.4.4 (Resize Text): users must be able to enlarge text to 200 percent without losing content or function. MDN’s clamp() page adds a practical guideline for fluid type: make the maximum at least twice the minimum. The example above has a 2.5rem maximum, only about 1.67 times its 1.5rem minimum, so if you target strict zoom compliance, raise the maximum to 3rem.

For full-height sections, the old 100vh trap on phones is solved by the small, large and dynamic viewport units. svh is the smallest viewport (browser UI expanded), lvh the largest, and dvh adjusts as the UI moves. MDN notes dvh can cause resizing while scrolling and a performance cost, so svh is the calmer default:

.hero {
  min-height: 100vh;  /* fallback */
  min-height: 100svh;
}

This replaces the JavaScript fix in CSS Fix for iOS vh-unit Bug. For components, container query units such as cqi size things relative to their container instead of the screen.

font-size-adjust

It works in all current browsers (Baseline newly available since July 2024; “widely available” follows after roughly 30 months) and is a genuinely useful way to stop layout shift when a web font is replaced by a fallback with a different x-height. It scales fonts so a chosen metric, by default the x-height, is a fixed proportion of the font size. The adjusted size is the target ratio divided by the font’s own ratio, multiplied by the font size.

body {
  font-family: "Brand Serif", Georgia, serif;
  font-size-adjust: ex-height 0.5;
}

Here the target is an x-height of half the font size, so every font in the stack is scaled to match it. You can also write ex-height from-font, which reads the ratio from the first available font (your web font) and applies it to any fallback, so you do not have to guess a number. Either way the adjustment applies to the whole stack, so test your pairing. The screenshot below shows Georgia and Verdana, whose x-heights differ a lot, before and after.

Demo page with cards showing Georgia and Verdana without and with font-size-adjust, an isolated blend mode, an email field with a red invalid border and a viewport-unit hero
The first two cards compare Georgia and Verdana without and with font-size-adjust: ex-height 0.5; the adjusted pair is visibly closer in size. The form card turns the border red through :has(). The demo uses :invalid so the state shows in a static capture; in production prefer :user-invalid, which waits for interaction.

Do not confuse this with text-size-adjust, which controls mobile text inflation. MDN lists it as limited availability and warns that disabling inflation on sites that are not responsive makes text hard to read. If your layout is responsive you rarely need it; do not set it to none out of habit.

font-stretch and variable fonts

The old description of font-stretch as picking a condensed face is correct but incomplete. It accepts percentages now, which matters for variable fonts with a width axis. The spec has renamed the property font-width and kept font-stretch as an alias, and MDN lists font-stretch as widely available, so keep using that name. For ligatures and small caps, font-variant-* properties still depend on the font containing those features.

Blend modes

mix-blend-mode and background-blend-mode are widely available. The practical cautions today are about containment and cost. First, blending creates a stacking context and also blends with whatever sits behind it, including the parent background, so add isolation: isolate to the parent when you want the effect limited to its children. Second, blending large animated areas costs GPU time, so test on a mid-range phone.

.poster {
  position: relative;
  isolation: isolate;
}
.poster b {
  mix-blend-mode: difference;
}

For a full walkthrough, see How to Create Spotify Colorizer Effects With CSS Blend Modes.

View transitions and scroll-driven animations

Same-document view transitions are Baseline newly available, so they work in current browsers but not in older ones; wrap document.startViewTransition() in a feature check and let the page update instantly without it. Scroll-driven animations (animation-timeline: scroll() or view()) have limited availability, so write the static state first and add the animation inside @supports (animation-timeline: scroll()). Honor prefers-reduced-motion for both.

More tricks that graduated

text-wrap, nesting and @layer

text-wrap: balance evens out line lengths in headings and avoids a single orphaned word. It is newly available, and browsers only balance short blocks of a few lines, so apply it to headings, not body copy. Unsupported browsers simply wrap as before, which makes it a safe enhancement.

h1, h2, h3 { text-wrap: balance; }

.card {
  padding: 1rem;
  & h2 { font-size: 1.25rem; }
  &:hover { box-shadow: 0 4px 16px #0002; }
}

@layer reset, base, components;

Native nesting means you no longer need a preprocessor just to nest rules, and @layer lets you order whole groups of styles so specificity battles between a reset, a framework and your components end by design. Both are widely available. Remember that unlayered styles beat layered ones, which is handy for overrides but surprising if you forget it.

Tricks to skip

Font smoothing

-webkit-font-smoothing and -moz-osx-font-smoothing are not part of any standard and only affect macOS. Browsers ignore them elsewhere, so they do no harm, but they hide a real problem rather than fix it. If text renders poorly, choose a better font or weight, and never rely on smoothing for readability.

Vendor prefixes and IE hacks

Remove :-webkit-matches, :-moz-any, -ms-text-size-adjust and any @media all and (-ms-high-contrast: none) blocks. A build step such as Autoprefixer with a current browserslist adds only what you need. If your stylesheet still contains old prefixes, a quick audit saves bytes and confusion.

Fixing the symptom instead of the cause

This idea survives from the original: when CSS is being used to patch a design or content problem, fix the root. Fluid type that needs five overrides signals a weak type scale. A blend mode that rescues an unreadable photo overlay signals the wrong image. For more habits worth building, read Level Up Your CSS Skills With These 20 Pro CSS Tips and CSS Vertical Centering, which grid and flexbox have made much simpler.

A quick checklist before using any new CSS

  1. Look up the property on MDN and read the Baseline badge, not only the demo.
  2. Decide what the page looks like when the feature is missing. If that is acceptable, ship it. If not, add a base style first.
  3. Wrap genuine enhancements in @supports, or use a forgiving selector list.
  4. Test with browser zoom, a large default font size, and reduced motion enabled.
  5. Check performance for anything that blends, filters or animates large areas.