What's New in CSS 2025

A timeline of CSS features that shipped in browsers in 2025. Customizable selects, invoker commands, CSS functions, scroll-state queries, and much more.

Customizable Select

Layout Chrome 134

Fully style HTML select dropdowns with appearance: base-select. The dropdown renders in the top layer, supports rich HTML in options, and the new selectedcontent element reflects the chosen option.

select {
  appearance: base-select;
}

select::picker(select) {
  background: #1a1a2e;
  border: 1px solid #333;
  border-radius: 12px;
  padding: 8px;
}

Invoker Commands

Layout Chrome 135

Buttons can now perform actions on other elements declaratively with commandfor and command attributes. Open modals, toggle popovers, and close dialogs without JavaScript.

<button commandfor="my-dialog" command="show-modal">
  Open Dialog
</button>

<dialog id="my-dialog">
  <p>Hello!</p>
  <button commandfor="my-dialog" command="close">
    Close
  </button>
</dialog>

Dialog Light Dismiss

Layout Chrome 134

The closedby attribute brings popover-style light dismiss to dialogs. Set closedby="any" to close on backdrop click or ESC, or closedby="closerequest" for ESC only.

<!-- Close on backdrop click or ESC -->
<dialog closedby="any">
  <p>Click outside to close</p>
</dialog>

<!-- Close on ESC only -->
<dialog closedby="closerequest">
  <p>Press ESC to close</p>
</dialog>

popover=hint & Interest Invokers

Layout Chrome 135

Hint popovers are ephemeral and don't close other popovers. The interestfor attribute triggers them on hover/focus declaratively. Perfect for tooltips and hovercards.

<button interestfor="my-tip">
  Hover me
</button>

<div id="my-tip" popover="hint">
  This is a tooltip!
</div>

Scroll-State Container Queries

Animation Chrome 133

Style descendants based on whether a scroll container is stuck, snapped, or scrollable. Use container-type: scroll-state with @container scroll-state() queries.

.sticky-header {
  position: sticky;
  top: 0;
  container-type: scroll-state;
}

@container scroll-state(stuck: top) {
  .sticky-header {
    box-shadow: 0 2px 8px rgba(0,0,0,.15);
    backdrop-filter: blur(12px);
  }
}

Tree Counting Functions

Animation Chrome 136

sibling-index() returns an element's position among siblings; sibling-count() returns the total. Ideal for staggered animations and dynamic layouts without manual indexing.

.card {
  animation-delay: calc(sibling-index() * 80ms);
  opacity: 0;
  animation: fade-in 0.4s forwards;
}

.item {
  flex-basis: calc(100% / sibling-count());
}

CSS Scroll Markers & Buttons

Layout Chrome 135

Build carousels with CSS-only pseudo-elements. ::scroll-button() creates navigation arrows, ::scroll-marker creates dot indicators. Both are accessible and stylable.

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

.carousel::scroll-button(left) {
  content: '←';
}
.carousel::scroll-button(right) {
  content: '→';
}

.carousel > * {
  scroll-snap-align: center;
}
.carousel > *::scroll-marker {
  content: '';
  border-radius: 50%;
  width: 10px; height: 10px;
  background: gray;
}

text-box (trim & edge)

Typography Chrome 133, Safari 18.2

Trim invisible font metric space for true optical centering. text-box: trim-both cap alphabetic removes ascent/descent padding so text is visually centered in buttons, badges, and headings.

.button {
  text-box: trim-both cap alphabetic;
  /* shorthand for:
     text-box-trim: trim-both;
     text-box-edge: cap alphabetic; */
}

h1 {
  text-box: trim-both cap alphabetic;
  /* text sits flush — no phantom spacing */
}

CSS if() Function

Workflow Chrome 137

Conditional values in CSS properties. Use if(media(...): value; else: fallback) for inline media queries, if(supports(...): ...) for feature queries, and if(style(...): ...) for style queries.

.container {
  padding: if(
    media(width >= 768px): 40px;
    else: 16px
  );

  display: if(
    supports(display: grid): grid;
    else: flex
  );
}

CSS Custom Functions (@function)

Workflow Chrome 137

Define reusable functions with @function --name(args) { @return ... }. Write composable, maintainable CSS logic like conditional border-radius, clamped values, and utility helpers.

@function --fluid-size(--min, --max) {
  @return clamp(
    var(--min),
    var(--min) + (var(--max) - var(--min)) * 
      (100vw - 320px) / (1200 - 320),
    var(--max)
  );
}

h1 { font-size: --fluid-size(1.5rem, 3rem); }

Advanced attr() Function

Workflow Chrome 133

attr() now works beyond content property and can parse values as typed data: colors, lengths, numbers, and custom identifiers. Use with any CSS property.

<div data-color="#7c3aed" data-size="200">

.box {
  background: attr(data-color type(<color>));
  width: attr(data-size type(<length>), 100px);
  /* typed parsing with fallback */
}

shape() Function

Animation Chrome 137

Create complex, responsive clip paths with the shape() function. Supports curves, lines, and CSS custom properties for dynamic clipping that scales with element size.

.blob {
  clip-path: shape(
    from 0% 50%,
    curve to 50% 0% with 0% 0%,
    curve to 100% 50% with 100% 0%,
    curve to 50% 100% with 100% 100%,
    curve to 0% 50% with 0% 100%
  );
}

stretch Sizing Keyword

Layout Baseline 2025

Make elements fill their containing block with width: stretch or height: stretch. Unlike 100%, it applies to the margin box so margins are respected without calc() hacks.

.full-width {
  width: stretch;
  margin-inline: 24px;
  /* fills container minus margins — 
     no calc(100% - 48px) needed */
}

.full-height {
  height: stretch;
}

corner-shape Property

Layout Chrome 142

Go beyond border-radius with corner-shape: round, bevel, notch, scoop, or squircle. Create flower shapes, hexagonal grids, and iOS-style squircles in pure CSS.

.squircle {
  border-radius: 24px;
  corner-shape: squircle;
}

.notched {
  border-radius: 16px;
  corner-shape: notch;
}

.scooped {
  border-radius: 40px;
  corner-shape: scoop;
}

Anchored Container Queries

Layout Chrome 138

Style elements based on their anchor positioning fallback. Use @container anchored(fallback: flip-block) to flip tooltip arrows automatically when position changes.

.tooltip {
  position: absolute;
  position-anchor: --trigger;
  position-area: top;
  position-try-fallbacks: flip-block;
}

@container anchored(fallback: flip-block) {
  .tooltip::after {
    /* flip arrow direction */
    transform: rotate(180deg);
  }
}

Nested View Transition Groups

Animation Chrome 136

Retain 3D and clipping effects during view transitions by nesting ::view-transition-group pseudo-elements. Use view-transition-group: nearest on child elements.

.card {
  view-transition-name: card;
}

.card-image {
  view-transition-name: card-img;
  view-transition-group: nearest;
  /* nests under ::view-transition-group(card) 
     instead of the root group */
}
ESC