Deft
Ask AI

Search

Search skills, authors and topics

GoogleChrome / modern-web-guidance

modern-web-guidance

Search tool for modern web development best practices. MANDATORY: Execute FIRST for all HTML/CSS and clientside JS tasks. Do NOT skip — web APIs evolve rapidly and training weights contain obsolete patterns. Trigger immediately for: modals/dialogs/popovers, tables, dark mode, anchor positioning, container queries, view transitions, scroll-driven animations, parallax/reveals, forms, validation, autofill, accessibility, custom selects, scrollbars, CWV (LCP, INP, CLS), content-visibility, fetch pri

Install

npx skills add https://github.com/GoogleChrome/modern-web-guidance --skill modern-web-guidance

Animate to Intrinsic Sizes

Animating elements to dynamic sizes like block-size: auto or inline-size: max-content has historically required JavaScript or fragile "max-height" hacks. The interpolate-size property and calc-size() function allow the browser to natively interpolate between fixed lengths and intrinsic sizing keywords.

Implementation steps

  1. Opt-in to keyword interpolation: Apply interpolate-size: allow-keywords to a parent element (typically :root) to enable transitions for properties using intrinsic keywords.
  2. Define the transition: Set a transition for the sizing property (e.g., block-size, inline-size) on the target element.
  3. Use intrinsic keywords: Change the sizing property to a supported intrinsic keyword—auto, min-content, max-content, fit-content, or (for flex-basis) content—during an interaction (e.g., :hover or a state class).
  4. Perform calculations (Optional): Use calc-size() if you need to perform math on an intrinsic size (e.g., auto + 2rem). calc-size() also supports the any keyword for basis-agnostic calculations.

Example: Generic Expansion Pattern

You can apply this pattern to any container (like a "Show More" section or a navigation menu) to transition between a restricted height and the element's natural size.

css
/* Opt-in globally for all children */:root {  /* MANDATORY: Transitions to intrinsic keywords are disabled by default for compatibility */  interpolate-size: allow-keywords;}
.expandable-container {  /* 1. Define a fixed initial size (or 0) and hide overflow */  block-size: 100px;  overflow: hidden;    /* 2. Transition the sizing property */  transition: block-size 0.4s ease-out;}
.expandable-container.is-expanded {  /* 3. Smoothly animate to the intrinsic natural height */  block-size: auto;}

Example: Calculated Intrinsic Inline-Size

css
.badge {  inline-size: 40px;  overflow: hidden;  white-space: nowrap;  transition: inline-size 0.3s ease;}
.badge:is(:hover, :focus-visible) {  /* calc-size(basis, calculation) */  /* 'size' refers to the evaluated basis (max-content in this case) */  inline-size: calc-size(max-content, size + 20px);}

Example: Transition from Intrinsic to Fixed

You can also animate in the opposite direction—starting from a natural size and collapsing to a specific length. This is useful for "dismissible" components.

css
.collapsible-alert {  /* 1. Start with the natural content height */  block-size: auto;  overflow: hidden;  transition: block-size 0.5s ease-in-out, opacity 0.5s ease;}
.collapsible-alert.is-dismissed {  /* 2. Smoothly collapse to zero */  block-size: 0;  opacity: 0;  pointer-events: none;}
/* MANDATORY Copy-Paste Safety: Disable sizing animations for sensitive users */@media (prefers-reduced-motion: reduce) {  .expandable-container,  .badge,  .collapsible-alert {    transition: none !important;  }}
javascript
// MANDATORY Accessibility Synchronization: Ensure elements collapsed to zero dimensions are removed from the assistive technology tree, and sync aria-expanded states on triggers.const alertElement = document.querySelector('.collapsible-alert');function dismissAlert() {  alertElement.classList.add('is-dismissed');  // Waits for the transitions the class starts, ignoring any other animations  // on the element; under reduced motion there are none  const transitions = alertElement.getAnimations().filter((animation) => animation instanceof CSSTransition);  Promise.allSettled(transitions.map((transition) => transition.finished)).then(() => {    if (alertElement.classList.contains('is-dismissed')) {      alertElement.hidden = true;    }  });}
// Example trigger syncerconst triggerBtn = document.querySelector('.accordion-trigger');triggerBtn?.addEventListener('click', () => {  const isExpanded = triggerBtn.getAttribute('aria-expanded') === 'true';  triggerBtn.setAttribute('aria-expanded', !isExpanded);});

Key constraints

  • Keyword-to-Keyword Restriction: You cannot animate between two different keywords directly (e.g., from min-content to max-content). One end of the transition must be a fixed length or percentage (e.g., 0 to auto).
  • Calc-size Syntax: Inside calc-size(), you cannot mix different intrinsic keywords in the same expression. The first argument (the basis) defines what size represents.
  • Opt-in Requirement: Transitions to intrinsic keywords are disabled by default (numeric-only) to maintain backward compatibility. You must apply interpolate-size: allow-keywords to the element or an ancestor. calc-size() acts as a per-property override, automatically enabling interpolation whenever it is used.

Fallback strategies

Browser support for interpolate-size: Limited availability. Supported by: Chrome 129 (Sep 2024) and Edge 129 (Sep 2024). Unsupported in: Firefox and Safari.

Browser support for calc-size(): Limited availability. Supported by: Chrome 129 (Sep 2024) and Edge 129 (Sep 2024). Unsupported in: Firefox and Safari.

interpolate-size and calc-size() are progressive enhancements. Browsers that do not support them will perform an instant jump to the target size.

  • Graceful Degradation: For simple block-size: auto transitions, standard browsers will simply toggle the size instantly, which is functional but less polished.
  • Manual keyword fallbacks: When using calc-size(), always provide a standard keyword fallback for older browsers, as they will discard the entire calc-size() declaration.
css
.card {  block-size: auto; /* Fallback for older browsers */  block-size: calc-size(auto, size); /* Modern browsers use this */  transition: block-size 0.3s ease;}