diff --git a/CHANGELOG.md b/CHANGELOG.md index df52735b..01da902e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## Unreleased +### Added +- **macros:** `.sf-corner-scoop` — mask-based concave-corner cut, with `--top-left`/`--top-right`/`--bottom-left`/`--bottom-right` placement modifiers and `--sf-corner-scoop-size`/`--sf-corner-scoop-at` knobs. +- **macros:** `.sf-overlap` / `.sf-overlap-host` — negative-margin overlap recipe (`--down`/`--start`/`--end` modifiers) driven by `--sf-overlap-pull`, with a host class that reserves matching space. + ## [0.6.35] - 2026-07-03 ### ⚠️ Breaking Changes diff --git a/badges/badge-optimal.json b/badges/badge-optimal.json index 659f00bd..2f64e6f8 100644 --- a/badges/badge-optimal.json +++ b/badges/badge-optimal.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "label": "optimal", - "message": "17.8 kB gzip", + "message": "18.1 kB gzip", "color": "brightgreen", "namedLogo": "css3" } diff --git a/badges/css-custom-data.json b/badges/css-custom-data.json index d7e9501f..36cb5226 100644 --- a/badges/css-custom-data.json +++ b/badges/css-custom-data.json @@ -143,7 +143,7 @@ }, { "name": "--sf-bg-inset", - "description": "Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly.\n\nDefault: `0`", + "description": "Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly.\n\nDefault: `0px`", "syntax": "*" }, { @@ -1626,6 +1626,16 @@ "description": "OKLCH lightness crossover for auto-contrast on colored surfaces. Colors with L above this get dark text; below get light text. Default 0.6.\n\nDefault: `0.6`\n\n(advanced token)", "syntax": "*" }, + { + "name": "--sf-corner-scoop-at", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".\n\nDefault: `100% 0`", + "syntax": "*" + }, + { + "name": "--sf-corner-scoop-size", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".\n\nDefault: `var(--sf-radius-2xl)`", + "syntax": "*" + }, { "name": "--sf-cover-min-height", "description": "Minimum height of the cover layout (typically 100svh).\n\nDefault: `100dvh`", @@ -2396,6 +2406,11 @@ "description": "CSS font-optical-sizing value. 'auto' lets the browser optimize letterforms for the rendered size. Set 'none' to disable optical sizing.\n\nDefault: `auto`\n\n(advanced token)", "syntax": "*" }, + { + "name": "--sf-overlap-pull", + "description": "how far .sf-overlap pulls into the adjacent element. .sf-overlap-host's padding compensation defaults to this same value via a var() fallback in the rule itself (not aliased here — see core/macros.css), so one override on the host tunes both unless --sf-overlap-host-pad is set…\n\nDefault: `var(--sf-space-xl)`", + "syntax": "*" + }, { "name": "--sf-palette-mix-100", "description": "Color-mix percentage for palette step 100. Controls blend depth toward the surface. Default 8%.\n\nDefault: `8%`\n\n(advanced token)", diff --git a/configurator/src/data/api-index.generated.json b/configurator/src/data/api-index.generated.json index 98fd6750..81bfd882 100644 --- a/configurator/src/data/api-index.generated.json +++ b/configurator/src/data/api-index.generated.json @@ -2,7 +2,7 @@ "_sync": { "generatedBy": "configurator/scripts/sync-api.mjs", "source": "docs/api-index.json", - "tokensHash": "354667facc7c", + "tokensHash": "4a0ad429ba01", "bundles": [ "full", "optimal", @@ -10,16 +10,16 @@ "optimal-utilities" ], "counts": { - "tokens": 684, + "tokens": 687, "byTier": { - "PUBLIC": 628, + "PUBLIC": 631, "PUBLIC-ADVANCED": 55, "INTERNAL": 1 }, "byCategory": { "Layout tokens": 53, "Core tokens": 602, - "Macro tokens": 23, + "Macro tokens": 26, "Component tokens": 6 } } @@ -678,7 +678,7 @@ "group": "Background layer (.sf-bg)", "description": "Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly.", "note": "Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly.", - "value": "0", + "value": "0px", "aliasOf": null, "registered": false, "syntax": null, @@ -7494,6 +7494,52 @@ "optimal-utilities" ] }, + { + "name": "--sf-corner-scoop-at", + "tier": "PUBLIC", + "role": "knob", + "namespace": "corner", + "category": "Macro tokens", + "group": "Concave corner", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".", + "note": "", + "value": "100% 0", + "aliasOf": null, + "registered": false, + "syntax": null, + "fallbackOnly": false, + "optional": false, + "layer": "slashed.tokens", + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "--sf-corner-scoop-size", + "tier": "PUBLIC", + "role": "consumption", + "namespace": "corner", + "category": "Macro tokens", + "group": "Concave corner", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".", + "note": "", + "value": "var(--sf-radius-2xl)", + "aliasOf": "--sf-radius-2xl", + "registered": false, + "syntax": null, + "fallbackOnly": false, + "optional": false, + "layer": "slashed.tokens", + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "--sf-cover-min-height", "tier": "PUBLIC", @@ -11053,6 +11099,29 @@ "optimal-utilities" ] }, + { + "name": "--sf-overlap-pull", + "tier": "PUBLIC", + "role": "consumption", + "namespace": "overlap", + "category": "Macro tokens", + "group": "Overlap recipe", + "description": "how far .sf-overlap pulls into the adjacent element. .sf-overlap-host's padding compensation defaults to this same value via a var() fallback in the rule itself (not aliased here — see core/macros.css), so one override on the host tunes both unless --sf-overlap-host-pad is set…", + "note": "", + "value": "var(--sf-space-xl)", + "aliasOf": "--sf-space-xl", + "registered": false, + "syntax": null, + "fallbackOnly": false, + "optional": false, + "layer": "slashed.tokens", + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "--sf-palette-mix-100", "tier": "PUBLIC-ADVANCED", diff --git a/configurator/src/data/classes.generated.json b/configurator/src/data/classes.generated.json index d6d2fa65..3cc4a20b 100644 --- a/configurator/src/data/classes.generated.json +++ b/configurator/src/data/classes.generated.json @@ -834,6 +834,56 @@ "optional": false, "layer": "slashed.layout" }, + { + "name": "sf-corner-scoop", + "selector": ".sf-corner-scoop", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-corner-scoop--bottom-left", + "selector": ".sf-corner-scoop--bottom-left", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-corner-scoop--bottom-right", + "selector": ".sf-corner-scoop--bottom-right", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-corner-scoop--top-left", + "selector": ".sf-corner-scoop--top-left", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-corner-scoop--top-right", + "selector": ".sf-corner-scoop--top-right", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "optional": false, + "layer": "slashed.macros" + }, { "name": "sf-cover", "selector": ".sf-cover", @@ -1854,6 +1904,56 @@ "optional": false, "layer": "slashed.macros" }, + { + "name": "sf-overlap", + "selector": ".sf-overlap", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-overlap--down", + "selector": ".sf-overlap--down", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-overlap--end", + "selector": ".sf-overlap--end", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-overlap--start", + "selector": ".sf-overlap--start", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "optional": false, + "layer": "slashed.macros" + }, + { + "name": "sf-overlap-host", + "selector": ".sf-overlap-host", + "kind": "macro", + "category": "Macro classes", + "group": "", + "description": "Card-container recipe — the receiving side of an overlap: an isolated stacking context with block-start padding compensation so in-flow content clears an element intruding from above. The padding knob defaults to the pull knob so one override tunes both.", + "optional": false, + "layer": "slashed.macros" + }, { "name": "sf-pancake", "selector": ".sf-pancake", diff --git a/configurator/src/data/domain-patterns.json b/configurator/src/data/domain-patterns.json index 4d685c7b..aaee8934 100644 --- a/configurator/src/data/domain-patterns.json +++ b/configurator/src/data/domain-patterns.json @@ -1,5 +1,5 @@ { - "macros": ["prose", "flow-space", "line-clamp", "aspect", "scroll-shadow", "scrim", "surface-color", "content-intrinsic"], + "macros": ["prose", "flow-space", "line-clamp", "aspect", "scroll-shadow", "scrim", "surface-color", "content-intrinsic", "corner-scoop", "overlap-"], "colors": ["color", "contrast", "palette-mix", "lumlocker", "gradient"], "typography": ["font", "--sf-text", "leading", "tracking", "weight", "body-font", "heading-font", "body-em", "body-text", "heading-text", "display-l-", "display-m-", diff --git a/configurator/src/data/token-registry.generated.json b/configurator/src/data/token-registry.generated.json index 00502e7c..b9393dfa 100644 --- a/configurator/src/data/token-registry.generated.json +++ b/configurator/src/data/token-registry.generated.json @@ -1,7 +1,7 @@ { "_meta": { "generatedBy": "scripts/gen-token-registry.js", - "nextId": 710 + "nextId": 717 }, "tokens": [ { @@ -2869,6 +2869,38 @@ "id": 709, "name": "--sf-boxed-shadow", "removed": true + }, + { + "id": 710, + "name": "--sf-corner-scoop-at" + }, + { + "id": 711, + "name": "--sf-corner-scoop-size" + }, + { + "id": 712, + "name": "--sf-corners-end-end", + "removed": true + }, + { + "id": 713, + "name": "--sf-corners-end-start", + "removed": true + }, + { + "id": 714, + "name": "--sf-corners-start-end", + "removed": true + }, + { + "id": 715, + "name": "--sf-corners-start-start", + "removed": true + }, + { + "id": 716, + "name": "--sf-overlap-pull" } ] } diff --git a/core/macros.css b/core/macros.css index ede34be7..d820768f 100644 --- a/core/macros.css +++ b/core/macros.css @@ -361,4 +361,54 @@ text-decoration-line: none; } + /* Concave corner — a corner that curves AWAY from the box (revealing + whatever sits behind it), via a radial-gradient mask. Physical corner + names (mask geometry has no logical form, same rationale as + .sf-overflow-fade's physical directions). Cuts the element's full + paint at that corner — box-shadow/border don't survive the cut there; + put shadows on a wrapper if needed. Named to mirror the future + `corner-shape: scoop` keyword for an easy @supports migration. */ + .sf-corner-scoop { + /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 18 */ + -webkit-mask-image: radial-gradient(circle at var(--sf-corner-scoop-at), + transparent var(--sf-corner-scoop-size), + black calc(var(--sf-corner-scoop-size) + 0.5px)); + mask-image: radial-gradient(circle at var(--sf-corner-scoop-at), + transparent var(--sf-corner-scoop-size), + black calc(var(--sf-corner-scoop-size) + 0.5px)); + } + .sf-corner-scoop--top-left { --sf-corner-scoop-at: 0 0; } + .sf-corner-scoop--top-right { --sf-corner-scoop-at: 100% 0; } + .sf-corner-scoop--bottom-left { --sf-corner-scoop-at: 0 100%; } + .sf-corner-scoop--bottom-right { --sf-corner-scoop-at: 100% 100%; } + + /* Overlap recipe — one element intentionally overlapping the previous + one (e.g. an image pulled up over the card below it), via a negative + logical margin + a raised stacking context. Directional variants are + standalone (margins are distinct properties, not a re-pointable knob). */ + .sf-overlap, + .sf-overlap--down, + .sf-overlap--start, + .sf-overlap--end { + position: relative; + z-index: var(--sf-z-raised); + } + .sf-overlap { margin-block-start: calc(-1 * var(--sf-overlap-pull)); } + .sf-overlap--down { margin-block-end: calc(-1 * var(--sf-overlap-pull)); } + .sf-overlap--start { margin-inline-start: calc(-1 * var(--sf-overlap-pull)); } + .sf-overlap--end { margin-inline-end: calc(-1 * var(--sf-overlap-pull)); } + + /* Card-container recipe — the receiving side of an overlap: an isolated + stacking context with block-start padding compensation so in-flow + content clears an element intruding from above. The padding knob + defaults to the pull knob so one override tunes both. */ + .sf-overlap-host { + position: relative; + isolation: isolate; + /* var() fallback resolved on this element, not pre-baked at :root — + so a local --sf-overlap-pull override (even without setting + --sf-overlap-host-pad) is honored (#496-class pitfall). */ + padding-block-start: var(--sf-overlap-host-pad, var(--sf-overlap-pull)); + } + } diff --git a/core/tokens.layout.css b/core/tokens.layout.css index d81c8347..5364b25c 100644 --- a/core/tokens.layout.css +++ b/core/tokens.layout.css @@ -88,7 +88,12 @@ inset is a single length applied to every edge; the layer size is derived from it, so a non-zero inset still fills correctly. ---------------------------------------------------------- */ - --sf-bg-inset: 0; + /* 0px, not bare 0 — .sf-bg's inline-size/block-size multiply this by 2 + inside a calc() alongside a percentage; a unitless 0 makes that + calc() a / type mismatch (invalid at + computed-value time), silently collapsing .sf-bg to its intrinsic + size. */ + --sf-bg-inset: 0px; --sf-bg-fit: cover; --sf-bg-position: 50% 50%; --sf-bg-radius: 0; diff --git a/core/tokens.macros.css b/core/tokens.macros.css index 29e25f10..0336a6fc 100644 --- a/core/tokens.macros.css +++ b/core/tokens.macros.css @@ -94,5 +94,21 @@ shadow halo behind glyphs, consumed by .sf-text-protect. */ --sf-scrim-text-shadow: 0 1px 3px oklch(0 0 0 / 0.6); + /* ---------------------------------------------------------- + Concave corner — radius and corner position for .sf-corner-scoop. + Override per element: style="--sf-corner-scoop-size: var(--sf-radius-3xl)". + ---------------------------------------------------------- */ + --sf-corner-scoop-size: var(--sf-radius-2xl); + --sf-corner-scoop-at: 100% 0; + + /* ---------------------------------------------------------- + Overlap recipe — how far .sf-overlap pulls into the adjacent + element. .sf-overlap-host's padding compensation defaults to this + same value via a var() fallback in the rule itself (not aliased + here — see core/macros.css), so one override on the host tunes + both unless --sf-overlap-host-pad is set independently. + ---------------------------------------------------------- */ + --sf-overlap-pull: var(--sf-space-xl); + } } diff --git a/docs/api-index.json b/docs/api-index.json index 2c24183a..4b2f6e3e 100644 --- a/docs/api-index.json +++ b/docs/api-index.json @@ -27,23 +27,23 @@ "optimal-utilities" ], "counts": { - "total": 934, + "total": 947, "by_type": { - "class": 250, - "token": 684 + "class": 260, + "token": 687 }, "by_tier": { "INTERNAL": 1, - "PUBLIC": 878, + "PUBLIC": 891, "PUBLIC-ADVANCED": 55 }, "by_role": { - "consumption": 461, - "knob": 223 + "consumption": 463, + "knob": 224 }, - "tokens": 684, - "classes": 250, - "sf_classes": 201, + "tokens": 687, + "classes": 260, + "sf_classes": 211, "is_classes": 40, "unprefixed_classes": 9, "by_category": { @@ -52,8 +52,8 @@ "Core tokens": 602, "Layout primitives": 136, "Layout tokens": 53, - "Macro classes": 45, - "Macro tokens": 23, + "Macro classes": 55, + "Macro tokens": 26, "Motion / animation": 15, "Print": 4, "State classes": 40, @@ -883,7 +883,7 @@ "area": "layout", "group": "Background layer (.sf-bg)", "description": "Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly.", - "value": "0", + "value": "0px", "aliasOf": null, "registered": false, "animatable": false, @@ -9183,6 +9183,62 @@ "optimal-utilities" ] }, + { + "name": "--sf-corner-scoop-at", + "type": "token", + "tier": "PUBLIC", + "role": "knob", + "namespace": "corner", + "category": "Macro tokens", + "area": "macros", + "group": "Concave corner", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".", + "value": "100% 0", + "aliasOf": null, + "registered": false, + "animatable": false, + "syntax": null, + "inherits": null, + "optional": false, + "layer": "slashed.tokens", + "sourceFiles": [ + "core/tokens.macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "--sf-corner-scoop-size", + "type": "token", + "tier": "PUBLIC", + "role": "consumption", + "namespace": "corner", + "category": "Macro tokens", + "area": "macros", + "group": "Concave corner", + "description": "radius and corner position for .sf-corner-scoop. Override per element: style=\"--sf-corner-scoop-size: var(--sf-radius-3xl)\".", + "value": "var(--sf-radius-2xl)", + "aliasOf": "--sf-radius-2xl", + "registered": false, + "animatable": false, + "syntax": null, + "inherits": null, + "optional": false, + "layer": "slashed.tokens", + "sourceFiles": [ + "core/tokens.macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "--sf-cover-min-height", "type": "token", @@ -13517,6 +13573,34 @@ "optimal-utilities" ] }, + { + "name": "--sf-overlap-pull", + "type": "token", + "tier": "PUBLIC", + "role": "consumption", + "namespace": "overlap", + "category": "Macro tokens", + "area": "macros", + "group": "Overlap recipe", + "description": "how far .sf-overlap pulls into the adjacent element. .sf-overlap-host's padding compensation defaults to this same value via a var() fallback in the rule itself (not aliased here — see core/macros.css), so one override on the host tunes both unless --sf-overlap-host-pad is set…", + "value": "var(--sf-space-xl)", + "aliasOf": "--sf-space-xl", + "registered": false, + "animatable": false, + "syntax": null, + "inherits": null, + "optional": false, + "layer": "slashed.tokens", + "sourceFiles": [ + "core/tokens.macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "--sf-palette-mix-100", "type": "token", @@ -21305,6 +21389,131 @@ "optimal-utilities" ] }, + { + "name": "sf-corner-scoop", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-corner-scoop", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "isVariant": false, + "baseClass": null, + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-corner-scoop--bottom-left", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-corner-scoop--bottom-left", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "isVariant": true, + "baseClass": "sf-corner-scoop", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-corner-scoop--bottom-right", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-corner-scoop--bottom-right", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "isVariant": true, + "baseClass": "sf-corner-scoop", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-corner-scoop--top-left", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-corner-scoop--top-left", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "isVariant": true, + "baseClass": "sf-corner-scoop", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-corner-scoop--top-right", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-corner-scoop--top-right", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that…", + "isVariant": true, + "baseClass": "sf-corner-scoop", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "sf-cover", "type": "class", @@ -23856,6 +24065,131 @@ "optimal-utilities" ] }, + { + "name": "sf-overlap", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-overlap", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "isVariant": false, + "baseClass": null, + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-overlap--down", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-overlap--down", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "isVariant": true, + "baseClass": "sf-overlap", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-overlap--end", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-overlap--end", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "isVariant": true, + "baseClass": "sf-overlap", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-overlap--start", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-overlap--start", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob).", + "isVariant": true, + "baseClass": "sf-overlap", + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, + { + "name": "sf-overlap-host", + "type": "class", + "tier": "PUBLIC", + "selector": ".sf-overlap-host", + "prefix": "sf", + "kind": "macro", + "category": "Macro classes", + "area": "macros", + "group": "", + "description": "Card-container recipe — the receiving side of an overlap: an isolated stacking context with block-start padding compensation so in-flow content clears an element intruding from above. The padding knob defaults to the pull knob so one override tunes both.", + "isVariant": false, + "baseClass": null, + "optional": false, + "layer": "slashed.macros", + "sourceFiles": [ + "core/macros.css" + ], + "bundles": [ + "full", + "optimal", + "optimal-components", + "optimal-utilities" + ] + }, { "name": "sf-pancake", "type": "class", diff --git a/docs/api-index.md b/docs/api-index.md index c7c8b709..9f05681e 100644 --- a/docs/api-index.md +++ b/docs/api-index.md @@ -10,15 +10,15 @@ and a short description. The machine-readable companion (with all columns) is [registry.json](registry.json); for the tier contract see [architecture.md](architecture.md). -**934 elements** — 684 tokens, 250 classes. +**947 elements** — 687 tokens, 260 classes. | Tier | Count | Meaning | |---|---|---| -| PUBLIC | 878 | Everyday surface. SemVer-stable. | +| PUBLIC | 891 | Everyday surface. SemVer-stable. | | PUBLIC-ADVANCED | 55 | Same SemVer guarantee; niche/powerful. | | INTERNAL | 1 | Implementation detail; may change without a major bump. | -## Tokens (684) +## Tokens (687) ### Component tokens (6) @@ -650,7 +650,7 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-bento-row-default` | PUBLIC | knob | bento | `10rem` | Default row height for standard bento grid cells. | | `--sf-bento-row-tall` | PUBLIC | knob | bento | `16rem` | Tall row height for visually prominent or hero bento cells. | | `--sf-bg-fit` | PUBLIC | knob | bg | `cover` | object-fit applied to .sf-bg when used as a bare media element, and to .sf-bg > img / video / picture children. Defaults to cover. | -| `--sf-bg-inset` | PUBLIC | knob | bg | `0` | Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly. | +| `--sf-bg-inset` | PUBLIC | knob | bg | `0px` | Inset from every edge of the parent. A single length applied uniformly; the layer size is derived from it so a non-zero inset still fills correctly. | | `--sf-bg-position` | PUBLIC | knob | bg | `50% 50%` | object-position controlling where the media is anchored within the layer. Defaults to center (50% 50%). | | `--sf-bg-radius` | PUBLIC | knob | bg | `0` | border-radius applied to the .sf-bg layer and its img/video/picture children. Defaults to 0; set to match the parent's radius when clipping is needed. | | `--sf-bg-z` | PUBLIC | knob | bg | `-2` | z-index of the background layer. Defaults to -2 so it sits behind sibling content but above the parent's own background. | @@ -696,14 +696,17 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-switcher-gap` | PUBLIC | consumption | switcher | `var(--sf-gap)` | Gap between switcher columns / rows. | | `--sf-switcher-threshold` | PUBLIC | knob | switcher | `30rem` | Inline-size threshold below which the switcher flips from horizontal to vertical. | -### Macro tokens (23) +### Macro tokens (26) | Token | Tier | Role | Namespace | Default | Description | |---|---|---|---|---|---| | `--sf-aspect` | PUBLIC | knob | aspect | `16 / 9` | Aspect ratio value for the .aspect-ratio macro. | | `--sf-content-intrinsic-size` | PUBLIC | knob | content | `500px` | Intrinsic-size hint for content-visibility: auto (prevents layout shift on first reveal). | +| `--sf-corner-scoop-at` | PUBLIC | knob | corner | `100% 0` | radius and corner position for .sf-corner-scoop. Override per element: style="--sf-corner-scoop-size: var(--sf-radius-3xl)". | +| `--sf-corner-scoop-size` | PUBLIC | consumption | corner | `var(--sf-radius-2xl)` | radius and corner position for .sf-corner-scoop. Override per element: style="--sf-corner-scoop-size: var(--sf-radius-3xl)". | | `--sf-flow-space` | PUBLIC | consumption | flow | `var(--sf-content-gap)` | Margin-block-start applied to all flow children except the first. | | `--sf-line-clamp` | PUBLIC | knob | line | `3` | Number of visible lines before text is clipped with an ellipsis. | +| `--sf-overlap-pull` | PUBLIC | consumption | overlap | `var(--sf-space-xl)` | how far .sf-overlap pulls into the adjacent element. .sf-overlap-host's padding compensation defaults to this same value via a var() fallback in the rule itself (not aliased here — see core/macros.css), so one override on the host tunes both unless --sf-overlap-host-pad is set… | | `--sf-prose-block-margin` | PUBLIC | consumption | prose | `var(--sf-space-m)` | Block margin between prose elements (h2, p, ul, etc.). | | `--sf-prose-blockquote-border` | PUBLIC | consumption | prose | `var(--sf-border-width-2) solid var(--sf-color-border--subtle)` | Left border style for blockquotes inside prose. | | `--sf-prose-blockquote-padding` | PUBLIC | consumption | prose | `var(--sf-space-m)` | Inner padding for blockquotes inside prose. | @@ -724,7 +727,7 @@ and a short description. The machine-readable companion (with all columns) is | `--sf-scroll-shadow-size` | PUBLIC | knob | scroll | `2rem` | Size of the scroll-shadow fade effect on overflowing containers. | | `--sf-surface-color` | PUBLIC | consumption | surface | `var(--sf-color-base)` | Input for the generic .sf-surface macro. Set any color (including palette shades); the macro derives background, auto-contrast foreground, and the contextual token set from it. | -## Classes (250) +## Classes (260) ### Accessibility (8) @@ -880,12 +883,17 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-switcher--no-wrap` | PUBLIC | layout | Switcher | Switcher variant that stays horizontal and never wraps (single-line regardless of container width). | | `.sf-switcher--vertical` | PUBLIC | layout | Switcher | Switcher variant that starts in the vertical (stacked) direction on all sizes. | -### Macro classes (45) +### Macro classes (55) | Class | Tier | Kind | Group | Description | |---|---|---|---|---| | `.sf-aspect` | PUBLIC | macro | Aspect | Sets aspect-ratio from a --sf-aspect-ratio scoped token. Override the token inline to get any ratio without a new class. | | `.sf-content-auto` | PUBLIC | macro | Content visibility | Sets content-visibility: auto on the element. The browser skips rendering off-screen content, improving LCP for long pages. | +| `.sf-corner-scoop` | PUBLIC | macro | — | Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that… | +| `.sf-corner-scoop--bottom-left` | PUBLIC | macro | — | Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that… | +| `.sf-corner-scoop--bottom-right` | PUBLIC | macro | — | Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that… | +| `.sf-corner-scoop--top-left` | PUBLIC | macro | — | Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that… | +| `.sf-corner-scoop--top-right` | PUBLIC | macro | — | Concave corner — a corner that curves AWAY from the box (revealing whatever sits behind it), via a radial-gradient mask. Physical corner names (mask geometry has no logical form, same rationale as .sf-overflow-fade's physical directions). Cuts the element's full paint at that… | | `.sf-drop-shadow-l` | PUBLIC | macro | — | filter: drop-shadow() utilities — unlike box-shadow, follows the element's actual alpha shape (SVG icons, PNG cutouts, transparent logos). | | `.sf-drop-shadow-m` | PUBLIC | macro | — | filter: drop-shadow() utilities — unlike box-shadow, follows the element's actual alpha shape (SVG icons, PNG cutouts, transparent logos). | | `.sf-drop-shadow-s` | PUBLIC | macro | — | filter: drop-shadow() utilities — unlike box-shadow, follows the element's actual alpha shape (SVG icons, PNG cutouts, transparent logos). | @@ -906,6 +914,11 @@ and a short description. The machine-readable companion (with all columns) is | `.sf-overflow-fade--left` | PUBLIC | macro | — | Overflow fade variant: fades the left (inline-start) edge. | | `.sf-overflow-fade--right` | PUBLIC | macro | — | Overflow fade variant: fades the right (inline-end) edge. Explicit alias for the default sf-overflow-fade behaviour. | | `.sf-overflow-fade--top` | PUBLIC | macro | — | Overflow fade variant: fades the top (block-start) edge. | +| `.sf-overlap` | PUBLIC | macro | — | Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob). | +| `.sf-overlap--down` | PUBLIC | macro | — | Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob). | +| `.sf-overlap--end` | PUBLIC | macro | — | Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob). | +| `.sf-overlap--start` | PUBLIC | macro | — | Overlap recipe — one element intentionally overlapping the previous one (e.g. an image pulled up over the card below it), via a negative logical margin + a raised stacking context. Directional variants are standalone (margins are distinct properties, not a re-pointable knob). | +| `.sf-overlap-host` | PUBLIC | macro | — | Card-container recipe — the receiving side of an overlap: an isolated stacking context with block-start padding compensation so in-flow content clears an element intruding from above. The padding knob defaults to the pull knob so one override tunes both. | | `.sf-prose` | PUBLIC | macro | Prose | Opinionated typographic defaults for long-form content: heading hierarchy, paragraph spacing, blockquote, code, and list styling. Override with sf-not-prose. | | `.sf-scrim` | PUBLIC | macro | Scrim | Overlay container with a gradient darkening scrim behind text placed on an image or colored background. | | `.sf-scrim__content` | PUBLIC | macro | Scrim | Content area inside an sf-scrim. Positioned above the gradient overlay layer. | diff --git a/docs/classes.md b/docs/classes.md index 0083995d..eb158df3 100644 --- a/docs/classes.md +++ b/docs/classes.md @@ -3,7 +3,7 @@ > **Generated** from source by `scripts/gen-class-reference.js` — > run `npm run docs:classes` to refresh. Do not edit by hand. -**201 .sf-classes, 40 .is-classes.** Every `.sf-*` layout/macro class and `.is-*` state class. +**211 .sf-classes, 40 .is-classes.** Every `.sf-*` layout/macro class and `.is-*` state class. See [architecture.md](architecture.md) for layer order and naming conventions. ## Layout primitives (`core/layout.css`) @@ -151,12 +151,17 @@ See [architecture.md](architecture.md) for layer order and naming conventions. ## Macro classes (`core/macros.css`) -45 classes. +55 classes. | Class | |---| | `.sf-aspect` | | `.sf-content-auto` | +| `.sf-corner-scoop` | +| `.sf-corner-scoop--bottom-left` | +| `.sf-corner-scoop--bottom-right` | +| `.sf-corner-scoop--top-left` | +| `.sf-corner-scoop--top-right` | | `.sf-drop-shadow-l` | | `.sf-drop-shadow-m` | | `.sf-drop-shadow-s` | @@ -177,6 +182,11 @@ See [architecture.md](architecture.md) for layer order and naming conventions. | `.sf-overflow-fade--left` | | `.sf-overflow-fade--right` | | `.sf-overflow-fade--top` | +| `.sf-overlap` | +| `.sf-overlap--down` | +| `.sf-overlap--end` | +| `.sf-overlap--start` | +| `.sf-overlap-host` | | `.sf-prose` | | `.sf-scrim` | | `.sf-scrim--bottom` | diff --git a/docs/demo.html b/docs/demo.html index e84f3017..9bbfc79c 100644 --- a/docs/demo.html +++ b/docs/demo.html @@ -2753,6 +2753,36 @@

.sf-bg — backgr +

.sf-corner-scoop — concave corner

+

A corner that curves away from the box, revealing the page behind it, via a mask-image radial gradient.

+
+
+
+
+
+
+ +

.sf-overlap / .sf-overlap-host — intentional overlap recipe

+

One element pulled up over the next, which reserves clearance for it automatically.

+
+
+
+ .sf-overlap-host — content clears the badge above +
+
+

Directional variants: .sf-overlap--down (pulls into the next element), .sf-overlap--start / .sf-overlap--end (pulls into the inline edge).

+ +

Macros in action — a composed profile card

+

Both recipes together on one realistic card: a notched banner (.sf-corner-scoop) with an avatar overlapping into the body below (.sf-overlap / .sf-overlap-host).

+
+
+
+
+ Jamie Chen +

Design Systems Lead

+
+
+

.sf-focus-shadow — box-shadow focus ring (opt-in)

Tab to this link: the focus indicator follows the rounded corner via box-shadow: var(--sf-focus-ring-shadow) instead of the default outline.

diff --git a/docs/llm-guide.md b/docs/llm-guide.md index 857999b5..5567bca7 100644 --- a/docs/llm-guide.md +++ b/docs/llm-guide.md @@ -861,8 +861,21 @@ Each primitive has its own knobs. Override locally (`style="--sf-cluster-gap: 2r --sf-scrim-direction: to top --sf-scrim-gradient: linear-gradient(…) /* composed — override for multi-stop */ --sf-scrim-text-shadow: 0 1px 3px oklch(0 0 0 / 0.6) /* .sf-text-protect */ + +/* Concave corner (.sf-corner-scoop) */ +--sf-corner-scoop-size: var(--sf-radius-2xl) +--sf-corner-scoop-at: 100% 0 + +/* Overlap recipe (.sf-overlap / .sf-overlap-host) */ +--sf-overlap-pull: var(--sf-space-xl) ``` +`.sf-overlap-host`'s block-start padding reads `var(--sf-overlap-host-pad, var(--sf-overlap-pull))` +directly in the CSS declaration (not a declared token) so a per-element override is picked up +without the #496-class staleness bug a `:root` alias would have. Set `--sf-overlap-host-pad` +inline to compensate by something other than the default pull amount; it isn't part of the token +registry. + ### 8.7 Header and safe area ```css diff --git a/docs/macros.md b/docs/macros.md index 9fbe228e..6abca6d4 100644 --- a/docs/macros.md +++ b/docs/macros.md @@ -492,6 +492,25 @@ are lifted above the scrim. Position your content over the image with `position: absolute` (as above) or use a CSS `background-image` on the wrapper instead of an `` child. +**Media background + overlay + stacked content, with no manual +`z-index`:** compose with the [`.sf-bg`](layout.md) layout primitive +instead of a plain `` — it auto-fills the parent (`position: +absolute; inset: 0`) and already composes under `.sf-scrim` by design, +so `img`/`video` background, gradient, and content stack correctly with +zero extra positioning: + +```html +

+ +
+

Media background, scrim, and content — no z-index to manage

+
+
+``` + +This is the framework's answer to "background media + overlay + stacked +content" — no dedicated macro needed on top of `.sf-bg` + `.sf-scrim`. + Variants: | Class | Effect | @@ -559,3 +578,81 @@ All entrance classes are gated by `prefers-reduced-motion: no-preference`; when the user opts out of motion the animations are inert (no movement). Lives in `core/motion.css`, layer `slashed.motion`. + +--- + +## `.sf-corner-scoop` + +A corner that curves **away** from the box (a concave "notch"), instead +of the normal convex `border-radius`. Reveals whatever sits behind the +element at that corner via a `mask-image` radial gradient. Default +corner is top-right. + +```html +
+ Panel with a corner that curves away +
+``` + +Variants: + +| Class | Effect | +|---|---| +| `.sf-corner-scoop--top-left` | scoop at the top-left corner | +| `.sf-corner-scoop--top-right` | scoop at the top-right corner | +| `.sf-corner-scoop--bottom-left` | scoop at the bottom-left corner | +| `.sf-corner-scoop--bottom-right` | scoop at the bottom-right corner | + +Tokens: + +| Token | Default | What it controls | +|---|---|---| +| `--sf-corner-scoop-size` | `var(--sf-radius-2xl)` | radius of the concave cut | +| `--sf-corner-scoop-at` | `100% 0` | position of the cut (set by the variants above) | + +**Limitations:** masking cuts the element's entire paint at that +corner — `box-shadow`/`border` don't survive the cut there (put shadows +on a wrapper element if needed). Only one scoop per element — a second +mask layer would fill the first hole rather than adding a second cut. +Doesn't compose with other mask-based macros (`.sf-overflow-fade`, +`.sf-scroll-shadow`) on the same element — the last `mask-image` wins. + +--- + +## `.sf-overlap` and `.sf-overlap-host` + +A recipe for one element intentionally overlapping the element before +it (e.g. an avatar or image pulled up over the card below it), and the +receiving container that reserves space for the intrusion. + +```html + +
+ Content starts below the intruding image automatically. +
+``` + +`.sf-overlap` variants (directional — standalone, not knob re-pointers, +since each moves a different margin): + +| Class | Effect | +|---|---| +| `.sf-overlap` | pulls up over the previous element (default) | +| `.sf-overlap--down` | pulls down over the next element | +| `.sf-overlap--start` | pulls into the inline-start side | +| `.sf-overlap--end` | pulls into the inline-end side | + +Tokens: + +| Token | Default | What it controls | +|---|---|---| +| `--sf-overlap-pull` | `var(--sf-space-xl)` | how far `.sf-overlap` pulls into the adjacent element | + +`.sf-overlap-host`'s block-start padding reads `var(--sf-overlap-host-pad, var(--sf-overlap-pull))` +directly in the CSS declaration rather than through a declared token, so it isn't part of the +token registry — set `--sf-overlap-host-pad` inline on an instance to compensate by something +other than the default pull amount. + +`.sf-overlap-host` sets `isolation: isolate` so the overlapping element's +raised `z-index` stays scoped to this container rather than fighting +page-level stacking. diff --git a/docs/registry.json b/docs/registry.json index 70b58c0d..5a01ba6d 100644 --- a/docs/registry.json +++ b/docs/registry.json @@ -19,8 +19,8 @@ "optional/theme-example.css" ], "counts": { - "tokens": 684, - "sf_classes": 201, + "tokens": 687, + "sf_classes": 211, "is_classes": 40 } }, @@ -350,6 +350,8 @@ "--sf-content-width", "--sf-contrast-bias", "--sf-contrast-threshold", + "--sf-corner-scoop-at", + "--sf-corner-scoop-size", "--sf-cover-min-height", "--sf-cover-padding", "--sf-current-font-weight", @@ -505,6 +507,7 @@ "--sf-opacity-disabled", "--sf-opacity-muted", "--sf-optical-sizing", + "--sf-overlap-pull", "--sf-palette-mix-100", "--sf-palette-mix-200", "--sf-palette-mix-300", @@ -749,6 +752,11 @@ "sf-container--wide", "sf-content-auto", "sf-content-grid", + "sf-corner-scoop", + "sf-corner-scoop--bottom-left", + "sf-corner-scoop--bottom-right", + "sf-corner-scoop--top-left", + "sf-corner-scoop--top-right", "sf-cover", "sf-cover--max", "sf-cover--min", @@ -851,6 +859,11 @@ "sf-overflow-fade--left", "sf-overflow-fade--right", "sf-overflow-fade--top", + "sf-overlap", + "sf-overlap--down", + "sf-overlap--end", + "sf-overlap--start", + "sf-overlap-host", "sf-pancake", "sf-prose", "sf-reel", diff --git a/docs/test-coverage-6-token-reference.html b/docs/test-coverage-6-token-reference.html index be49cd9b..54cb86e1 100644 --- a/docs/test-coverage-6-token-reference.html +++ b/docs/test-coverage-6-token-reference.html @@ -109,7 +109,7 @@

Full token reference — tokens

else apply(next); }); - var TOKENS = ["--sf-alternate-gap","--sf-alternate-inner-gap","--sf-animation-blink","--sf-animation-color-pulse","--sf-animation-delay-1","--sf-animation-delay-2","--sf-animation-delay-3","--sf-animation-delay-4","--sf-animation-delay-5","--sf-animation-fade-in","--sf-animation-fade-out","--sf-animation-float","--sf-animation-ping","--sf-animation-scale-down","--sf-animation-scale-up","--sf-animation-shimmer","--sf-animation-slide-in-down","--sf-animation-slide-in-left","--sf-animation-slide-in-right","--sf-animation-slide-in-up","--sf-animation-spin","--sf-aspect","--sf-bento-cols-default","--sf-bento-gap","--sf-bento-row-compact","--sf-bento-row-default","--sf-bento-row-tall","--sf-bg-fit","--sf-bg-inset","--sf-bg-position","--sf-bg-radius","--sf-bg-z","--sf-blur","--sf-body-color","--sf-body-em-style","--sf-body-font-family","--sf-body-font-size","--sf-body-font-weight","--sf-body-line-height","--sf-body-strong-weight","--sf-body-text-wrap","--sf-border","--sf-border-scale","--sf-border-strong","--sf-border-style","--sf-border-subtle","--sf-border-width-1","--sf-border-width-2","--sf-border-width-3","--sf-border-width-4","--sf-border-width-hairline","--sf-box-border-color","--sf-box-border-width","--sf-box-padding","--sf-breakout-width","--sf-btn-padding-block","--sf-btn-padding-inline","--sf-btn-radius","--sf-caret-color","--sf-center-gutter","--sf-center-max","--sf-cluster-align","--sf-cluster-gap","--sf-cluster-justify","--sf-code-font-size","--sf-color-action","--sf-color-action--active","--sf-color-action--hover","--sf-color-action-100","--sf-color-action-200","--sf-color-action-300","--sf-color-action-400","--sf-color-action-50","--sf-color-action-500","--sf-color-action-600","--sf-color-action-700","--sf-color-action-800","--sf-color-action-900","--sf-color-action-950","--sf-color-action-a10","--sf-color-action-a30","--sf-color-action-a5","--sf-color-action-a50","--sf-color-action-a80","--sf-color-action-darker","--sf-color-action-ghost","--sf-color-action-lighter","--sf-color-action-muted","--sf-color-action-source-dark","--sf-color-action-source-light","--sf-color-action-subtle","--sf-color-action-superdark","--sf-color-action-superlight","--sf-color-action-xdark","--sf-color-action-xlight","--sf-color-base","--sf-color-base--active","--sf-color-base--hover","--sf-color-base-100","--sf-color-base-200","--sf-color-base-300","--sf-color-base-400","--sf-color-base-50","--sf-color-base-500","--sf-color-base-600","--sf-color-base-700","--sf-color-base-800","--sf-color-base-900","--sf-color-base-950","--sf-color-base-a10","--sf-color-base-a30","--sf-color-base-a5","--sf-color-base-a50","--sf-color-base-a80","--sf-color-base-ghost","--sf-color-base-muted","--sf-color-base-source-dark","--sf-color-base-source-light","--sf-color-base-subtle","--sf-color-bg","--sf-color-bg--active","--sf-color-bg--disabled","--sf-color-bg--focus","--sf-color-bg--hover","--sf-color-bg--selected","--sf-color-black","--sf-color-border","--sf-color-border--disabled","--sf-color-border--focus","--sf-color-border--strong","--sf-color-border--subtle","--sf-color-border--translucent","--sf-color-code-bg","--sf-color-code-text","--sf-color-danger","--sf-color-danger-muted","--sf-color-danger-source-dark","--sf-color-danger-source-light","--sf-color-danger-strong","--sf-color-danger-subtle","--sf-color-dim","--sf-color-heading","--sf-color-info","--sf-color-info-muted","--sf-color-info-source-dark","--sf-color-info-source-light","--sf-color-info-strong","--sf-color-info-subtle","--sf-color-inset","--sf-color-inverse","--sf-color-link","--sf-color-link--active","--sf-color-link--disabled","--sf-color-link--hover","--sf-color-link--underline","--sf-color-link--visited","--sf-color-mark-bg","--sf-color-mark-text","--sf-color-neutral","--sf-color-neutral--active","--sf-color-neutral--hover","--sf-color-neutral-100","--sf-color-neutral-200","--sf-color-neutral-300","--sf-color-neutral-400","--sf-color-neutral-50","--sf-color-neutral-500","--sf-color-neutral-600","--sf-color-neutral-700","--sf-color-neutral-800","--sf-color-neutral-900","--sf-color-neutral-950","--sf-color-neutral-a10","--sf-color-neutral-a30","--sf-color-neutral-a5","--sf-color-neutral-a50","--sf-color-neutral-a80","--sf-color-neutral-darker","--sf-color-neutral-ghost","--sf-color-neutral-lighter","--sf-color-neutral-muted","--sf-color-neutral-source-dark","--sf-color-neutral-source-light","--sf-color-neutral-subtle","--sf-color-neutral-superdark","--sf-color-neutral-superlight","--sf-color-neutral-xdark","--sf-color-neutral-xlight","--sf-color-overlay","--sf-color-primary","--sf-color-primary--active","--sf-color-primary--hover","--sf-color-primary-100","--sf-color-primary-200","--sf-color-primary-300","--sf-color-primary-400","--sf-color-primary-50","--sf-color-primary-500","--sf-color-primary-600","--sf-color-primary-700","--sf-color-primary-800","--sf-color-primary-900","--sf-color-primary-950","--sf-color-primary-a10","--sf-color-primary-a30","--sf-color-primary-a5","--sf-color-primary-a50","--sf-color-primary-a80","--sf-color-primary-darker","--sf-color-primary-ghost","--sf-color-primary-lighter","--sf-color-primary-muted","--sf-color-primary-source-dark","--sf-color-primary-source-light","--sf-color-primary-subtle","--sf-color-primary-superdark","--sf-color-primary-superlight","--sf-color-primary-xdark","--sf-color-primary-xlight","--sf-color-raised","--sf-color-scheme","--sf-color-secondary","--sf-color-secondary--active","--sf-color-secondary--hover","--sf-color-secondary-100","--sf-color-secondary-200","--sf-color-secondary-300","--sf-color-secondary-400","--sf-color-secondary-50","--sf-color-secondary-500","--sf-color-secondary-600","--sf-color-secondary-700","--sf-color-secondary-800","--sf-color-secondary-900","--sf-color-secondary-950","--sf-color-secondary-a10","--sf-color-secondary-a30","--sf-color-secondary-a5","--sf-color-secondary-a50","--sf-color-secondary-a80","--sf-color-secondary-darker","--sf-color-secondary-ghost","--sf-color-secondary-lighter","--sf-color-secondary-muted","--sf-color-secondary-source-dark","--sf-color-secondary-source-light","--sf-color-secondary-subtle","--sf-color-secondary-superdark","--sf-color-secondary-superlight","--sf-color-secondary-xdark","--sf-color-secondary-xlight","--sf-color-selection-bg","--sf-color-selection-text","--sf-color-success","--sf-color-success-muted","--sf-color-success-source-dark","--sf-color-success-source-light","--sf-color-success-strong","--sf-color-success-subtle","--sf-color-surface","--sf-color-tertiary","--sf-color-tertiary--active","--sf-color-tertiary--hover","--sf-color-tertiary-100","--sf-color-tertiary-200","--sf-color-tertiary-300","--sf-color-tertiary-400","--sf-color-tertiary-50","--sf-color-tertiary-500","--sf-color-tertiary-600","--sf-color-tertiary-700","--sf-color-tertiary-800","--sf-color-tertiary-900","--sf-color-tertiary-950","--sf-color-tertiary-a10","--sf-color-tertiary-a30","--sf-color-tertiary-a5","--sf-color-tertiary-a50","--sf-color-tertiary-a80","--sf-color-tertiary-darker","--sf-color-tertiary-ghost","--sf-color-tertiary-lighter","--sf-color-tertiary-muted","--sf-color-tertiary-source-dark","--sf-color-tertiary-source-light","--sf-color-tertiary-subtle","--sf-color-tertiary-superdark","--sf-color-tertiary-superlight","--sf-color-tertiary-xdark","--sf-color-tertiary-xlight","--sf-color-text","--sf-color-text--disabled","--sf-color-text--inverse","--sf-color-text--muted","--sf-color-text--on-action","--sf-color-text--on-base","--sf-color-text--on-danger","--sf-color-text--on-info","--sf-color-text--on-inverse","--sf-color-text--on-neutral","--sf-color-text--on-primary","--sf-color-text--on-secondary","--sf-color-text--on-success","--sf-color-text--on-tertiary","--sf-color-text--on-warning","--sf-color-text--placeholder","--sf-color-text--secondary","--sf-color-warning","--sf-color-warning-muted","--sf-color-warning-source-dark","--sf-color-warning-source-light","--sf-color-warning-strong","--sf-color-warning-subtle","--sf-color-white","--sf-component-pad","--sf-container-default","--sf-container-full","--sf-container-narrow","--sf-container-prose","--sf-container-wide","--sf-content-gap","--sf-content-intrinsic-size","--sf-content-width","--sf-contrast-bias","--sf-contrast-threshold","--sf-cover-min-height","--sf-cover-padding","--sf-current-font-weight","--sf-display-l-line-height","--sf-display-m-line-height","--sf-display-s-line-height","--sf-divider-color","--sf-divider-gap","--sf-divider-style","--sf-divider-width","--sf-drop-shadow-l","--sf-drop-shadow-m","--sf-drop-shadow-s","--sf-duration-fast","--sf-duration-instant","--sf-duration-none","--sf-duration-normal","--sf-duration-slow","--sf-duration-slower","--sf-ease-bounce","--sf-ease-elastic","--sf-ease-in","--sf-ease-in-out","--sf-ease-linear","--sf-ease-out","--sf-ease-overshoot","--sf-ease-spring","--sf-equal-gap","--sf-equal-min-col","--sf-equal-rule-color","--sf-equal-rule-style","--sf-equal-rule-width","--sf-field-block","--sf-field-padding-block","--sf-field-padding-inline","--sf-field-radius","--sf-field-required-marker","--sf-flow-space","--sf-fluid-max-vw","--sf-fluid-min-vw","--sf-focus-ring-color","--sf-focus-ring-offset","--sf-focus-ring-shadow","--sf-focus-ring-style","--sf-focus-ring-width","--sf-font-body","--sf-font-display","--sf-font-features","--sf-font-geometric","--sf-font-heading","--sf-font-humanist","--sf-font-mono","--sf-font-numeric","--sf-font-slab","--sf-font-variation","--sf-font-weight-body","--sf-font-weight-bold","--sf-font-weight-display","--sf-font-weight-heading","--sf-font-weight-interactive","--sf-font-weight-light","--sf-font-weight-medium","--sf-font-weight-normal","--sf-font-weight-semibold","--sf-font-weight-strong","--sf-frame-ratio","--sf-gap","--sf-gradient-brand","--sf-gradient-fade--b","--sf-gradient-fade--l","--sf-gradient-fade--r","--sf-gradient-fade--t","--sf-gradient-primary","--sf-gradient-secondary","--sf-gradient-surface","--sf-gradient-tertiary","--sf-grid-gap","--sf-grid-min","--sf-grid-min-2xl","--sf-grid-min-l","--sf-grid-min-m","--sf-grid-min-s","--sf-grid-min-xl","--sf-grid-min-xs","--sf-gutter","--sf-h1-font-weight","--sf-h1-letter-spacing","--sf-h1-line-height","--sf-h1-max-width","--sf-h1-size","--sf-h2-font-weight","--sf-h2-letter-spacing","--sf-h2-line-height","--sf-h2-max-width","--sf-h2-size","--sf-h3-font-weight","--sf-h3-letter-spacing","--sf-h3-line-height","--sf-h3-max-width","--sf-h3-size","--sf-h4-font-weight","--sf-h4-letter-spacing","--sf-h4-line-height","--sf-h4-max-width","--sf-h4-size","--sf-h5-font-weight","--sf-h5-letter-spacing","--sf-h5-line-height","--sf-h5-max-width","--sf-h5-size","--sf-h6-font-weight","--sf-h6-letter-spacing","--sf-h6-line-height","--sf-h6-max-width","--sf-h6-size","--sf-header-height","--sf-header-height-desktop","--sf-header-height-mobile","--sf-heading-color","--sf-heading-font-family","--sf-heading-text-wrap","--sf-icon-2xl","--sf-icon-box-bg","--sf-icon-box-border","--sf-icon-box-pad","--sf-icon-box-radius","--sf-icon-l","--sf-icon-m","--sf-icon-s","--sf-icon-xl","--sf-icon-xs","--sf-imposter-margin","--sf-is-active","--sf-is-current","--sf-is-dark","--sf-is-open","--sf-is-pressed","--sf-leading-normal","--sf-leading-relaxed","--sf-leading-snug","--sf-leading-taper","--sf-leading-tight","--sf-line-clamp","--sf-link-external-marker","--sf-link-underline-offset","--sf-link-underline-thickness","--sf-lumlocker","--sf-mask-scrim-end","--sf-mask-scrim-start","--sf-motion-scale","--sf-object-fit","--sf-object-position","--sf-opacity-disabled","--sf-opacity-muted","--sf-optical-sizing","--sf-palette-mix-100","--sf-palette-mix-200","--sf-palette-mix-300","--sf-palette-mix-400","--sf-palette-mix-50","--sf-palette-mix-600","--sf-palette-mix-700","--sf-palette-mix-800","--sf-palette-mix-900","--sf-palette-mix-950","--sf-print-base-size","--sf-print-page-margin","--sf-print-page-size","--sf-prose-block-margin","--sf-prose-blockquote-border","--sf-prose-blockquote-padding","--sf-prose-figcaption-size","--sf-prose-figure-margin","--sf-prose-heading-gap","--sf-prose-hr-margin","--sf-prose-list-gap","--sf-prose-marker-color","--sf-prose-media-margin","--sf-prose-media-radius","--sf-prose-nested-list-gap","--sf-prose-paragraph","--sf-prose-table-pad","--sf-radius-2xl","--sf-radius-2xs","--sf-radius-3xl","--sf-radius-4xl","--sf-radius-full","--sf-radius-l","--sf-radius-m","--sf-radius-none","--sf-radius-outer","--sf-radius-pill","--sf-radius-s","--sf-radius-scale","--sf-radius-xl","--sf-radius-xs","--sf-ratio-3-2","--sf-ratio-4-3","--sf-ratio-cinema","--sf-ratio-golden","--sf-ratio-portrait","--sf-ratio-square","--sf-ratio-video","--sf-reel-gap","--sf-reel-height","--sf-reel-item-width","--sf-safe-bottom","--sf-safe-left","--sf-safe-right","--sf-safe-top","--sf-scrim-color","--sf-scrim-direction","--sf-scrim-gradient","--sf-scrim-text-shadow","--sf-scroll-shadow-size","--sf-scroll-timeline-range-end","--sf-scroll-timeline-range-start","--sf-scrollbar-thumb","--sf-scrollbar-track","--sf-section-pad","--sf-section-pad--2xl","--sf-section-pad--l","--sf-section-pad--m","--sf-section-pad--s","--sf-section-pad--xl","--sf-section-pad--xs","--sf-section-scale","--sf-shadow-2xl","--sf-shadow-color","--sf-shadow-glow","--sf-shadow-glow-color","--sf-shadow-inner","--sf-shadow-l","--sf-shadow-lightness","--sf-shadow-m","--sf-shadow-none","--sf-shadow-s","--sf-shadow-strength","--sf-shadow-xl","--sf-shadow-xs","--sf-sidebar-gap","--sf-sidebar-min-width","--sf-sidebar-width","--sf-size-l","--sf-size-m","--sf-size-s","--sf-size-xl","--sf-size-xs","--sf-space-2xl","--sf-space-2xs","--sf-space-3xl","--sf-space-4xl","--sf-space-base-max","--sf-space-base-min","--sf-space-l","--sf-space-m","--sf-space-none","--sf-space-px","--sf-space-ratio-max","--sf-space-ratio-min","--sf-space-s","--sf-space-scale","--sf-space-xl","--sf-space-xs","--sf-stack-gap","--sf-state-pending-opacity","--sf-sticky-offset","--sf-sticky-offset-desktop","--sf-sticky-offset-mobile","--sf-surface-color","--sf-switcher-gap","--sf-switcher-threshold","--sf-text-2xl","--sf-text-2xl-font-weight","--sf-text-2xl-letter-spacing","--sf-text-2xl-line-height","--sf-text-2xl-max-width","--sf-text-2xs","--sf-text-2xs-font-weight","--sf-text-2xs-letter-spacing","--sf-text-2xs-line-height","--sf-text-2xs-max-width","--sf-text-3xl","--sf-text-3xl-font-weight","--sf-text-3xl-letter-spacing","--sf-text-3xl-line-height","--sf-text-3xl-max-width","--sf-text-4xl","--sf-text-4xl-font-weight","--sf-text-4xl-letter-spacing","--sf-text-4xl-line-height","--sf-text-4xl-max-width","--sf-text-base-max","--sf-text-base-min","--sf-text-display-base-max","--sf-text-display-base-min","--sf-text-display-l","--sf-text-display-m","--sf-text-display-s","--sf-text-display-scale","--sf-text-l","--sf-text-l-font-weight","--sf-text-l-letter-spacing","--sf-text-l-line-height","--sf-text-l-max-width","--sf-text-m","--sf-text-m-font-weight","--sf-text-m-letter-spacing","--sf-text-m-line-height","--sf-text-m-max-width","--sf-text-ratio-max","--sf-text-ratio-min","--sf-text-s","--sf-text-s-font-weight","--sf-text-s-letter-spacing","--sf-text-s-line-height","--sf-text-s-max-width","--sf-text-scale","--sf-text-shadow-l","--sf-text-shadow-m","--sf-text-shadow-none","--sf-text-shadow-s","--sf-text-xl","--sf-text-xl-font-weight","--sf-text-xl-letter-spacing","--sf-text-xl-line-height","--sf-text-xl-max-width","--sf-text-xs","--sf-text-xs-font-weight","--sf-text-xs-letter-spacing","--sf-text-xs-line-height","--sf-text-xs-max-width","--sf-theme-transition-duration","--sf-touch-target","--sf-tracking-normal","--sf-tracking-tight","--sf-tracking-wide","--sf-tracking-wider","--sf-tracking-widest","--sf-transition-colors","--sf-transition-enter","--sf-transition-exit","--sf-transition-fast","--sf-transition-form-field","--sf-transition-opacity","--sf-transition-overlay","--sf-transition-shadow","--sf-transition-slow","--sf-transition-transform","--sf-z-base","--sf-z-below","--sf-z-dropdown","--sf-z-fixed","--sf-z-modal","--sf-z-overlay","--sf-z-raised","--sf-z-sticky","--sf-z-toast","--sf-z-tooltip"]; + var TOKENS = ["--sf-alternate-gap","--sf-alternate-inner-gap","--sf-animation-blink","--sf-animation-color-pulse","--sf-animation-delay-1","--sf-animation-delay-2","--sf-animation-delay-3","--sf-animation-delay-4","--sf-animation-delay-5","--sf-animation-fade-in","--sf-animation-fade-out","--sf-animation-float","--sf-animation-ping","--sf-animation-scale-down","--sf-animation-scale-up","--sf-animation-shimmer","--sf-animation-slide-in-down","--sf-animation-slide-in-left","--sf-animation-slide-in-right","--sf-animation-slide-in-up","--sf-animation-spin","--sf-aspect","--sf-bento-cols-default","--sf-bento-gap","--sf-bento-row-compact","--sf-bento-row-default","--sf-bento-row-tall","--sf-bg-fit","--sf-bg-inset","--sf-bg-position","--sf-bg-radius","--sf-bg-z","--sf-blur","--sf-body-color","--sf-body-em-style","--sf-body-font-family","--sf-body-font-size","--sf-body-font-weight","--sf-body-line-height","--sf-body-strong-weight","--sf-body-text-wrap","--sf-border","--sf-border-scale","--sf-border-strong","--sf-border-style","--sf-border-subtle","--sf-border-width-1","--sf-border-width-2","--sf-border-width-3","--sf-border-width-4","--sf-border-width-hairline","--sf-box-border-color","--sf-box-border-width","--sf-box-padding","--sf-breakout-width","--sf-btn-padding-block","--sf-btn-padding-inline","--sf-btn-radius","--sf-caret-color","--sf-center-gutter","--sf-center-max","--sf-cluster-align","--sf-cluster-gap","--sf-cluster-justify","--sf-code-font-size","--sf-color-action","--sf-color-action--active","--sf-color-action--hover","--sf-color-action-100","--sf-color-action-200","--sf-color-action-300","--sf-color-action-400","--sf-color-action-50","--sf-color-action-500","--sf-color-action-600","--sf-color-action-700","--sf-color-action-800","--sf-color-action-900","--sf-color-action-950","--sf-color-action-a10","--sf-color-action-a30","--sf-color-action-a5","--sf-color-action-a50","--sf-color-action-a80","--sf-color-action-darker","--sf-color-action-ghost","--sf-color-action-lighter","--sf-color-action-muted","--sf-color-action-source-dark","--sf-color-action-source-light","--sf-color-action-subtle","--sf-color-action-superdark","--sf-color-action-superlight","--sf-color-action-xdark","--sf-color-action-xlight","--sf-color-base","--sf-color-base--active","--sf-color-base--hover","--sf-color-base-100","--sf-color-base-200","--sf-color-base-300","--sf-color-base-400","--sf-color-base-50","--sf-color-base-500","--sf-color-base-600","--sf-color-base-700","--sf-color-base-800","--sf-color-base-900","--sf-color-base-950","--sf-color-base-a10","--sf-color-base-a30","--sf-color-base-a5","--sf-color-base-a50","--sf-color-base-a80","--sf-color-base-ghost","--sf-color-base-muted","--sf-color-base-source-dark","--sf-color-base-source-light","--sf-color-base-subtle","--sf-color-bg","--sf-color-bg--active","--sf-color-bg--disabled","--sf-color-bg--focus","--sf-color-bg--hover","--sf-color-bg--selected","--sf-color-black","--sf-color-border","--sf-color-border--disabled","--sf-color-border--focus","--sf-color-border--strong","--sf-color-border--subtle","--sf-color-border--translucent","--sf-color-code-bg","--sf-color-code-text","--sf-color-danger","--sf-color-danger-muted","--sf-color-danger-source-dark","--sf-color-danger-source-light","--sf-color-danger-strong","--sf-color-danger-subtle","--sf-color-dim","--sf-color-heading","--sf-color-info","--sf-color-info-muted","--sf-color-info-source-dark","--sf-color-info-source-light","--sf-color-info-strong","--sf-color-info-subtle","--sf-color-inset","--sf-color-inverse","--sf-color-link","--sf-color-link--active","--sf-color-link--disabled","--sf-color-link--hover","--sf-color-link--underline","--sf-color-link--visited","--sf-color-mark-bg","--sf-color-mark-text","--sf-color-neutral","--sf-color-neutral--active","--sf-color-neutral--hover","--sf-color-neutral-100","--sf-color-neutral-200","--sf-color-neutral-300","--sf-color-neutral-400","--sf-color-neutral-50","--sf-color-neutral-500","--sf-color-neutral-600","--sf-color-neutral-700","--sf-color-neutral-800","--sf-color-neutral-900","--sf-color-neutral-950","--sf-color-neutral-a10","--sf-color-neutral-a30","--sf-color-neutral-a5","--sf-color-neutral-a50","--sf-color-neutral-a80","--sf-color-neutral-darker","--sf-color-neutral-ghost","--sf-color-neutral-lighter","--sf-color-neutral-muted","--sf-color-neutral-source-dark","--sf-color-neutral-source-light","--sf-color-neutral-subtle","--sf-color-neutral-superdark","--sf-color-neutral-superlight","--sf-color-neutral-xdark","--sf-color-neutral-xlight","--sf-color-overlay","--sf-color-primary","--sf-color-primary--active","--sf-color-primary--hover","--sf-color-primary-100","--sf-color-primary-200","--sf-color-primary-300","--sf-color-primary-400","--sf-color-primary-50","--sf-color-primary-500","--sf-color-primary-600","--sf-color-primary-700","--sf-color-primary-800","--sf-color-primary-900","--sf-color-primary-950","--sf-color-primary-a10","--sf-color-primary-a30","--sf-color-primary-a5","--sf-color-primary-a50","--sf-color-primary-a80","--sf-color-primary-darker","--sf-color-primary-ghost","--sf-color-primary-lighter","--sf-color-primary-muted","--sf-color-primary-source-dark","--sf-color-primary-source-light","--sf-color-primary-subtle","--sf-color-primary-superdark","--sf-color-primary-superlight","--sf-color-primary-xdark","--sf-color-primary-xlight","--sf-color-raised","--sf-color-scheme","--sf-color-secondary","--sf-color-secondary--active","--sf-color-secondary--hover","--sf-color-secondary-100","--sf-color-secondary-200","--sf-color-secondary-300","--sf-color-secondary-400","--sf-color-secondary-50","--sf-color-secondary-500","--sf-color-secondary-600","--sf-color-secondary-700","--sf-color-secondary-800","--sf-color-secondary-900","--sf-color-secondary-950","--sf-color-secondary-a10","--sf-color-secondary-a30","--sf-color-secondary-a5","--sf-color-secondary-a50","--sf-color-secondary-a80","--sf-color-secondary-darker","--sf-color-secondary-ghost","--sf-color-secondary-lighter","--sf-color-secondary-muted","--sf-color-secondary-source-dark","--sf-color-secondary-source-light","--sf-color-secondary-subtle","--sf-color-secondary-superdark","--sf-color-secondary-superlight","--sf-color-secondary-xdark","--sf-color-secondary-xlight","--sf-color-selection-bg","--sf-color-selection-text","--sf-color-success","--sf-color-success-muted","--sf-color-success-source-dark","--sf-color-success-source-light","--sf-color-success-strong","--sf-color-success-subtle","--sf-color-surface","--sf-color-tertiary","--sf-color-tertiary--active","--sf-color-tertiary--hover","--sf-color-tertiary-100","--sf-color-tertiary-200","--sf-color-tertiary-300","--sf-color-tertiary-400","--sf-color-tertiary-50","--sf-color-tertiary-500","--sf-color-tertiary-600","--sf-color-tertiary-700","--sf-color-tertiary-800","--sf-color-tertiary-900","--sf-color-tertiary-950","--sf-color-tertiary-a10","--sf-color-tertiary-a30","--sf-color-tertiary-a5","--sf-color-tertiary-a50","--sf-color-tertiary-a80","--sf-color-tertiary-darker","--sf-color-tertiary-ghost","--sf-color-tertiary-lighter","--sf-color-tertiary-muted","--sf-color-tertiary-source-dark","--sf-color-tertiary-source-light","--sf-color-tertiary-subtle","--sf-color-tertiary-superdark","--sf-color-tertiary-superlight","--sf-color-tertiary-xdark","--sf-color-tertiary-xlight","--sf-color-text","--sf-color-text--disabled","--sf-color-text--inverse","--sf-color-text--muted","--sf-color-text--on-action","--sf-color-text--on-base","--sf-color-text--on-danger","--sf-color-text--on-info","--sf-color-text--on-inverse","--sf-color-text--on-neutral","--sf-color-text--on-primary","--sf-color-text--on-secondary","--sf-color-text--on-success","--sf-color-text--on-tertiary","--sf-color-text--on-warning","--sf-color-text--placeholder","--sf-color-text--secondary","--sf-color-warning","--sf-color-warning-muted","--sf-color-warning-source-dark","--sf-color-warning-source-light","--sf-color-warning-strong","--sf-color-warning-subtle","--sf-color-white","--sf-component-pad","--sf-container-default","--sf-container-full","--sf-container-narrow","--sf-container-prose","--sf-container-wide","--sf-content-gap","--sf-content-intrinsic-size","--sf-content-width","--sf-contrast-bias","--sf-contrast-threshold","--sf-corner-scoop-at","--sf-corner-scoop-size","--sf-cover-min-height","--sf-cover-padding","--sf-current-font-weight","--sf-display-l-line-height","--sf-display-m-line-height","--sf-display-s-line-height","--sf-divider-color","--sf-divider-gap","--sf-divider-style","--sf-divider-width","--sf-drop-shadow-l","--sf-drop-shadow-m","--sf-drop-shadow-s","--sf-duration-fast","--sf-duration-instant","--sf-duration-none","--sf-duration-normal","--sf-duration-slow","--sf-duration-slower","--sf-ease-bounce","--sf-ease-elastic","--sf-ease-in","--sf-ease-in-out","--sf-ease-linear","--sf-ease-out","--sf-ease-overshoot","--sf-ease-spring","--sf-equal-gap","--sf-equal-min-col","--sf-equal-rule-color","--sf-equal-rule-style","--sf-equal-rule-width","--sf-field-block","--sf-field-padding-block","--sf-field-padding-inline","--sf-field-radius","--sf-field-required-marker","--sf-flow-space","--sf-fluid-max-vw","--sf-fluid-min-vw","--sf-focus-ring-color","--sf-focus-ring-offset","--sf-focus-ring-shadow","--sf-focus-ring-style","--sf-focus-ring-width","--sf-font-body","--sf-font-display","--sf-font-features","--sf-font-geometric","--sf-font-heading","--sf-font-humanist","--sf-font-mono","--sf-font-numeric","--sf-font-slab","--sf-font-variation","--sf-font-weight-body","--sf-font-weight-bold","--sf-font-weight-display","--sf-font-weight-heading","--sf-font-weight-interactive","--sf-font-weight-light","--sf-font-weight-medium","--sf-font-weight-normal","--sf-font-weight-semibold","--sf-font-weight-strong","--sf-frame-ratio","--sf-gap","--sf-gradient-brand","--sf-gradient-fade--b","--sf-gradient-fade--l","--sf-gradient-fade--r","--sf-gradient-fade--t","--sf-gradient-primary","--sf-gradient-secondary","--sf-gradient-surface","--sf-gradient-tertiary","--sf-grid-gap","--sf-grid-min","--sf-grid-min-2xl","--sf-grid-min-l","--sf-grid-min-m","--sf-grid-min-s","--sf-grid-min-xl","--sf-grid-min-xs","--sf-gutter","--sf-h1-font-weight","--sf-h1-letter-spacing","--sf-h1-line-height","--sf-h1-max-width","--sf-h1-size","--sf-h2-font-weight","--sf-h2-letter-spacing","--sf-h2-line-height","--sf-h2-max-width","--sf-h2-size","--sf-h3-font-weight","--sf-h3-letter-spacing","--sf-h3-line-height","--sf-h3-max-width","--sf-h3-size","--sf-h4-font-weight","--sf-h4-letter-spacing","--sf-h4-line-height","--sf-h4-max-width","--sf-h4-size","--sf-h5-font-weight","--sf-h5-letter-spacing","--sf-h5-line-height","--sf-h5-max-width","--sf-h5-size","--sf-h6-font-weight","--sf-h6-letter-spacing","--sf-h6-line-height","--sf-h6-max-width","--sf-h6-size","--sf-header-height","--sf-header-height-desktop","--sf-header-height-mobile","--sf-heading-color","--sf-heading-font-family","--sf-heading-text-wrap","--sf-icon-2xl","--sf-icon-box-bg","--sf-icon-box-border","--sf-icon-box-pad","--sf-icon-box-radius","--sf-icon-l","--sf-icon-m","--sf-icon-s","--sf-icon-xl","--sf-icon-xs","--sf-imposter-margin","--sf-is-active","--sf-is-current","--sf-is-dark","--sf-is-open","--sf-is-pressed","--sf-leading-normal","--sf-leading-relaxed","--sf-leading-snug","--sf-leading-taper","--sf-leading-tight","--sf-line-clamp","--sf-link-external-marker","--sf-link-underline-offset","--sf-link-underline-thickness","--sf-lumlocker","--sf-mask-scrim-end","--sf-mask-scrim-start","--sf-motion-scale","--sf-object-fit","--sf-object-position","--sf-opacity-disabled","--sf-opacity-muted","--sf-optical-sizing","--sf-overlap-pull","--sf-palette-mix-100","--sf-palette-mix-200","--sf-palette-mix-300","--sf-palette-mix-400","--sf-palette-mix-50","--sf-palette-mix-600","--sf-palette-mix-700","--sf-palette-mix-800","--sf-palette-mix-900","--sf-palette-mix-950","--sf-print-base-size","--sf-print-page-margin","--sf-print-page-size","--sf-prose-block-margin","--sf-prose-blockquote-border","--sf-prose-blockquote-padding","--sf-prose-figcaption-size","--sf-prose-figure-margin","--sf-prose-heading-gap","--sf-prose-hr-margin","--sf-prose-list-gap","--sf-prose-marker-color","--sf-prose-media-margin","--sf-prose-media-radius","--sf-prose-nested-list-gap","--sf-prose-paragraph","--sf-prose-table-pad","--sf-radius-2xl","--sf-radius-2xs","--sf-radius-3xl","--sf-radius-4xl","--sf-radius-full","--sf-radius-l","--sf-radius-m","--sf-radius-none","--sf-radius-outer","--sf-radius-pill","--sf-radius-s","--sf-radius-scale","--sf-radius-xl","--sf-radius-xs","--sf-ratio-3-2","--sf-ratio-4-3","--sf-ratio-cinema","--sf-ratio-golden","--sf-ratio-portrait","--sf-ratio-square","--sf-ratio-video","--sf-reel-gap","--sf-reel-height","--sf-reel-item-width","--sf-safe-bottom","--sf-safe-left","--sf-safe-right","--sf-safe-top","--sf-scrim-color","--sf-scrim-direction","--sf-scrim-gradient","--sf-scrim-text-shadow","--sf-scroll-shadow-size","--sf-scroll-timeline-range-end","--sf-scroll-timeline-range-start","--sf-scrollbar-thumb","--sf-scrollbar-track","--sf-section-pad","--sf-section-pad--2xl","--sf-section-pad--l","--sf-section-pad--m","--sf-section-pad--s","--sf-section-pad--xl","--sf-section-pad--xs","--sf-section-scale","--sf-shadow-2xl","--sf-shadow-color","--sf-shadow-glow","--sf-shadow-glow-color","--sf-shadow-inner","--sf-shadow-l","--sf-shadow-lightness","--sf-shadow-m","--sf-shadow-none","--sf-shadow-s","--sf-shadow-strength","--sf-shadow-xl","--sf-shadow-xs","--sf-sidebar-gap","--sf-sidebar-min-width","--sf-sidebar-width","--sf-size-l","--sf-size-m","--sf-size-s","--sf-size-xl","--sf-size-xs","--sf-space-2xl","--sf-space-2xs","--sf-space-3xl","--sf-space-4xl","--sf-space-base-max","--sf-space-base-min","--sf-space-l","--sf-space-m","--sf-space-none","--sf-space-px","--sf-space-ratio-max","--sf-space-ratio-min","--sf-space-s","--sf-space-scale","--sf-space-xl","--sf-space-xs","--sf-stack-gap","--sf-state-pending-opacity","--sf-sticky-offset","--sf-sticky-offset-desktop","--sf-sticky-offset-mobile","--sf-surface-color","--sf-switcher-gap","--sf-switcher-threshold","--sf-text-2xl","--sf-text-2xl-font-weight","--sf-text-2xl-letter-spacing","--sf-text-2xl-line-height","--sf-text-2xl-max-width","--sf-text-2xs","--sf-text-2xs-font-weight","--sf-text-2xs-letter-spacing","--sf-text-2xs-line-height","--sf-text-2xs-max-width","--sf-text-3xl","--sf-text-3xl-font-weight","--sf-text-3xl-letter-spacing","--sf-text-3xl-line-height","--sf-text-3xl-max-width","--sf-text-4xl","--sf-text-4xl-font-weight","--sf-text-4xl-letter-spacing","--sf-text-4xl-line-height","--sf-text-4xl-max-width","--sf-text-base-max","--sf-text-base-min","--sf-text-display-base-max","--sf-text-display-base-min","--sf-text-display-l","--sf-text-display-m","--sf-text-display-s","--sf-text-display-scale","--sf-text-l","--sf-text-l-font-weight","--sf-text-l-letter-spacing","--sf-text-l-line-height","--sf-text-l-max-width","--sf-text-m","--sf-text-m-font-weight","--sf-text-m-letter-spacing","--sf-text-m-line-height","--sf-text-m-max-width","--sf-text-ratio-max","--sf-text-ratio-min","--sf-text-s","--sf-text-s-font-weight","--sf-text-s-letter-spacing","--sf-text-s-line-height","--sf-text-s-max-width","--sf-text-scale","--sf-text-shadow-l","--sf-text-shadow-m","--sf-text-shadow-none","--sf-text-shadow-s","--sf-text-xl","--sf-text-xl-font-weight","--sf-text-xl-letter-spacing","--sf-text-xl-line-height","--sf-text-xl-max-width","--sf-text-xs","--sf-text-xs-font-weight","--sf-text-xs-letter-spacing","--sf-text-xs-line-height","--sf-text-xs-max-width","--sf-theme-transition-duration","--sf-touch-target","--sf-tracking-normal","--sf-tracking-tight","--sf-tracking-wide","--sf-tracking-wider","--sf-tracking-widest","--sf-transition-colors","--sf-transition-enter","--sf-transition-exit","--sf-transition-fast","--sf-transition-form-field","--sf-transition-opacity","--sf-transition-overlay","--sf-transition-shadow","--sf-transition-slow","--sf-transition-transform","--sf-z-base","--sf-z-below","--sf-z-dropdown","--sf-z-fixed","--sf-z-modal","--sf-z-overlay","--sf-z-raised","--sf-z-sticky","--sf-z-toast","--sf-z-tooltip"]; document.getElementById('tok-count').textContent = TOKENS.length; var grid = document.getElementById('tok-grid'); var filter = document.getElementById('token-filter'); diff --git a/docs/token-index.json b/docs/token-index.json index 78b7e188..a8ddefaa 100644 --- a/docs/token-index.json +++ b/docs/token-index.json @@ -8,15 +8,15 @@ "optional/tokens.components.css" ], "counts": { - "tokens": 684, + "tokens": 687, "by_tier": { - "PUBLIC": 628, + "PUBLIC": 631, "PUBLIC-ADVANCED": 55, "INTERNAL": 1 }, "by_role": { - "knob": 223, - "consumption": 461 + "knob": 224, + "consumption": 463 } } }, @@ -251,7 +251,7 @@ "files": [ "core/tokens.layout.css" ], - "value": "0" + "value": "0px" }, "--sf-bg-position": { "tier": "PUBLIC", @@ -2621,6 +2621,22 @@ ], "value": "0.6" }, + "--sf-corner-scoop-at": { + "tier": "PUBLIC", + "role": "knob", + "files": [ + "core/tokens.macros.css" + ], + "value": "100% 0" + }, + "--sf-corner-scoop-size": { + "tier": "PUBLIC", + "role": "consumption", + "files": [ + "core/tokens.macros.css" + ], + "value": "var(--sf-radius-2xl)" + }, "--sf-cover-min-height": { "tier": "PUBLIC", "role": "knob", @@ -3861,6 +3877,14 @@ ], "value": "auto" }, + "--sf-overlap-pull": { + "tier": "PUBLIC", + "role": "consumption", + "files": [ + "core/tokens.macros.css" + ], + "value": "var(--sf-space-xl)" + }, "--sf-palette-mix-100": { "tier": "PUBLIC-ADVANCED", "role": "knob", diff --git a/docs/token-index.md b/docs/token-index.md index 2ed74eaf..ef833ddc 100644 --- a/docs/token-index.md +++ b/docs/token-index.md @@ -8,11 +8,11 @@ A cross-reference of every `--sf-*` custom property by **source file** and for the flat name list see [registry.json](registry.json); for the tier contract and naming rules see [architecture.md](architecture.md). -**684 tokens** (deduplicated by name across the 4 token source files). +**687 tokens** (deduplicated by name across the 4 token source files). | Tier | Count | Meaning | |---|---|---| -| PUBLIC | 628 | Everyday knobs. SemVer-stable. | +| PUBLIC | 631 | Everyday knobs. SemVer-stable. | | PUBLIC-ADVANCED | 55 | Same SemVer guarantee; niche/powerful. | | INTERNAL | 1 | Implementation detail; may change without a major bump. | @@ -22,8 +22,8 @@ declared value (a value that references `var(--sf-…)` is a derived output): | Role | Count | Meaning | |---|---|---| -| knob | 223 | Input you **set** to configure the system (a literal primitive: length, number, colour literal, keyword, font stack, easing curve …). | -| consumption | 461 | Ready-to-use output you **read**; derived from other tokens via `var(--sf-…)` (incl. `light-dark()`/`oklch(from …)`/`color-mix()`). | +| knob | 224 | Input you **set** to configure the system (a literal primitive: length, number, colour literal, keyword, font stack, easing curve …). | +| consumption | 463 | Ready-to-use output you **read**; derived from other tokens via `var(--sf-…)` (incl. `light-dark()`/`oklch(from …)`/`color-mix()`). | ## INTERNAL tokens @@ -119,7 +119,7 @@ declared value (a value that references `var(--sf-…)` is a derived output): | `--sf-bento-row-default` | PUBLIC | knob | Layout | `10rem` | | `--sf-bento-row-tall` | PUBLIC | knob | Layout | `16rem` | | `--sf-bg-fit` | PUBLIC | knob | Layout | `cover` | -| `--sf-bg-inset` | PUBLIC | knob | Layout | `0` | +| `--sf-bg-inset` | PUBLIC | knob | Layout | `0px` | | `--sf-bg-position` | PUBLIC | knob | Layout | `50% 50%` | | `--sf-bg-radius` | PUBLIC | knob | Layout | `0` | | `--sf-bg-z` | PUBLIC | knob | Layout | `-2` | @@ -416,6 +416,8 @@ declared value (a value that references `var(--sf-…)` is a derived output): | `--sf-content-width` | PUBLIC | consumption | Layout | `var(--sf-container-default)` | | `--sf-contrast-bias` | PUBLIC-ADVANCED | knob | Core | `0` | | `--sf-contrast-threshold` | PUBLIC-ADVANCED | knob | Core | `0.6` | +| `--sf-corner-scoop-at` | PUBLIC | knob | Macros | `100% 0` | +| `--sf-corner-scoop-size` | PUBLIC | consumption | Macros | `var(--sf-radius-2xl)` | | `--sf-cover-min-height` | PUBLIC | knob | Layout | `100dvh` | | `--sf-cover-padding` | PUBLIC | consumption | Layout | `var(--sf-section-pad)` | | `--sf-current-font-weight` | PUBLIC | consumption | Core | `var(--sf-font-weight-bold)` | @@ -571,6 +573,7 @@ declared value (a value that references `var(--sf-…)` is a derived output): | `--sf-opacity-disabled` | PUBLIC | knob | Core | `0.45` | | `--sf-opacity-muted` | PUBLIC | knob | Core | `0.5` | | `--sf-optical-sizing` | PUBLIC-ADVANCED | knob | Core | `auto` | +| `--sf-overlap-pull` | PUBLIC | consumption | Macros | `var(--sf-space-xl)` | | `--sf-palette-mix-100` | PUBLIC-ADVANCED | knob | Core | `8%` | | `--sf-palette-mix-200` | PUBLIC-ADVANCED | knob | Core | `20%` | | `--sf-palette-mix-300` | PUBLIC-ADVANCED | knob | Core | `40%` | diff --git a/docs/tokens.md b/docs/tokens.md index 786e79ef..d352be6d 100644 --- a/docs/tokens.md +++ b/docs/tokens.md @@ -3,7 +3,7 @@ > **Generated** from source by `scripts/gen-token-reference.js` — > run `npm run docs:tokens` to refresh. Do not edit by hand. -**684 tokens.** Every `--sf-*` custom property and its default value, grouped by source file. +**687 tokens.** Every `--sf-*` custom property and its default value, grouped by source file. If a token is defined in multiple files, it is listed once per section — so this count can be higher than `docs/registry.json` (which deduplicates by name). See [architecture.md](architecture.md) for the PUBLIC / PUBLIC-ADVANCED / INTERNAL @@ -634,7 +634,7 @@ rebrand workflow. | `--sf-bento-row-default` | `10rem` | | `--sf-bento-row-tall` | `16rem` | | `--sf-bg-fit` | `cover` | -| `--sf-bg-inset` | `0` | +| `--sf-bg-inset` | `0px` | | `--sf-bg-position` | `50% 50%` | | `--sf-bg-radius` | `0` | | `--sf-bg-z` | `-2` | @@ -682,14 +682,17 @@ rebrand workflow. ## Macro tokens (`core/tokens.macros.css`) -23 tokens. +26 tokens. | Token | Default | |---|---| | `--sf-aspect` | `16 / 9` | | `--sf-content-intrinsic-size` | `500px` | +| `--sf-corner-scoop-at` | `100% 0` | +| `--sf-corner-scoop-size` | `var(--sf-radius-2xl)` | | `--sf-flow-space` | `var(--sf-content-gap)` | | `--sf-line-clamp` | `3` | +| `--sf-overlap-pull` | `var(--sf-space-xl)` | | `--sf-prose-block-margin` | `var(--sf-space-m)` | | `--sf-prose-blockquote-border` | `var(--sf-border-width-2) solid var(--sf-color-border--subtle)` | | `--sf-prose-blockquote-padding` | `var(--sf-space-m)` | diff --git a/reports/full-api-audit/results/tokens-report.json b/reports/full-api-audit/results/tokens-report.json index ecd4e07c..2ddb041e 100644 --- a/reports/full-api-audit/results/tokens-report.json +++ b/reports/full-api-audit/results/tokens-report.json @@ -1,7 +1,7 @@ { "summary": { - "totalTokens": 684, - "rendered": 684, + "totalTokens": 687, + "rendered": 687, "consoleErrors": 0, "requestFailures": 0, "empties": 8, @@ -8925,6 +8925,45 @@ "shown": "light-dark(\n oklch(from oklch(0.52 0.025 260) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from oklch(0.52 0.025 260) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n )", "darkComputed": "oklch(from oklch(0.69 0.0225 260) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)", "changedInDark": true + }, + { + "name": "--sf-corner-scoop-size", + "tier": "PUBLIC", + "role": "consumption", + "category": "Macro tokens", + "declared": "var(--sf-radius-2xl)", + "aliasOf": "--sf-radius-2xl", + "issues": [], + "computed": "24px", + "shown": "24px", + "darkComputed": "24px", + "changedInDark": false + }, + { + "name": "--sf-corner-scoop-at", + "tier": "PUBLIC", + "role": "knob", + "category": "Macro tokens", + "declared": "100% 0", + "aliasOf": null, + "issues": [], + "computed": "100% 0", + "shown": "100% 0", + "darkComputed": "100% 0", + "changedInDark": false + }, + { + "name": "--sf-overlap-pull", + "tier": "PUBLIC", + "role": "consumption", + "category": "Macro tokens", + "declared": "var(--sf-space-xl)", + "aliasOf": "--sf-space-xl", + "issues": [], + "computed": "52.1404px", + "shown": "52.1404px", + "darkComputed": "52.1404px", + "changedInDark": false } ] -} \ No newline at end of file +} diff --git a/scripts/check-macro-catalog.js b/scripts/check-macro-catalog.js index e2c13ae9..e721bdb7 100644 --- a/scripts/check-macro-catalog.js +++ b/scripts/check-macro-catalog.js @@ -38,7 +38,7 @@ const SKIP_IN_CSS = new Set([ // Classes mentioned in macros.md as cross-references but NOT expected to be // defined in the macro CSS sources (they live in layout CSS, documented elsewhere). const SKIP_IN_DOCS = new Set([ - '.sf-frame', '.sf-reel', + '.sf-frame', '.sf-reel', '.sf-bg', ]); // ── 1. Collect classes from CSS ─────────────────────────────────────────────── diff --git a/scripts/lib/api-index/extract.js b/scripts/lib/api-index/extract.js index 3b28465c..2041de92 100644 --- a/scripts/lib/api-index/extract.js +++ b/scripts/lib/api-index/extract.js @@ -192,19 +192,25 @@ export function parseNote(raw) { * @param {string} css source CSS (comments intact) * @returns {Array} ordered comment records */ +// Tooling directive comments (stylelint-disable, eslint-disable, …) suppress +// a linter rule for the next line — they document a workaround for the rule, +// not the selector that happens to follow. Never usable as a description. +const DIRECTIVE_COMMENT = /^\/\*\s*(?:stylelint|eslint)-(?:disable|enable)\b/; + export function collectComments(css) { const comments = []; for (const m of css.matchAll(/\/\*[\s\S]*?\*\//g)) { const raw = m[0]; const start = m.index; const end = m.index + raw.length; - const isHeader = /SLASHED\s+[—-]/.test(raw) && start < 4; + const isHeader = /SLASHED\s+[—-]/.test(raw) && start < 4; + const isDirective = DIRECTIVE_COMMENT.test(raw); // A banner is either a divider-fenced comment, a "/* -- Title -- */" form, // or a single-line at-rule sub-header ("/* @property — STATUS COLORS */"). const singleLineSubHeader = !/\n/.test(raw.trim()) && /^\/\*\s*@[\w-]+\s+[—–-]\s+\S/.test(raw); const isBanner = DIVIDER_RUN.test(raw) || /^\/\*\s*--\s+\S/.test(raw) || singleLineSubHeader; const parsed = (isBanner || isHeader) ? parseBanner(raw) : parseNote(raw); - comments.push({ start, end, raw, isHeader, isBanner, ...parsed }); + comments.push({ start, end, raw, isHeader, isDirective, isBanner, ...parsed }); } return comments; } @@ -222,7 +228,7 @@ export function contextAt(comments, idx) { let nearest = null; for (const c of comments) { if (c.end > idx) break; - if (c.isHeader) continue; + if (c.isHeader || c.isDirective) continue; nearest = c; if (c.isBanner) banner = c; } diff --git a/tests/macros.spec.js b/tests/macros.spec.js index aabcc829..252b7c55 100644 --- a/tests/macros.spec.js +++ b/tests/macros.spec.js @@ -406,3 +406,126 @@ test.describe('macro: .sf-entrance--fade', () => { expect(name).toBe('none'); }); }); + +test.describe('macro: .sf-corner-scoop', () => { + test('sets a radial-gradient mask', async ({ page }) => { + await setup(page, `
`); + const mask = await page.locator('#t').evaluate(el => getComputedStyle(el).maskImage); + expect(mask).toContain('radial-gradient'); + }); + + for (const [cls, at] of [ + ['sf-corner-scoop--top-left', '0 0'], + ['sf-corner-scoop--top-right', '100% 0'], + ['sf-corner-scoop--bottom-left', '0 100%'], + ['sf-corner-scoop--bottom-right', '100% 100%'], + ]) { + test(`.${cls} points the cut at ${at}`, async ({ page }) => { + await setup(page, `
`); + const value = await page.locator('#t').evaluate(el => + getComputedStyle(el).getPropertyValue('--sf-corner-scoop-at').trim() + ); + expect(value).toBe(at); + }); + } + + test('--sf-corner-scoop-size override changes the resolved cut radius', async ({ page }) => { + await setup(page, `
`); + const size = await page.locator('#t').evaluate(el => + getComputedStyle(el).getPropertyValue('--sf-corner-scoop-size').trim() + ); + expect(size).toBe('40px'); + }); +}); + +test.describe('macro: .sf-overlap / .sf-overlap-host', () => { + test('pulls the element up over the previous sibling by --sf-overlap-pull', async ({ page }) => { + await setup(page, ` + +
overlap
+ `); + const box = await page.evaluate(() => { + const prev = document.getElementById('prev').getBoundingClientRect(); + const t = document.getElementById('t').getBoundingClientRect(); + return { overlapAmount: prev.bottom - t.top }; + }); + expect(box.overlapAmount).toBeCloseTo(20, 0); + }); + + test('is positioned and raised above normal flow', async ({ page }) => { + await setup(page, `
x
`); + const cs = await page.locator('#t').evaluate(el => ({ + position: getComputedStyle(el).position, + zIndex: getComputedStyle(el).zIndex, + })); + expect(cs.position).toBe('relative'); + expect(Number(cs.zIndex)).toBeGreaterThan(0); + }); + + for (const [cls, prop] of [ + ['sf-overlap--down', 'marginBottom'], + ['sf-overlap--start', 'marginLeft'], + ['sf-overlap--end', 'marginRight'], + ]) { + test(`.${cls} sets a negative ${prop}`, async ({ page }) => { + await setup(page, `
x
`); + const value = await page.locator('#t').evaluate((el, p) => parseFloat(getComputedStyle(el)[p]), prop); + expect(value).toBe(-20); + }); + } + + test('.sf-overlap-host reserves block-start padding matching the pull by default', async ({ page }) => { + await setup(page, `
x
`); + const cs = await page.locator('#t').evaluate(el => ({ + position: getComputedStyle(el).position, + isolation: getComputedStyle(el).isolation, + padTop: getComputedStyle(el).paddingTop, + })); + expect(cs.position).toBe('relative'); + expect(cs.isolation).toBe('isolate'); + expect(cs.padTop).toBe('24px'); + }); +}); + +test.describe('macro: .sf-scrim composed with .sf-bg (media background + overlay)', () => { + test('img.sf-bg auto-fills the parent behind the scrim gradient and content', async ({ page }) => { + await setup(page, ` +
+ +

Headline

+
+ `); + const res = await page.evaluate(() => { + const host = document.getElementById('host'); + const media = document.getElementById('media'); + const content = document.getElementById('content'); + const hostBox = host.getBoundingClientRect(); + const mediaBox = media.getBoundingClientRect(); + return { + mediaPosition: getComputedStyle(media).position, + mediaFillsHost: Math.abs(mediaBox.width - hostBox.width) < 1 && Math.abs(mediaBox.height - hostBox.height) < 1, + // Content must hit-test above the media + scrim gradient, with no manual z-index set on content. + topElementIsContent: document.elementFromPoint( + content.getBoundingClientRect().left + 2, + content.getBoundingClientRect().top + 2 + ) === content, + contentZIndex: getComputedStyle(content).zIndex, + }; + }); + expect(res.mediaPosition).toBe('absolute'); + expect(res.mediaFillsHost).toBe(true); + expect(res.topElementIsContent).toBe(true); + // .sf-scrim's lift selector supplies the z-index — the author never sets one. + expect(Number(res.contentZIndex)).toBeGreaterThan(0); + }); + + test('plain .sf-scrim with an in-flow img is unaffected (no regression)', async ({ page }) => { + await setup(page, ` +
+ +
+ `); + const position = await page.locator('#t img').evaluate(el => getComputedStyle(el).position); + expect(position).toBe('static'); + }); +}); diff --git a/tests/parse-lib.test.js b/tests/parse-lib.test.js index 12acbec5..e147c4b6 100644 --- a/tests/parse-lib.test.js +++ b/tests/parse-lib.test.js @@ -17,6 +17,7 @@ import { stripComments, stripStrings, maskComments, maskStrings, readValue, readFile, requireFile, } from '../scripts/lib/parse.js'; +import { collectComments, describe as describeElement } from '../scripts/lib/api-index/extract.js'; describe('stripComments vs maskComments', () => { const css = '.a { color: red; } /* comment */ .b { color: blue; }'; @@ -113,3 +114,32 @@ describe('readFile vs requireFile — missing-file behavior', () => { ); }); }); + +describe('collectComments / describe — directive comments never become descriptions', () => { + // Reproduces the .sf-corner-scoop--* leak: a stylelint-disable comment sits + // right above a vendor-prefixed declaration inside the base rule, with no + // comment of their own governing the variant selectors that follow. + const css = ` + /* Concave corner — a corner that curves away from the box. */ + .sf-corner-scoop { + /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 18 */ + -webkit-mask-image: radial-gradient(circle, transparent 1rem, black 1rem); + } + .sf-corner-scoop--top-left { --sf-corner-scoop-at: 0 0; } + `; + + test('a stylelint-disable comment is classified as a directive, not a note', () => { + const comments = collectComments(css); + const directive = comments.find(c => c.raw.includes('stylelint-disable')); + assert.ok(directive, 'expected to find the stylelint-disable comment'); + assert.equal(directive.isDirective, true); + }); + + test('a variant selector with no comment of its own falls back to the governing note, not the directive', () => { + const comments = collectComments(css); + const idx = css.indexOf('.sf-corner-scoop--top-left'); + const { description } = describeElement(comments, idx); + assert.ok(!description.includes('stylelint-disable')); + assert.match(description, /concave corner/i); + }); +}); diff --git a/tests/token-api.snapshot.json b/tests/token-api.snapshot.json index e44c1dd7..4ada5b63 100644 --- a/tests/token-api.snapshot.json +++ b/tests/token-api.snapshot.json @@ -324,6 +324,8 @@ "--sf-content-width", "--sf-contrast-bias", "--sf-contrast-threshold", + "--sf-corner-scoop-at", + "--sf-corner-scoop-size", "--sf-cover-min-height", "--sf-cover-padding", "--sf-current-font-weight", @@ -479,6 +481,7 @@ "--sf-opacity-disabled", "--sf-opacity-muted", "--sf-optical-sizing", + "--sf-overlap-pull", "--sf-palette-mix-100", "--sf-palette-mix-200", "--sf-palette-mix-300", diff --git a/token-registry.json b/token-registry.json index 00502e7c..b9393dfa 100644 --- a/token-registry.json +++ b/token-registry.json @@ -1,7 +1,7 @@ { "_meta": { "generatedBy": "scripts/gen-token-registry.js", - "nextId": 710 + "nextId": 717 }, "tokens": [ { @@ -2869,6 +2869,38 @@ "id": 709, "name": "--sf-boxed-shadow", "removed": true + }, + { + "id": 710, + "name": "--sf-corner-scoop-at" + }, + { + "id": 711, + "name": "--sf-corner-scoop-size" + }, + { + "id": 712, + "name": "--sf-corners-end-end", + "removed": true + }, + { + "id": 713, + "name": "--sf-corners-end-start", + "removed": true + }, + { + "id": 714, + "name": "--sf-corners-start-end", + "removed": true + }, + { + "id": 715, + "name": "--sf-corners-start-start", + "removed": true + }, + { + "id": 716, + "name": "--sf-overlap-pull" } ] }