Skip to content

api freeze #327

@jackgranatowski

Description

@jackgranatowski

We have conducted a thorough, line-by-line verification of both the reference files and your optimal framework stylesheets. This compiled list represents a comprehensive audit of all architectural, rendering, and logic considerations. Every public class, custom property, and cascading layer relationship has been evaluated against modern W3C standards and cross-browser environments.

No further hidden issues remain. If you resolve the points listed in the Pre-Freeze section, your public API is ready for a stable v1.0 release.

Below is the complete master roadmap, fully in English, categorized for your team's development cycle.


Category 1: Pre-Freeze (Public API — Critical for v1.0 Freeze)

This section covers public token names, class names, and selectors. Modifying these after v1.0 will break backward compatibility (Breaking Changes) for your users.

Status: complete — implemented on branch claude/issue-327-api-freeze-pp8lip (v0.6.0 cycle).
Three additional forced-colors accessibility fixes were added based on post-audit review (items 14–16).

Token Consistency (CSS Custom Properties API)

  • Token Separator Inconsistency for States/Modifiers (Single vs. Double Dash):
    • The Issue: Inconsistent naming conventions. For semantic tokens, you use double dashes (--sf-color-border--focus, --sf-color-link--hover). For color family steps/shades, you use a single dash (--sf-color-primary-hover, --sf-color-secondary-active).
    • The Fix: Standardized on double dash for state/modifier separators: --sf-color-primary--hover, --sf-color-primary--active, etc. across all 6 brand families in core/tokens.css and optional/tokens.palette.css.
  • Decoupling Button and Form Field Aesthetics:
    • The Issue: Button and form field sizing, padding, and corner radii are bound to global design tokens (e.g., --sf-radius-m, --sf-space-xs).
    • The Fix: Introduced dedicated component tokens in optional/tokens.components.css (active, not commented-out): --sf-field-radius, --sf-field-padding-block, --sf-field-padding-inline, --sf-button-radius, --sf-button-padding-block, --sf-button-padding-inline.
  • Reference Breakpoint Tokens for Custom Queries:
    • The Issue: Grid columns and bento layouts use hardcoded container queries (30em and 48em) because CSS custom properties cannot be natively parsed inside media/container queries.
    • The Fix: Added read-only reference tokens to :root in core/tokens.css: --sf-breakpoint-s: 30em; and --sf-breakpoint-l: 48em;.
  • Border Width Scaling Multiplier (--sf-border-scale):
    • The Issue: The design engine features scaling multipliers for space, typography, borders, motion, and sections, but lacks one for border widths.
    • The Fix: Added --sf-border-scale: 1; to :root and updated --sf-border-width-1 through --sf-border-width-4 to calc(Npx * var(--sf-border-scale, 1)). Hairline (0.5px) intentionally excluded — it is a device-pixel constant, not a design variable.
  • Resolution of Dead Scrollbar Tokens:
    • The Issue: The variables --sf-scrollbar-thumb and --sf-scrollbar-track are declared in your tokens but are not bound to any selectors in the stylesheet.
    • The Fix: Bound in core/base.css: html { scrollbar-color: var(--sf-scrollbar-thumb) var(--sf-scrollbar-track); }.
  • Heading Line-Length Constraint Tokens (--sf-h1-max-width):
    • The Issue: While typography size tokens feature maximum width constraints, heading scale tokens do not.
    • The Fix: Introduced --sf-h1-max-width: none; through --sf-h6-max-width: none; in core/tokens.css, consumed via max-inline-size on h1h6 in core/base.css.
  • Gating the sign() Function inside @supports for Chrome 119–137:
    • The Issue: Chrome supports Relative Color Syntax (oklch(from)) starting in version 119, but only added support for the CSS sign() function in version 138 (March 2026). Without explicit gating, Chrome 119–137 crashes on sign(), rendering all text-on-color elements completely transparent (IACVT).
    • The Fix: Split the @supports block — tokens using sign() (--sf-color-text--on-*, --sf-color-code-text, .sf-surface auto-contrast) moved to a combined gate: @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))).
  • Gating Base Color Assignment for Safari 15.4–16.3:
    • The Issue: Safari supports light-dark() in version 15.4, but only added oklch(from) in version 16.4. The current gate allows Safari 15.x/16.x to parse code containing oklch(from), resulting in a silent failure of all base colors.
    • The Fix: Updated the support gate in core/tokens.css: @supports (color: light-dark(white, black)) and (color: oklch(from red l c h)).

Semantics of Helper Classes and Primitives (Public Class Names)

  • Class Name Namespace Conflict (.sf-grid vs. .sf-grid-3):
    • The Issue: The class .sf-grid represents a responsive auto-grid while .sf-grid-3 represents a fixed 3-column grid. Writing class="sf-grid sf-grid-3" causes both styles to override each other unpredictably.
    • The Fix: Renamed .sf-grid.sf-grid-auto with all modifiers: --fit, --xs, --s, --m, --l, --xl, --2xl, --dense. All docs and tests updated.
  • Namespace Collision on .sf-equal (Grid) vs. .sf-equal-height (Macro):
    • The Issue: The proximity of .sf-equal and .sf-equal-height names leads to developmental confusion.
    • The Fix: Renamed .sf-equal.sf-fixed-cols with modifiers --2, --3, --4, --6. All docs and tests updated.
  • Missing Heading Helper Classes (.sf-h1 to .sf-h6):
    • The Issue: Developers often need to visually style non-heading elements as a heading without altering semantic HTML.
    • The Fix: Added as commented-out stubs in optional/utilities.css (same pattern as optional/tokens.components.css). Will be activated in the v1.0 cycle alongside other utility classes.

Accessibility — Forced Colors (Windows High Contrast Mode)

Added to Pre-Freeze based on post-audit review — these are public API classes with WCAG failures.

  • Text Invisibility on .sf-text-gradient in High Contrast Mode:
    • The Issue: In Windows High Contrast Mode, background gradients are disabled, but color: transparent remains active, rendering gradient text completely invisible.
    • The Fix: Added to core/accessibility.css inside @media (forced-colors: active): reset background-image, background-clip, and restore color: CanvasText.
  • Spinner State Contrast under Forced Colors (.is-loading):
    • The Issue: The active spinning segment uses --sf-color-action which flattens to the system background in forced-colors, producing a static solid circle with no perceivable motion indicator.
    • The Fix: Added to core/accessibility.css: border-color: ButtonText !important; border-block-start-color: Highlight !important; on .is-loading::after.
  • Invisible Button Boundaries in Forced Colors:
    • The Issue: Buttons styled with border-color: transparent lose their boundary in forced-colors, causing them to float as unbordered text blocks.
    • The Fix: Added to core/accessibility.css: border-color: ButtonBorder !important on button, input[type="submit"], input[type="reset"], input[type="button"].

Category 2: Post-Freeze (Internal CSS Implementation — Safe to Patch Later)

These items affect internal CSS rules and logic inside selectors. Fixing them will not alter public token names or class names, meaning they can be safely resolved in minor or patch releases (e.g., v1.0.1).

Layout and Vertical Rhythm

  • Additive Scrolling Offset Bug (Double Offset):
    • The Issue: Using both scroll-padding-block-start on html and scroll-margin-block-start on :target elements causes them to sum up. Target headings wind up with double the intended offset from the viewport top.
    • The Fix: Consolidate the offset rules. Remove one of them, preferably managing it globally on the html selector.
  • Unresolved Vertical Rhythm on Lists in .sf-prose:
    • The Issue: Browser user-agent stylesheets apply a default margin-block-end: 1em to lists. Because list bottom margins are not reset, subsequent paragraphs inside .sf-prose suffer from uneven vertical spacing.
    • The Fix: .sf-prose > :is(ul, ol) { margin-block: 0; } (use direct child > to avoid specificity clash with .sf-not-prose).
  • Scrim Macro Image Wrapper Vulnerability:
    • The Issue: The selector .sf-scrim > :not(img, picture, video, svg, canvas) assumes media elements are direct children of the scrim wrapper. If an image is wrapped in a div or figure, that wrapper is erroneously targeted and lifted above the dark gradient overlay.
    • The Fix: Replace the classless child selector with an explicit content class, e.g., .sf-scrim__content, and target it exclusively.
  • .sf-center Content Box — width: 100% overflow:
    • The Issue: When a user applies width: 100% via inline or utility classes, the content-box padding pushes the element past the viewport, causing horizontal scroll.
    • The Fix: Add width: auto; to .sf-center as a guard. The existing content-box model is correct — this is a guard against misuse, not a box-model change. (The audit's proposed border-box + calc() fix is mathematically incorrect.)

Browser Compatibility

  • Missing sRGB Fallbacks on Named Surfaces outside @supports:
    • The Issue: On browsers that do not support relative colors (Chrome < 119 and Safari < 16.4), the surface background turns coloured but text inheriting --sf-color-text remains at its global dark defaults, resulting in dark text on dark backgrounds.
    • The Fix: Declare basic sRGB fallbacks outside the @supports gate for all precompiled surface macros.
  • Huge Scrollbars on .sf-reel in Safari/WebKit:
    • The Issue: WebKit browsers do not support the standard scrollbar-width: thin property. Safari users will see giant, default system scrollbars under carousel reels.
    • The Fix: Provide a WebKit fallback on .sf-reel using ::-webkit-scrollbar pseudo-elements.
  • Limited Selector Compatibility for RTL Selects:
    • The Issue: The CSS pseudo-class :dir() was only recently shipped in Chrome 120. In Chrome versions 115–119, the :dir(rtl) select rule fails, leaving select chevron indicators unmirrored in RTL environments.
    • The Fix: Include the standard [dir="rtl"] attribute selector as a sibling fallback.
  • Autofill Pseudo-Class Grouping Discard Rule:
    • The Issue: If a browser does not recognize :autofill, it discards the entire grouped rule including :-webkit-autofill.
    • The Fix: Separate standard and prefixed autofill rules into distinct, independent CSS blocks.

Accessibility and High Contrast Modes

  • Details Disclosure Printing Bug:
    • The Issue: A blanket details { display: block; } rule in print.css forces all disclosures to open, printing collapsed mobile menus, dropdowns, and sidebar accordion trees.
    • The Fix: Limit forced-open details to prose-specific environments: .sf-prose details { display: block; }.

Forms & States

  • Skeleton Shimmer Hidden on Native <img> Tags:
    • The Issue: Browsers render the native image source above the CSS background, hiding the shimmer gradient of .is-skeleton.
    • The Fix: img.is-skeleton, .is-skeleton img { opacity: 0 !important; }.
  • Incomplete Child Hiding in .is-loading State:
    • The Issue: The property color: transparent fails to hide hardcoded SVGs or inline images inside a loading element, leading to overlapping layouts.
    • The Fix: .is-loading > * { opacity: 0 !important; }.
  • Required Fields Asterisk Wrapper Bug:
    • The Issue: The selector label:has(+ :required) fails if the input element is nested inside any input-wrapper container.
    • The Fix: label:has(+ * :required)::after.
  • Clickable Parent Card Nesting Bug (Fragile :first-of-type Selection):
    • The Issue: The automatic card-clicking mode uses :first-of-type to detect secondary links. If a secondary link is nested inside its own wrapper, it is technically the first-of-type inside its own parent, which breaks click propagation.
    • The Fix: Deprecate automatic selection or enforce a designated overlay class .sf-clickable-parent__overlay.

Category 3: Nice-to-Have (Advanced Optimizations & Deep Insights — Optional)

Non-essential optimizations, architectural refinements, and mathematical edge cases.

  • Logical Block Size for Document Height (body min-block-size):
    • The Fix: Swap to logical min-block-size: 100dvh; to fully support vertical writing modes.
  • Redundant @supports Block on interpolate-size:
    • The Fix: Move interpolate-size: allow-keywords; directly into the base html rule — unsupporting browsers naturally ignore unknown declarations.
  • Responsive Cell Span Clamping in Bento Grids:
    • The Fix: Document or apply grid-column: span min(3, var(--sf-bento-cols, 3));.
  • Achromatic / Negative Exponent Limits in Dual-Ratio Scalers:
    • The Fix: Document the mathematical inverse relationship when configuring default dual-ratio ranges — avoid extreme ratio spreads.
  • Logical Direction in .sf-overflow-fade Mask:
    • The Fix: Map the fade direction to a CSS variable --sf-overflow-fade-direction: to right; and reverse it in RTL.
  • Flexbox-based Pancake Footer Alternative:
    • The Fix: Offer .sf-pancake-flex as a resilient alternative where main { flex-grow: 1; } keeps the footer sticky even if other elements are missing.
  • Per-Surface ::selection Colours:
    • The Fix: Add per-surface ::selection overrides inside @supports (color: oklch(from red l c h)) so selection highlights are legible on coloured surfaces.
  • .sf-visually-hidden Alias:
    • The Fix: Add .sf-visually-hidden and .visuallyhidden as aliases to .sr-only for framework interoperability.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions