
CSS and Styling — 2026 Week 8
This week in CSS was defined by two converging themes: powerful new layout and geometry primitives arriving in browsers, and a practical reckoning with how… Compiled for immediate developer deployment.


Navigation API - a better way to navigate, is now Baseline Newly Available | Blog | web.dev

Building Fluid Typographic Scales with clamp() and :heading()
:heading() and pow() by adding fluid scaling via clamp(). The approach defines a linear ramp between minimum and maximum viewport widths (e.g., 375px to 1440px) with rem-based min and max sizes, letting the maths interpolate automatically. Because each heading level maps a --heading-level custom property (h1 = 5, h6 = 0), the clamp() logic lives in one :heading block rather than duplicated across six selectors. The article explains the exponent assignment: h1 uses exponent 5 so that 1rem multiplied by the scale ratio raised to that power produces the largest size, while h6 at exponent 0 evaluates to the base size. An important WCAG 1.4.4 note: clamp() min and max must use rem (never px) so the scale respects browser zoom. The :heading() pseudo-class and pow() are currently experimental, available only in Safari Technology Preview.
Typographic Scales in CSS with :heading(), sibling-index(), and pow()
:heading(), sibling-index(), and pow() (from CSS Values and Units Level 4) collapses a verbose typographic scale into three custom properties and one font-size rule. The core formula is font-size = base-size x ratio^(6 - sibling-index()): for h1 (index 1) that becomes ratio^5, the largest value; for h6 (index 6) it becomes ratio^0 = 1, the base size. With a Perfect Fourth ratio (1.333), h1 lands at 4.209rem and h6 at 1rem. Swapping ratios — Minor Third (1.2), Major Third (1.25), Perfect Fifth (1.5) — requires changing a single --typographic-scale variable. The article flags a critical limitation: sibling-index() counts DOM siblings, not semantic heading levels, so repeated or skipped heading tags break the scale. A fallback approach using explicit --heading-level assignments on each :heading(N) selector is provided for real-world layouts. Currently only Safari Technology Preview supports all three features.
Why is Anchor Positioning not working?
anchor-name is resolvable: the anchor element's box must be fully laid out before the positioned element's box. This single rule explains a cascade of frustrating edge cases. When .element is a child of .anchor, adding any non-static position to .anchor makes it a containing block, breaking the rule — except when .element uses position: fixed, which shifts its containing block to the viewport. Sibling order matters too: if .anchor appears after .element in the DOM and carries position: absolute or fixed, the lookup fails; position: relative or sticky on the anchor still works because those are laid out before absolutely-positioned elements. z-index cannot fix ordering failures. The most complete failure mode is when .anchor sits entirely outside .element's containing block — the reference is always invalid regardless of DOM order.
CSS Anchor Positioning: Scoping, Implicit Anchors, and Conditional Hiding
anchor-scope, implicit anchors, and position-visibility. The anchor-scope property solves the naming-collision problem that arises when repeated components share the same anchor-name: without it, all tooltips in a card list stack on the last DOM element because anchor names are globally visible. Applying anchor-scope: --card to each parent li creates a subtree boundary fixing this. Implicit anchors, created automatically by the Popover API via popovertarget or the showPopover({ source }) JavaScript call, bypass CSS naming entirely and are inherently collision-free. Finally, position-visibility with values anchors-visible and no-overflow controls graceful hiding: when triggered, the element's visibility computes to force-hidden, a hard override that descendant visibility: visible declarations cannot escape. Chrome's current implementation defaults to always rather than the spec-defined anchors-visible, a divergence still under CSSWG discussion.
5 CSS fouls that I see way too often
width: 100% on block elements — adding an explicit 100% width causes overflow when margins are added, because box-sizing: border-box does not account for margins. Second: using a fixed height instead of min-height, which breaks as content grows. Third: copy-pasted component styles that diverge over time — Powell demos his own live site, where a duplicated fancy-button and link-button pair and a misclosed @layer block show the real cost of skipping cleanup. Fourth: suppressing focus rings with outline: none globally instead of using :focus-visible, which lets the browser apply rings only during keyboard navigation (not mouse clicks) — demonstrated with a keyboard walkthrough of ESPN where focused elements are completely invisible. Fifth: overusing one layout tool — reaching for flexbox where CSS Grid is the cleaner fit, a pattern Powell says is amplified by AI models trained on older flexbox-heavy code.
una.im | border-shape: the future of the non-rectangular web
5 CSS fouls that I see way too often
width: 100% on block elements — adding an explicit 100% width causes overflow when margins are added, because box-sizing: border-box does not account for margins. Second: using a fixed height instead of min-height, which breaks as content grows. Third: copy-pasted component styles that diverge over time — Powell demos his own live site, where a duplicated fancy-button and link-button pair and a misclosed @layer block show the real cost of skipping cleanup. Fourth: suppressing focus rings with outline: none globally instead of using :focus-visible, which lets the browser apply rings only during keyboard navigation (not mouse clicks) — demonstrated with a keyboard walkthrough of ESPN where focused elements are completely invisible. Fifth: overusing one layout tool — reaching for flexbox where CSS Grid is the cleaner fit, a pattern Powell says is amplified by AI models trained on older flexbox-heavy code.Navigation API - a better way to navigate, is now Baseline Newly Available | Blog | web.dev
Building Fluid Typographic Scales with clamp() and :heading()
:heading() and pow() by adding fluid scaling via clamp(). The approach defines a linear ramp between minimum and maximum viewport widths (e.g., 375px to 1440px) with rem-based min and max sizes, letting the maths interpolate automatically. Because each heading level maps a --heading-level custom property (h1 = 5, h6 = 0), the clamp() logic lives in one :heading block rather than duplicated across six selectors. The article explains the exponent assignment: h1 uses exponent 5 so that 1rem multiplied by the scale ratio raised to that power produces the largest size, while h6 at exponent 0 evaluates to the base size. An important WCAG 1.4.4 note: clamp() min and max must use rem (never px) so the scale respects browser zoom. The :heading() pseudo-class and pow() are currently experimental, available only in Safari Technology Preview.Typographic Scales in CSS with :heading(), sibling-index(), and pow()
:heading(), sibling-index(), and pow() (from CSS Values and Units Level 4) collapses a verbose typographic scale into three custom properties and one font-size rule. The core formula is font-size = base-size x ratio^(6 - sibling-index()): for h1 (index 1) that becomes ratio^5, the largest value; for h6 (index 6) it becomes ratio^0 = 1, the base size. With a Perfect Fourth ratio (1.333), h1 lands at 4.209rem and h6 at 1rem. Swapping ratios — Minor Third (1.2), Major Third (1.25), Perfect Fifth (1.5) — requires changing a single --typographic-scale variable. The article flags a critical limitation: sibling-index() counts DOM siblings, not semantic heading levels, so repeated or skipped heading tags break the scale. A fallback approach using explicit --heading-level assignments on each :heading(N) selector is provided for real-world layouts. Currently only Safari Technology Preview supports all three features.Why is Anchor Positioning not working?
anchor-name is resolvable: the anchor element's box must be fully laid out before the positioned element's box. This single rule explains a cascade of frustrating edge cases. When .element is a child of .anchor, adding any non-static position to .anchor makes it a containing block, breaking the rule — except when .element uses position: fixed, which shifts its containing block to the viewport. Sibling order matters too: if .anchor appears after .element in the DOM and carries position: absolute or fixed, the lookup fails; position: relative or sticky on the anchor still works because those are laid out before absolutely-positioned elements. z-index cannot fix ordering failures. The most complete failure mode is when .anchor sits entirely outside .element's containing block — the reference is always invalid regardless of DOM order.CSS Anchor Positioning: Scoping, Implicit Anchors, and Conditional Hiding
anchor-scope, implicit anchors, and position-visibility. The anchor-scope property solves the naming-collision problem that arises when repeated components share the same anchor-name: without it, all tooltips in a card list stack on the last DOM element because anchor names are globally visible. Applying anchor-scope: --card to each parent li creates a subtree boundary fixing this. Implicit anchors, created automatically by the Popover API via popovertarget or the showPopover({ source }) JavaScript call, bypass CSS naming entirely and are inherently collision-free. Finally, position-visibility with values anchors-visible and no-overflow controls graceful hiding: when triggered, the element's visibility computes to force-hidden, a hard override that descendant visibility: visible declarations cannot escape. Chrome's current implementation defaults to always rather than the spec-defined anchors-visible, a divergence still under CSSWG discussion.This week in CSS was defined by two converging themes: powerful new layout and geometry primitives arriving in browsers, and a practical reckoning with how developers misuse the tools already at hand. The featured article on border-shape — testable today in Chrome Canary 146+ — showed how redefining an element's actual border geometry (rather than masking with clip-path) unlocks tooltips, chevron navigation, and scalloped borders that all automatically follow the new shape for background, outline, and shadow alike. Alongside it, the Navigation API reaching Baseline Newly Available status marks a meaningful moment: a single navigate event listener now replaces the fragmented History API pattern across all browsers.
Anchor Positioning matured further this week with complementary guides on anchor-scope (solving naming collisions in repeated components), position-visibility (conditional force-hidden hiding), and Temani Afif's precise diagnosis of why anchor lookups fail — rooted in layout-order rules that z-index cannot override. On typography, two companion posts demonstrated how :heading(), sibling-index(), and clamp() collapse verbose typographic scales into a handful of custom properties, though both features remain Safari-only for now.
Kevin Powell's critique of five common CSS mistakes — redundant width:100%, fixed heights, suppressed focus rings, and over-reliance on flexbox — grounded the week in evergreen fundamentals. His point that AI models trained on flexbox-heavy older code perpetuate those patterns was a quiet but important observation for teams adopting AI-assisted development.
- border-shape (Chrome Canary 146+) redefines element geometry so background, shadow, and outline all follow the custom shape automatically
- Navigation API is now Baseline across all major browsers — one navigate listener replaces the fragmented History API pattern for SPAs
- anchor-scope and position-visibility plug real collision and hiding gaps in CSS Anchor Positioning; anchor lookup fails due to layout order, not z-index