Advanced CSS Features You Should Not Miss

Tushar ShuklaTS
Tushar ShuklaA senior frontend developer, curious tech tinkerer and an anime fan.
0 views
7 mins read
In my previous blog, we explored beginner-friendly CSS features that are production-ready and easy to adopt. This post is kinda Part 2 of the series, where we'll go deeper into advanced CSS concepts. Some of these are already stable and usable today, while others are experimental and should be used with caution.
Let's dive in!
Just like Part 1, we'll rate each feature on a scale of Adoption Readiness Value (1-10) (Production Readiness in Part 1):
1-2 → Experimental or behind flags; use only for demos and learning.
3-5 → Limited support or niche usage; experiment but don't rely fully in production.
6-8 → Usable with some caution; check browser support.
9-10 → Solid and widely supported, use it freely.

1. Scoped Styles with @scope

What it is: A way to scope CSS rules to a specific part of the DOM, avoiding the global cascade mess.
Why it matters: Helpful in dealing with specificity related challenges.
Use it for: Component libraries, design systems, or any complex UI where you want to avoid style conflicts.
/* A product card where styles are scoped to .product-card only */
@scope (.product-card) {
  /* ★ scoped selectors apply only inside .product-card */
  :scope {
    --pad: 12px;
  }
  h3 {
    margin: 0 0 4px;
  }
  .price {
    font-weight: 700;
  }
}
Here, h3 elements inside .product-card are only affected - no accidental overrides elsewhere.
  • Adoption Readiness: 7/10 (use with fallbacks)
  • Browser Readiness: Chrome/Safari solid; check Firefox status - see Can I use
  • Docs: MDN

2. Entry Animations with @starting-style

What it is: Lets you define a starting style for elements when they first appear, making entry animations smoother.
Why it matters: Provides a way to create more polished and visually appealing transitions without relying on JavaScript.
Use it for: Popovers, accordions, toasts - anything that enters smoothly.
.popover[popover] {
  opacity: 0;
  transform: translateY(4px) scale(0.98);
  transition:
    opacity 150ms,
    transform 150ms;
  /* ★ allow discrete transitions like display/visibility with related features */
  transition-behavior: allow-discrete;
}
 
@starting-style {
  /* ★ declared starting style for the first frame */
  .popover[popover]:popover-open {
    opacity: 0;
    transform: translateY(6px) scale(0.96);
  }
}
 
.popover[popover]:popover-open {
  opacity: 1;
  transform: none;
}
This avoids the common “flash of unanimated content.”
  • Adoption Readiness: 7/10
  • Browser Readiness: Great in Chromium-based; check others - see Can I use
  • Docs: MDN

3. Discrete Transitions with transition-behavior: allow-discrete

What it is: Lets certain discrete properties (e.g. display, visibility) participate in transitions when paired with patterns like @starting-style.
Why it matters: Enables smoother transitions for properties that usually can't animate, like toggling visibility or display.
Use it for: Animating in/out UI without JS toggling hacks.
.drawer {
  display: none;
  transition:
    display 0.001s,
    transform 200ms ease-out;
  transform: translateX(20px);
  /* ★ enable discrete property transitions */
  transition-behavior: allow-discrete;
}
 
.drawer[open] {
  display: block; /* ★ will coordinate with animation */
  transform: none;
}
  • Adoption Readiness: 6/10
  • Browser Readiness: Powerful; still maturing across engines - see Can I use
  • Docs: MDN

4. Custom Properties with @property

What it is: Register CSS custom properties with syntax, initial value, and inheritable behavior. Enables transitions of custom props.
Why it matters: Allows animating custom properties directly, making them first-class citizens in CSS animations.
Use it for: Color themes, design tokens, animatable numbers without JS or via CSS Houdini.
/* ★ register type + initial value so it can animate */
@property --ring {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
 
.button {
  box-shadow: 0 0 0 var(--ring) rgba(0, 0, 0, 0.2);
  transition: --ring 200ms; /* ★ animate the custom prop itself */
}
 
.button:focus-visible {
  --ring: 6px;
}
  • Adoption Readiness: 8/10
  • Browser Readiness: Widely supported; mind Safari versions - see Can I use
  • Docs: MDN, CSS Houdini

5. Relative colors with oklch()

What it is: Next-gen color function with perceptual accuracy. You can even derive colors from others.
Why it matters: Avoids color contrast issues, provides a consistent way to derive colors based on a base color.
Use it for: Consistent contrast, light/dark adjustments, deriving tints/shades, define themes.
:root {
  --brand: oklch(62% 0.16 260);
} /* base brand */
.button {
  background: var(--brand);
  /* ★ derive a subtle hover from base brand */
  --hover: color-mix(in oklch, var(--brand), white 12%);
}
.button:hover {
  background: var(--hover);
}
  • Adoption Readiness: 8/10
  • Browser Readiness: Great today; ensure fallbacks if supporting older browsers - see Can I use
  • Docs: MDN, Color Mix

6. Scroll-driven animations — animation-timeline: view()

What it is: Bind animations to scroll progress instead of relying on JS scroll listeners. Drive animations from scroll or element visibility instead of time. Fine-grained control with animation-range.
Why it matters: Enables smooth, performant scroll-driven effects without heavy JS. Great for parallax, reveal animations, and more.
Use it for: Parallax headers, section reveals, scrubbed charts—without JS.
.progress-bar {
  animation: grow linear;
  animation-timeline: view();
  animation-range: entry 0% cover 100%;
}
 
@keyframes grow {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}
  • Adoption Readiness: 7/10
  • Browser Readiness: Chrome/Safari good; verify Firefox status - see Can I use
  • Docs: Chrome Blog

7. View Transitions API — page/route morphing

What it is: Smooth transitions between pages or UI states with almost no JS. Declarative cross-state transitions (SPAs and increasingly MPA) that snapshot old/new DOM and animate between.
Why it matters: Enhances user experience by providing fluid, context-aware transitions without the need for complex JavaScript logic.
Use it for: Seamless route changes, list-to-detail morphs, theme switches.
/* CSS: style the transition pseudo-elements */
::view-transition-old(root),
::view-transition-new(root) { animation-duration: 250ms; }
 
/* JS: opt-in and wrap DOM updates */
document.startViewTransition(async () => {
  // ★ update the DOM (navigate, toggle, etc.)
  await router.navigate('/product/42');
});
  • Adoption Readiness: 7/10
  • Browser Readiness: SPA flows widely usable; MPA expanding - see Can I use
  • Docs: Chrome Docs

8. Popover API - menus, tooltips, toasts

What it is: Native popover behavior (like dropdowns or tooltips) without complex JS. [popover] elements render in the top layer with built-in light-dismiss & focus management.
Why it matters: Simplifies popover management, avoids z-index issues, and provides a consistent API for popover-like components.
Use it for: Action menus, non-modal tips, in-product help.
<button popovertarget="menu1">Actions</button>
<div id="menu1" popover>
  <button>Rename</button>
  <button>Duplicate</button>
</div>
 
<style>
  /* ★ animate with @starting-style + :popover-open */
  #menu1 {
    opacity: 0;
    transform: scale(0.98);
    transition: 120ms;
  }
  @starting-style {
    #menu1:popover-open {
      opacity: 0;
      transform: scale(0.96);
    }
  }
  #menu1:popover-open {
    opacity: 1;
    transform: none;
  }
</style>
  • Adoption Readiness: 8/10
  • Browser Readiness: Solid across modern browsers - see Can I use
  • Docs: Chrome Docs

9. Responsive Images with image-set()

What it is: Serve multiple image resolutions depending on device pixel ratio.
Why it matters: Automatically picks the best image for the device, improving performance and visual quality.
Use it for: Variant-aware components (e.g., compact cards inside dense sidebars).
.hero {
  background-image: image-set("banner-1x.jpg" 1x, "banner-2x.jpg" 2x);
}
  • Adoption Readiness: 9/10
  • Browser Readiness: Widely supported - see Can I use
  • Docs: MDN

10. Smart Textareas with field-sizing

What it is: Automatically resizes a <textarea> as the user types. Native control over text field sizing behavior i.e. auto-expand without JS.
Why it matters: Improves usability by allowing users to see all their input without scrolling or resizing the field manually.
Use it for: Comments/chat inputs with minimal code.
textarea {
  /* ★ let the UA auto-grow based on content */
  field-sizing: content;
  max-height: 40svh; /* keep it reasonable */
}
  • Adoption Readiness: 4/10
  • Browser Readiness: Chrome 114+, Safari 17+, no Firefox - see Can I use
  • Docs: Chrome Blog

11. Position Anchoring with anchor()

What it is: Position elements relative to an anchor element without JS math.
Why it matters: Simplifies positioning logic, making it easier to create dynamic layouts.
Use it for: Tooltips, badges, teaching bubbles.
/* mark the anchor */
.button {
  anchor-name: --btn;
}
 
/* ★ position relative to the anchor’s edges */
.tooltip {
  position-anchor: --btn;
  inset-area: top span-right; /* or use anchor() with offsets */
  translate: 0 -6px;
}
  • Adoption Readiness: 5/10
  • Browser Readiness: Usable in Chromium; others catching up - see Can I use
  • Docs: MDN

12. Animating height: auto with interpolate-size

What it is: Allows animating from fixed to intrinsic sizes.
Why it matters: Simplifies transitions and animations like animating between height: 2rem and height: max-content without hacks.
Use it for: Expanding cards/FAQ answers with real content height.
.faq__answer {
  overflow: clip;
  height: 0;
  transition: height 200ms;
  /* ★ enable interpolating intrinsic sizes */
  interpolate-size: allow-keywords;
}
.faq[open] .faq__answer {
  height: max-content;
}
  • Adoption Readiness: 5/10
  • Browser Readiness: Shipping in some engines - see Can I use
  • Docs: MDN

13. Conditional Rules with @if

What it is: Allows defining styles based on conditions, similar to programming if statements.
Why it matters: Enables more dynamic and context-aware styling without complex workarounds.
Use it for: Theme switching, responsive design tweaks, or any scenario where styles depend on specific conditions.
@if (prefers-color-scheme: dark) {
  body {
    background: black;
    color: white;
  }
} @else  {
  body {
    background: white;
    color: black;
  }
}
  • Adoption Readiness: 2/10
  • Browser Readiness: Still draft
  • Docs: Spec

14. Styling Native <select>

What it is: Ongoing work to make <select> more themeable (top-layer, popover-based internals, new hooks).
Why it matters: Finally style selects without full custom widgets and JS.
Use it for: Prototyping native-feeling selects without full custom widgets.
<label>
  Sort
  <select>
    <option>Popular</option>
    <option>Newest</option>
    <option>Price</option>
  </select>
</label>
 
<style>
  /* ★ follow platform guidance; expect evolving hooks */
  select {
    appearance: base-select;
    background: white;
    border: 1px solid gray;
    padding: 0.5rem 0.75rem;
  }
</style>
  • Adoption Readiness: 3/10
  • Browser Readiness: Experiments; APIs evolving - see Can I use
  • Docs: Chrome Blog

15. CSS mixins & functions (proposal)

What it is: A proposal for native mixins/functions in CSS with Sass-like ergonomics without a build step.
Why it matters: Reduces reliance on preprocessors for reusable styles and logic.
Use it for: R&D and design system experiments.
/* syntax subject to change in proposals */
@function grid-gap($n) {
  gap: calc($n * 4px);
}
 
@mixin card {
  border-radius: 12px;
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.08);
}
 
.card {
  @mixin card; /* ★ apply proposed mixin */
}
.list {
  @function grid-gap(3);
}
  • Adoption Readiness: 2/10
  • Browser Readiness: Experiments; not standardized
  • Docs: Spec

Upcoming/Behind flags

These are not production ready but worth keeping an eye on:
  • CSS Mixins & Functions - Spec in progress
  • @if Rule - Conditional CSS
  • Some parts of Anchor Positioning - Still evolving

Closing thoughts

Advanced CSS is finally replacing a lot of JS glue. Use these features progressively: ship what helps now (image-set, Popover, View Transitions in SPAs), prototype the rest behind capability checks, and keep your components future-friendly.
You may also like
If you like my work, consider supporting me.
Support My Work
Found an issue?

If you found a typo, incorrect information or have a feature request, please raise an issue by clicking this button.

And that's a wrap!

Piqued your interest?

Let's connect!