diff --git a/SLASHED-for-WP/admin-app/.vendored-manifest.json b/SLASHED-for-WP/admin-app/.vendored-manifest.json index 01834e0a..09516d77 100644 --- a/SLASHED-for-WP/admin-app/.vendored-manifest.json +++ b/SLASHED-for-WP/admin-app/.vendored-manifest.json @@ -3,209 +3,217 @@ "description": "Files vendored from the SLASHED framework configurator. DO NOT edit directly — make changes in codeslash-dev/SLASHED → configurator/src/ and re-run npm run sync.", "frameworkRepo": "https://github.com/codeslash-dev/SLASHED", "configuratorSrc": "configurator/src/", - "source": "github:codeslash-dev/slashed@main", - "syncedAt": "2026-07-03T18:27:12.207Z" + "source": "local", + "syncedAt": "2026-07-07T13:45:03.410Z" }, "vendoredFiles": [ { "file": "src/app.css", - "source": "github:codeslash-dev/slashed/configurator/src/app.css@main" + "source": "local:configurator/src/app.css" }, { "file": "src/App.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/App.svelte@main" + "source": "local:configurator/src/App.svelte" }, { "file": "src/components/CommandPalette.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/CommandPalette.svelte@main" + "source": "local:configurator/src/components/CommandPalette.svelte" }, { "file": "src/components/DomainPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/DomainPanel.svelte@main" + "source": "local:configurator/src/components/DomainPanel.svelte" }, { "file": "src/components/inputs/ClampField.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/ClampField.svelte@main" + "source": "local:configurator/src/components/inputs/ClampField.svelte" }, { "file": "src/components/inputs/ColorInput.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/ColorInput.svelte@main" + "source": "local:configurator/src/components/inputs/ColorInput.svelte" }, { "file": "src/components/inputs/OklchColorDesk.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/OklchColorDesk.svelte@main" + "source": "local:configurator/src/components/inputs/OklchColorDesk.svelte" }, { "file": "src/components/inputs/PowerKnobRow.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/PowerKnobRow.svelte@main" + "source": "local:configurator/src/components/inputs/PowerKnobRow.svelte" }, { "file": "src/components/inputs/RangeWithNumber.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/RangeWithNumber.svelte@main" + "source": "local:configurator/src/components/inputs/RangeWithNumber.svelte" }, { "file": "src/components/inputs/SliderRow.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/SliderRow.svelte@main" + "source": "local:configurator/src/components/inputs/SliderRow.svelte" }, { "file": "src/components/inputs/TokenRow.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/inputs/TokenRow.svelte@main" + "source": "local:configurator/src/components/inputs/TokenRow.svelte" }, { "file": "src/components/panels/AllTokensTab.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/AllTokensTab.svelte@main" + "source": "local:configurator/src/components/panels/AllTokensTab.svelte" }, { "file": "src/components/panels/BordersPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/BordersPanel.svelte@main" + "source": "local:configurator/src/components/panels/BordersPanel.svelte" }, { "file": "src/components/panels/CheatsheetPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/CheatsheetPanel.svelte@main" + "source": "local:configurator/src/components/panels/CheatsheetPanel.svelte" }, { "file": "src/components/panels/ColorsPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/ColorsPanel.svelte@main" + "source": "local:configurator/src/components/panels/ColorsPanel.svelte" + }, + { + "file": "src/components/panels/ComponentsPanel.svelte", + "source": "local:configurator/src/components/panels/ComponentsPanel.svelte" }, { "file": "src/components/panels/EffectsPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/EffectsPanel.svelte@main" + "source": "local:configurator/src/components/panels/EffectsPanel.svelte" }, { "file": "src/components/panels/ExportPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/ExportPanel.svelte@main" + "source": "local:configurator/src/components/panels/ExportPanel.svelte" }, { "file": "src/components/panels/GenericTokenPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/GenericTokenPanel.svelte@main" + "source": "local:configurator/src/components/panels/GenericTokenPanel.svelte" }, { "file": "src/components/panels/HomePanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/HomePanel.svelte@main" + "source": "local:configurator/src/components/panels/HomePanel.svelte" }, { "file": "src/components/panels/LayoutPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/LayoutPanel.svelte@main" + "source": "local:configurator/src/components/panels/LayoutPanel.svelte" }, { "file": "src/components/panels/MacrosPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/MacrosPanel.svelte@main" + "source": "local:configurator/src/components/panels/MacrosPanel.svelte" }, { "file": "src/components/panels/MiscPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/MiscPanel.svelte@main" + "source": "local:configurator/src/components/panels/MiscPanel.svelte" }, { "file": "src/components/panels/MotionPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/MotionPanel.svelte@main" + "source": "local:configurator/src/components/panels/MotionPanel.svelte" }, { "file": "src/components/panels/ShadowsPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/ShadowsPanel.svelte@main" + "source": "local:configurator/src/components/panels/ShadowsPanel.svelte" }, { "file": "src/components/panels/SpacingPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/SpacingPanel.svelte@main" + "source": "local:configurator/src/components/panels/SpacingPanel.svelte" }, { "file": "src/components/panels/ThemesPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/ThemesPanel.svelte@main" + "source": "local:configurator/src/components/panels/ThemesPanel.svelte" }, { "file": "src/components/panels/TypographyPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/TypographyPanel.svelte@main" + "source": "local:configurator/src/components/panels/TypographyPanel.svelte" }, { "file": "src/components/panels/WcagPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/panels/WcagPanel.svelte@main" + "source": "local:configurator/src/components/panels/WcagPanel.svelte" }, { "file": "src/components/shell/PreviewPanel.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/shell/PreviewPanel.svelte@main" + "source": "local:configurator/src/components/shell/PreviewPanel.svelte" }, { "file": "src/components/shell/SidebarNav.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/shell/SidebarNav.svelte@main" + "source": "local:configurator/src/components/shell/SidebarNav.svelte" }, { "file": "src/components/shell/StatusBar.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/shell/StatusBar.svelte@main" + "source": "local:configurator/src/components/shell/StatusBar.svelte" }, { "file": "src/components/shell/StudioHeader.svelte", - "source": "github:codeslash-dev/slashed/configurator/src/components/shell/StudioHeader.svelte@main" + "source": "local:configurator/src/components/shell/StudioHeader.svelte" }, { "file": "src/data/api-index.generated.json", - "source": "github:codeslash-dev/slashed/configurator/src/data/api-index.generated.json@main" + "source": "local:configurator/src/data/api-index.generated.json" }, { "file": "src/data/bundles.generated.json", - "source": "github:codeslash-dev/slashed/configurator/src/data/bundles.generated.json@main" + "source": "local:configurator/src/data/bundles.generated.json" }, { "file": "src/data/classes.generated.json", - "source": "github:codeslash-dev/slashed/configurator/src/data/classes.generated.json@main" + "source": "local:configurator/src/data/classes.generated.json" }, { "file": "src/data/domain-patterns.json", - "source": "github:codeslash-dev/slashed/configurator/src/data/domain-patterns.json@main" + "source": "local:configurator/src/data/domain-patterns.json" }, { "file": "src/data/token-registry.generated.json", - "source": "github:codeslash-dev/slashed/configurator/src/data/token-registry.generated.json@main" + "source": "local:configurator/src/data/token-registry.generated.json" }, { "file": "src/lib/basics.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/basics.ts@main" + "source": "local:configurator/src/lib/basics.ts" }, { "file": "src/lib/codec.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/codec.ts@main" + "source": "local:configurator/src/lib/codec.ts" }, { "file": "src/lib/colorUtils.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/colorUtils.ts@main" + "source": "local:configurator/src/lib/colorUtils.ts" }, { "file": "src/lib/domains.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/domains.ts@main" + "source": "local:configurator/src/lib/domains.ts" }, { "file": "src/lib/lumlockerPreview.svelte.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/lumlockerPreview.svelte.ts@main" + "source": "local:configurator/src/lib/lumlockerPreview.svelte.ts" }, { "file": "src/lib/persistence.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/persistence.ts@main" + "source": "local:configurator/src/lib/persistence.ts" }, { "file": "src/lib/powerKnobs.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/powerKnobs.ts@main" + "source": "local:configurator/src/lib/powerKnobs.ts" }, { "file": "src/lib/previewResolver.svelte.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/previewResolver.svelte.ts@main" + "source": "local:configurator/src/lib/previewResolver.svelte.ts" }, { "file": "src/lib/savedThemes.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/savedThemes.ts@main" + "source": "local:configurator/src/lib/savedThemes.ts" }, { "file": "src/lib/theme.svelte.ts", - "source": "github:codeslash-dev/slashed/configurator/src/lib/theme.svelte.ts@main" + "source": "local:configurator/src/lib/theme.svelte.ts" + }, + { + "file": "src/lib/variableScales.ts", + "source": "local:configurator/src/lib/variableScales.ts" }, { "file": "src/main.ts", - "source": "github:codeslash-dev/slashed/configurator/src/main.ts@main" + "source": "local:configurator/src/main.ts" }, { "file": "src/types.ts", - "source": "github:codeslash-dev/slashed/configurator/src/types.ts@main" + "source": "local:configurator/src/types.ts" }, { "file": "src/vite-env.d.ts", - "source": "github:codeslash-dev/slashed/configurator/src/vite-env.d.ts@main" + "source": "local:configurator/src/vite-env.d.ts" } ] } diff --git a/SLASHED-for-WP/admin-app/framework-css/core/layout.css b/SLASHED-for-WP/admin-app/framework-css/core/layout.css index 64a310a1..6d6e1f9f 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/layout.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/layout.css @@ -23,6 +23,17 @@ .sf-section-group > .sf-section + .sf-section { padding-block-start: 0; } + /* Header — block padding on the standard spacing scale. Headers usually + want lighter vertical rhythm than .sf-section, so these map to + --sf-space-* directly. Only block padding is set; any inline gutter + (from .sf-section--guttered, a container, or the element itself) is + left intact. */ + .sf-header--xs { padding-block: var(--sf-space-xs); } + .sf-header--s { padding-block: var(--sf-space-s); } + .sf-header--m { padding-block: var(--sf-space-m); } + .sf-header--l { padding-block: var(--sf-space-l); } + .sf-header--xl { padding-block: var(--sf-space-xl); } + /* Collapse modifier */ .sf-section--collapse:has(+ .sf-section--collapse) { padding-block-end: calc(var(--sf-section-pad) / 2); @@ -55,7 +66,18 @@ background: linear-gradient(to right, transparent, var(--sf-divider-color), transparent); } - + /* Divide — rules between direct children, reusing .sf-divider's tokens. + Unlike .sf-divider (a standalone separator element), .sf-divide is + applied to the container and needs no extra markup per gap. */ + .sf-divide > * + * { + border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color); + } + .sf-divide--vertical > * + * { + border-block-start: none; + border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color); + } + + /* Container */ .sf-container { container: sf-layout / inline-size; @@ -75,7 +97,62 @@ container-type: inline-size; } + /* Overlay — absolute-fill layout helper for overlays/backdrops. */ + .sf-overlay { + position: absolute; + inset: 0; + } + + /* .sf-fluid-cq: opt a subtree into a CONTAINER-relative fluid scale (issue + #497). By default --sf-text-* / --sf-space-* interpolate against 100vw (the + viewport); inside .sf-fluid-cq they interpolate against 100cqi (this box's + inline size), so type + spacing track the container the way the layout + primitives (.sf-grid-cols-*, .sf-content-grid, .sf-bento) already do. Use + on nested / narrow contexts (sidebars, cards, 1/3 columns) where a + viewport-driven scale would size text as if it spanned the full page. + + Why the tokens are RE-DECLARED (not just --sf-fluid-width flipped): the + --sf-text-* / --sf-space-* tokens are computed at :root, so their + var(--sf-fluid-width) is substituted there (100vw) and the *resolved* + length inherits down — overriding --sf-fluid-width lower in the tree can't + retroactively rewrite an already-computed inherited value. The scale is + therefore re-declared in-scope so var(--sf-fluid-width) re-resolves to + 100cqi. It is re-declared on the direct children, not the element itself: + a query container's own cq units resolve against its ANCESTOR container, so + the re-computation must happen one level in — children compute against + .sf-fluid-cq and deeper descendants inherit that resolved value. + + The formulas below are character-identical to their :root twins in + core/tokens.css (both read var(--sf-fluid-width)); keep the two copies in + sync if the generative formula ever changes (cf. themes.css SL-001). */ + .sf-fluid-cq { container-type: inline-size; } + .sf-fluid-cq > * { + --sf-fluid-width: 100cqi; + --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); + --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); + --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); + --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); + --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); + --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); + --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); + --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); + --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); + --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); + --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); + --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); + --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); + --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); + --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); + --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); + --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); + } + + /* Stack & gap */ .sf-stack { display: flex; @@ -215,6 +292,32 @@ .sf-grid--dense { grid-auto-flow: dense; } + /* Grid-flex — flexbox alternative to .sf-grid for item counts that + don't divide evenly: leftover last-row items stretch to fill the + row by default, or stay fixed-width and centered with --center. + CSS Grid keeps orphans pinned to their track; flex does not. */ + .sf-grid-flex { + display: flex; + flex-wrap: wrap; + gap: var(--sf-grid-gap); + } + .sf-grid-flex > * { + flex: 1 1 min(var(--sf-grid-min), 100%); + } + + .sf-grid-flex--center { justify-content: center; } + .sf-grid-flex--center > * { + flex-grow: 0; + flex-basis: min(var(--sf-grid-min), 100%); + } + + .sf-grid-flex--xs { --sf-grid-min: var(--sf-grid-min-xs); } + .sf-grid-flex--s { --sf-grid-min: var(--sf-grid-min-s); } + .sf-grid-flex--m { --sf-grid-min: var(--sf-grid-min-m); } + .sf-grid-flex--l { --sf-grid-min: var(--sf-grid-min-l); } + .sf-grid-flex--xl { --sf-grid-min: var(--sf-grid-min-xl); } + .sf-grid-flex--2xl { --sf-grid-min: var(--sf-grid-min-2xl); } + .sf-icon { display: inline-block; inline-size: var(--sf-icon-size, var(--sf-icon-m)); @@ -287,41 +390,41 @@ /* Background layer — absolutely-positioned cover media that sits behind a parent's content. The parent is auto-promoted to a positioned, isolated - stacking context via :has(), so .sf-bg composes natively under .sf-scrim - (background → scrim gradient → content). Works as a bare + stacking context via :has(), so .sf-bg-layer composes natively under .sf-scrim + (background → scrim gradient → content). Works as a bare or as a wrapper with a nested > img / > video. */ - :where(:has(> .sf-bg)) { + :where(:has(> .sf-bg-layer)) { position: relative; isolation: isolate; } - .sf-bg { + .sf-bg-layer { position: absolute; - inset: var(--sf-bg-inset); - inline-size: calc(100% - 2 * var(--sf-bg-inset)); - block-size: calc(100% - 2 * var(--sf-bg-inset)); - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); - z-index: var(--sf-bg-z); + inset: var(--sf-bg-layer-inset); + inline-size: calc(100% - 2 * var(--sf-bg-layer-inset)); + block-size: calc(100% - 2 * var(--sf-bg-layer-inset)); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); + z-index: var(--sf-bg-layer-z); pointer-events: none; } - .sf-bg > :is(img, video, picture) { + .sf-bg-layer > :is(img, video, picture) { inline-size: 100%; block-size: 100%; - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); pointer-events: none; } - .sf-bg > picture > img { + .sf-bg-layer > picture > img { inline-size: 100%; block-size: 100%; - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); pointer-events: none; } @@ -468,7 +571,6 @@ .sf-content-grid > .sf-breakout { grid-column: breakout; } .sf-content-grid > .sf-full-bleed { grid-column: full; } - .sf-bento { display: grid; grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr); diff --git a/SLASHED-for-WP/admin-app/framework-css/core/macros.css b/SLASHED-for-WP/admin-app/framework-css/core/macros.css index ede34be7..d574b189 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/macros.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/macros.css @@ -310,6 +310,22 @@ .sf-scrim--bottom { --sf-scrim-direction: to top; } .sf-scrim--full { --sf-scrim-gradient: var(--sf-scrim-color); } + /* Named background surface preset (issue #523). Composes the + --sf-surface-bg-* token set (see tokens.macros.css) into one reusable, + named background: base colour + image/gradient/pattern, sizing, an + optional overlay layered above the image, and an optional animation. + Inert until a preset feeds the tokens. The overlay is the first + background-image layer so it paints ABOVE the image. */ + .sf-surface-bg { + background-color: var(--sf-surface-bg-color); + background-image: var(--sf-surface-bg-overlay), var(--sf-surface-bg-image); + background-size: var(--sf-surface-bg-size); + background-position: var(--sf-surface-bg-position); + background-repeat: var(--sf-surface-bg-repeat); + background-attachment: var(--sf-surface-bg-attachment); + animation: var(--sf-surface-bg-animation); + } + .sf-text-protect { text-shadow: var(--sf-scrim-text-shadow); } @@ -326,6 +342,14 @@ font-size: 0.85em; text-decoration: none; } + /* Alt-text form (accessible name for the glyph) — gated so a UA that + rejects the "value / string" grammar keeps the plain marker above + instead of dropping the whole declaration. */ + @supports (content: "x" / "y") { + .sf-link-external::after { + content: var(--sf-link-external-marker) / var(--sf-link-external-label); + } + } .sf-content-auto { content-visibility: auto; @@ -338,9 +362,11 @@ /* filter: drop-shadow() utilities — unlike box-shadow, follows the element's actual alpha shape (SVG icons, PNG cutouts, transparent logos). */ - .sf-drop-shadow-s { filter: var(--sf-drop-shadow-s); } - .sf-drop-shadow-m { filter: var(--sf-drop-shadow-m); } - .sf-drop-shadow-l { filter: var(--sf-drop-shadow-l); } + .sf-drop-shadow-xs { filter: var(--sf-drop-shadow-xs); } + .sf-drop-shadow-s { filter: var(--sf-drop-shadow-s); } + .sf-drop-shadow-m { filter: var(--sf-drop-shadow-m); } + .sf-drop-shadow-l { filter: var(--sf-drop-shadow-l); } + .sf-drop-shadow-xl { filter: var(--sf-drop-shadow-xl); } .sf-link--subtle:link, .sf-link--subtle:visited { @@ -361,4 +387,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/SLASHED-for-WP/admin-app/framework-css/core/themes.css b/SLASHED-for-WP/admin-app/framework-css/core/themes.css index 8f501eba..d64dd0b5 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/themes.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/themes.css @@ -1,7 +1,7 @@ /* SLASHED — core/themes.css @layer slashed.themes Light/dark activation, scoped theme color inheritance, LumLocker, and scheme-aware color adjustments. - Uses data-theme and token overrides. */ + Uses data-theme, .sf-theme-light/.sf-theme-dark aliases, and token overrides. */ @layer slashed.themes { @@ -12,10 +12,10 @@ } } - [data-theme="light"] { color-scheme: light; --sf-is-dark: 0; } - [data-theme="dark"] { color-scheme: dark; --sf-is-dark: 1; } + :is([data-theme="light"], .sf-theme-light) { color-scheme: light; --sf-is-dark: 0; } + :is([data-theme="dark"], .sf-theme-dark) { color-scheme: dark; --sf-is-dark: 1; } - :where([data-theme]:not(:root, html)) { + :where([data-theme]:not(:root, html), .sf-theme-light:not(:root, html), .sf-theme-dark:not(:root, html)) { background-color: var(--sf-color-bg); color: var(--sf-color-text); } @@ -42,13 +42,13 @@ ); } - :root[data-lumlocker] [data-theme="dark"] { + :root[data-lumlocker] :is([data-theme="dark"], .sf-theme-dark) { --sf-color-primary: oklch(from var(--sf-color-primary-source-dark, var(--sf-color-primary-source-light)) var(--sf-lumlocker) c h); --sf-color-secondary: oklch(from var(--sf-color-secondary-source-dark, var(--sf-color-secondary-source-light)) var(--sf-lumlocker) c h); --sf-color-tertiary: oklch(from var(--sf-color-tertiary-source-dark, var(--sf-color-tertiary-source-light)) var(--sf-lumlocker) c h); --sf-color-action: oklch(from var(--sf-color-action-source-dark, var(--sf-color-action-source-light)) var(--sf-lumlocker) c h); } - :root[data-lumlocker] [data-theme="light"] { + :root[data-lumlocker] :is([data-theme="light"], .sf-theme-light) { --sf-color-primary: oklch(from var(--sf-color-primary-source-light) var(--sf-lumlocker) c h); --sf-color-secondary: oklch(from var(--sf-color-secondary-source-light) var(--sf-lumlocker) c h); --sf-color-tertiary: oklch(from var(--sf-color-tertiary-source-light) var(--sf-lumlocker) c h); @@ -79,7 +79,7 @@ --sf-color-danger-source-dark: oklch(from var(--sf-color-danger-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h); } - [data-theme="dark"] { + :is([data-theme="dark"], .sf-theme-dark) { --sf-color-primary: var(--sf-color-primary-source-dark); --sf-color-secondary: var(--sf-color-secondary-source-dark); @@ -122,9 +122,22 @@ --sf-color-warning-strong: oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h); --sf-color-info-strong: oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h); --sf-color-danger-strong: oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h); + + /* :root-only aliases from tokens.css — var() resolves on the declaring + element, so without these re-declarations a nested [data-theme] + section inherits the :root-resolved (wrong-scheme) values (#496). */ + --sf-color-code-bg: var(--sf-color-inset); + --sf-color-bg--disabled: var(--sf-color-inset); + --sf-color-link--disabled: var(--sf-color-text--disabled); + --sf-scrollbar-thumb: var(--sf-color-neutral); + /* Non-sign() fallback: keeps inline code readable in engines with + oklch(from) but not sign() — the sign()-gated auto-contrast block + below overrides this with a proper per-background contrast calc + wherever sign() is supported. */ + --sf-color-code-text: var(--sf-color-text); } - [data-theme="light"] { + :is([data-theme="light"], .sf-theme-light) { --sf-color-primary: var(--sf-color-primary-source-light); --sf-color-secondary: var(--sf-color-secondary-source-light); @@ -167,12 +180,20 @@ --sf-color-warning-strong: oklch(from var(--sf-color-warning-source-light) calc(l - 0.25) c h); --sf-color-info-strong: oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h); --sf-color-danger-strong: oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h); + + /* :root-only aliases — see the matching note in the dark block (#496). */ + --sf-color-code-bg: var(--sf-color-inset); + --sf-color-bg--disabled: var(--sf-color-inset); + --sf-color-link--disabled: var(--sf-color-text--disabled); + --sf-scrollbar-thumb: var(--sf-color-neutral); + /* Non-sign() fallback — see matching note in the dark block (#496). */ + --sf-color-code-text: var(--sf-color-text); } } @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))) { - [data-theme="dark"] { + :is([data-theme="dark"], .sf-theme-dark) { --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); @@ -183,8 +204,12 @@ --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-inverse: oklch(from var(--sf-color-inverse) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); + /* Auto-contrast code text re-derived in-scope so it tracks the + per-scope --sf-color-code-bg above (#496). Same formula as + core/tokens.css — keep in sync. */ + --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); } - [data-theme="light"] { + :is([data-theme="light"], .sf-theme-light) { --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); @@ -195,6 +220,8 @@ --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-inverse: oklch(from var(--sf-color-inverse) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); + /* See matching note in the dark block (#496). */ + --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); } } diff --git a/SLASHED-for-WP/admin-app/framework-css/core/tokens.css b/SLASHED-for-WP/admin-app/framework-css/core/tokens.css index 35613a14..bcea0a84 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/tokens.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/tokens.css @@ -114,7 +114,7 @@ /* @property — INTERACTION STATE FLAGS */ /* PUBLIC hooks for Style Queries — safe to set from consumer code, unlike --sf-is-dark above. Allow components to react to states toggled on - ancestors via .is-* classes. */ + ancestors via .sf-is-* classes. */ @property --sf-is-active { syntax: ""; inherits: true; initial-value: 0; } @property --sf-is-current { syntax: ""; inherits: true; initial-value: 0; } @property --sf-is-pressed { syntax: ""; inherits: true; initial-value: 0; } @@ -136,6 +136,13 @@ --sf-text-display-base-min / -max --sf-text-display-s endpoints. --sf-space-ratio-min / -max modular ratio of the SPACE scale. --sf-space-base-min / -max --sf-space-m endpoints (rem, unitless). + --sf-fluid-width the length every fluid clamp() + interpolates against. Defaults to + 100vw (viewport-relative). Override + to 100cqi on a container scope to make + the type + space + display scales + container-relative — see .sf-fluid-cq + in core/layout.css (issue #497). Registered as so they can be used as pow() exponents/operands and multiplied by 1rem in calc(). Requires CSS pow() (Chrome 125+, @@ -156,6 +163,15 @@ @property --sf-space-base-min { syntax: ""; inherits: true; initial-value: 1; } @property --sf-space-base-max { syntax: ""; inherits: true; initial-value: 2; } + /* Fluid interpolation driver — the length term every fluid clamp() below + measures against. Universal syntax ("*") not "": a registered + requires a computationally-independent initial-value, and 100vw + (viewport-dependent) is not one. The real default lives on :root; an + opt-in scope (e.g. .sf-fluid-cq) overrides it to 100cqi so the whole + generative scale becomes container-relative without duplicating a single + formula. */ + @property --sf-fluid-width { syntax: "*"; inherits: true; } + /* ── Output token @property registrations ────────────────────────────────── Radius + space: syntax enables CSS transitions on layout values — border-radius/padding/gap etc. now interpolate instead of snapping. @@ -175,6 +191,7 @@ @property --sf-radius-full { syntax: ""; inherits: true; initial-value: 9999px; } @property --sf-radius-pill { syntax: ""; inherits: true; initial-value: 9999px; } @property --sf-radius-outer { syntax: ""; inherits: true; initial-value: 0; } + @property --sf-media-radius { syntax: ""; inherits: true; initial-value: 0; } @property --sf-space-none { syntax: ""; inherits: true; initial-value: 0; } @property --sf-space-px { syntax: ""; inherits: true; initial-value: 1px; } @@ -208,17 +225,22 @@ @property --sf-shadow-2xl { syntax: "*"; inherits: true; } @property --sf-shadow-inner { syntax: "*"; inherits: true; } @property --sf-text-shadow-none { syntax: "*"; inherits: true; } + @property --sf-text-shadow-xs { syntax: "*"; inherits: true; } @property --sf-text-shadow-s { syntax: "*"; inherits: true; } @property --sf-text-shadow-m { syntax: "*"; inherits: true; } @property --sf-text-shadow-l { syntax: "*"; inherits: true; } + @property --sf-text-shadow-xl { syntax: "*"; inherits: true; } + @property --sf-drop-shadow-xs { syntax: "*"; inherits: true; } @property --sf-drop-shadow-s { syntax: "*"; inherits: true; } @property --sf-drop-shadow-m { syntax: "*"; inherits: true; } @property --sf-drop-shadow-l { syntax: "*"; inherits: true; } + @property --sf-drop-shadow-xl { syntax: "*"; inherits: true; } @property --sf-shadow-glow { syntax: "*"; inherits: true; } :root { --sf-fluid-min-vw: 22.5; --sf-fluid-max-vw: 90; + --sf-fluid-width: 100vw; /* interpolation driver; 100cqi on a container scope → container-relative fluid (#497) */ --sf-text-ratio-min: 1.25; --sf-text-ratio-max: 1.333; --sf-text-base-min: 1; @@ -491,6 +513,17 @@ oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55) ); + /* Alt selection — the opposite scheme's treatment, for a surface + that deliberately inverts colours relative to the page (e.g. a + dark card on a light page) where the default pairing above + would land with poor contrast. Consumed by .sf-selection--alt + (optional/utilities.css). */ + --sf-color-selection-bg--alt: light-dark( + oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55), + oklch(from var(--sf-color-action-source-light) l c h / 0.28) + ); + --sf-color-selection-text--alt: inherit; + /* ---------------------------------------------------------- Status triplets — strong (direction-dependent, uses light-dark) danger = destructive actions, form validation errors, negative states @@ -957,7 +990,7 @@ --sf-font-weight-interactive: var(--sf-font-weight-semibold); /* buttons, badges, nav */ --sf-font-weight-strong: var(--sf-font-weight-bold); /* / emphasis */ - /* State token — consumed by .is-current in states.css. + /* State token — consumed by .sf-is-current in states.css. Override to match your nav design (e.g. semibold for thin fonts). */ --sf-current-font-weight: var(--sf-font-weight-bold); @@ -971,20 +1004,20 @@ to --sf-text-m (N=0): 2xs=-3 … 4xl=5. Override the inputs above to reshape the whole scale; override an individual --sf-text-* to pin one. ---------------------------------------------------------- */ - --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); - --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); - --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); - --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); - --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); - --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); - --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); - --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); - --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); + --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); + --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); + --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); + --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); + --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); + --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); + --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); /* Display — reuses the type ratios with its own larger base (s=0, m=1, l=2). */ - --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); - --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); - --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); /* ---------------------------------------------------------- Line heights & letter spacing @@ -1109,15 +1142,15 @@ /* Spacing — fluid (generative). Same engine as type; space uses base_max=2 (more dramatic growth than type's 1.25). Step exponent N relative to --sf-space-m (N=0): 2xs=-3 … 4xl=5. */ - --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); - --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); - --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); - --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); - --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); - --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); - --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); - --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); - --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); + --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); + --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); + --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); + --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); + --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); + --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); + --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); /* ---------------------------------------------------------- UI sizes @@ -1234,6 +1267,13 @@ Maths: outer = inner + padding ⇒ aligned curves at corners. */ --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad)); + /* Opt-in global media radius — 0 by default (off). Set to a + --sf-radius-* value to round /
globally: + :root { --sf-media-radius: var(--sf-radius-m); } + Applied via :where() in core/base.css so it never fights + .sf-bg-layer or a component's own radius. */ + --sf-media-radius: 0; + /* ---------------------------------------------------------- Shadows — BEM consumer API Use in your own classes: .card { box-shadow: var(--sf-shadow-m); } @@ -1286,6 +1326,10 @@ --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both; --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both; --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-up: sf-slide-out-up var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-down: sf-slide-out-down var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-left: sf-slide-out-left var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-right: sf-slide-out-right var(--sf-duration-normal) var(--sf-ease-in) both; --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite; --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite; --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite; @@ -1310,6 +1354,11 @@ --sf-scroll-timeline-range-start: entry 0%; --sf-scroll-timeline-range-end: cover 30%; + /* Exit counterpart — element is well into view before starting to + leave, finishing exactly as it scrolls fully out. */ + --sf-scroll-timeline-range-exit-start: cover 70%; + --sf-scroll-timeline-range-exit-end: exit 100%; + /* ---------------------------------------------------------- Mask scrim — edge-fade stops for scroll reels / overflow. Use in a mask-image gradient to fade content near an edge: @@ -1454,14 +1503,18 @@ Text shadows — share --sf-shadow-color / --sf-shadow-strength ---------------------------------------------------------- */ --sf-text-shadow-none: none; - --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)); - --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)); - --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)); - + --sf-text-shadow-xs: 0 0.5px 1px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1), 0.7)); + --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)); + --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)); + --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)); + --sf-text-shadow-xl: 0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)); + /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */ - --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7))); - --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7))); - --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7))); + --sf-drop-shadow-xs: drop-shadow(0 0.5px 1px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1), 0.7))); + --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7))); + --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7))); + --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7))); + --sf-drop-shadow-xl: drop-shadow(0 16px 32px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7))); } } @@ -1550,6 +1603,11 @@ link text. Override with your own glyph (include leading space if you want one): `:root { --sf-link-external-marker: " →" }`. */ --sf-link-external-marker: " \2197"; /* " ↗" — consumed by .sf-link-external */ + /* Accessible name for the marker glyph, via the CSS alt-text syntax + (`content: / `) — read by screen readers in place + of the glyph, appended after the link's own text. Localise or + disable (together with the marker) per-site. */ + --sf-link-external-label: "opens in a new window or external site"; /* ---------------------------------------------------------- Section padding diff --git a/SLASHED-for-WP/admin-app/framework-css/core/tokens.layout.css b/SLASHED-for-WP/admin-app/framework-css/core/tokens.layout.css index d81c8347..dda29819 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/tokens.layout.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/tokens.layout.css @@ -84,15 +84,20 @@ --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */ /* ---------------------------------------------------------- - Background layer (.sf-bg) — cover media behind sibling content. + Background layer (.sf-bg-layer) — cover media behind sibling content. 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; - --sf-bg-fit: cover; - --sf-bg-position: 50% 50%; - --sf-bg-radius: 0; - --sf-bg-z: -2; /* behind content; above the parent background */ + /* 0px, not bare 0 — .sf-bg-layer'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-layer to its intrinsic + size. */ + --sf-bg-layer-inset: 0px; + --sf-bg-layer-fit: cover; + --sf-bg-layer-position: 50% 50%; + --sf-bg-layer-radius: 0; + --sf-bg-layer-z: -2; /* behind content; above the parent background */ /* ---------------------------------------------------------- Icon (boxed variant) — extras for .sf-icon--boxed. diff --git a/SLASHED-for-WP/admin-app/framework-css/core/tokens.macros.css b/SLASHED-for-WP/admin-app/framework-css/core/tokens.macros.css index 29e25f10..22eb8a5c 100644 --- a/SLASHED-for-WP/admin-app/framework-css/core/tokens.macros.css +++ b/SLASHED-for-WP/admin-app/framework-css/core/tokens.macros.css @@ -94,5 +94,48 @@ shadow halo behind glyphs, consumed by .sf-text-protect. */ --sf-scrim-text-shadow: 0 1px 3px oklch(0 0 0 / 0.6); + /* ---------------------------------------------------------- + Named background surface preset — input set for the .sf-surface-bg + macro (issue #523). One reusable, named background bundling a base + colour fallback + image/gradient/pattern + sizing + an optional + overlay + an optional animation. Define a preset by setting these + on a scope, then apply .sf-surface-bg to compose them: + .hero-surface { + --sf-surface-bg-image: url("/hero.avif"); + --sf-surface-bg-overlay: var(--sf-scrim-gradient); // reuse the scrim + --sf-surface-bg-animation: sf-pan 40s linear infinite; + } +
+ -overlay layers ABOVE -image (first background-image layer). Builds on + the existing scrim + gradient tokens rather than new infra; for a + blurred backdrop compose .sf-scrim / a filter on top. Inert by default + (image/overlay/animation = none) so the bare class is a no-op until a + preset feeds it. ---------------------------------------------------- */ + --sf-surface-bg-color: transparent; + --sf-surface-bg-image: none; + --sf-surface-bg-overlay: none; + --sf-surface-bg-size: cover; + --sf-surface-bg-position: center; + --sf-surface-bg-repeat: no-repeat; + --sf-surface-bg-attachment: scroll; + --sf-surface-bg-animation: none; + + /* ---------------------------------------------------------- + Concave corner — radius and corner position for .sf-corner-scoop. + Override per element: style="--sf-corner-scoop-size: var(--sf-radius-3xl)" + (cut radius) or style="--sf-corner-scoop-at: 0 0" (which corner). + ---------------------------------------------------------- */ + --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/SLASHED-for-WP/admin-app/framework-css/badges/slashed.full.css b/SLASHED-for-WP/admin-app/framework-css/dist/slashed.full.css similarity index 78% rename from SLASHED-for-WP/admin-app/framework-css/badges/slashed.full.css rename to SLASHED-for-WP/admin-app/framework-css/dist/slashed.full.css index f98b2f27..bebf3c06 100644 --- a/SLASHED-for-WP/admin-app/framework-css/badges/slashed.full.css +++ b/SLASHED-for-WP/admin-app/framework-css/dist/slashed.full.css @@ -1,4 +1,4 @@ -/* SLASHED v0.6.34 — slashed.full.css */ +/*! SLASHED v0.7.1 — slashed.full.css | MIT License | github.com/codeslash-dev/SLASHED */ /* ─── core/layers.css ─── */ /* SLASHED — core/layers.css @@ -139,7 +139,7 @@ /* @property — INTERACTION STATE FLAGS */ /* PUBLIC hooks for Style Queries — safe to set from consumer code, unlike --sf-is-dark above. Allow components to react to states toggled on - ancestors via .is-* classes. */ + ancestors via .sf-is-* classes. */ @property --sf-is-active { syntax: ""; inherits: true; initial-value: 0; } @property --sf-is-current { syntax: ""; inherits: true; initial-value: 0; } @property --sf-is-pressed { syntax: ""; inherits: true; initial-value: 0; } @@ -161,6 +161,13 @@ --sf-text-display-base-min / -max --sf-text-display-s endpoints. --sf-space-ratio-min / -max modular ratio of the SPACE scale. --sf-space-base-min / -max --sf-space-m endpoints (rem, unitless). + --sf-fluid-width the length every fluid clamp() + interpolates against. Defaults to + 100vw (viewport-relative). Override + to 100cqi on a container scope to make + the type + space + display scales + container-relative — see .sf-fluid-cq + in core/layout.css (issue #497). Registered as so they can be used as pow() exponents/operands and multiplied by 1rem in calc(). Requires CSS pow() (Chrome 125+, @@ -181,6 +188,15 @@ @property --sf-space-base-min { syntax: ""; inherits: true; initial-value: 1; } @property --sf-space-base-max { syntax: ""; inherits: true; initial-value: 2; } + /* Fluid interpolation driver — the length term every fluid clamp() below + measures against. Universal syntax ("*") not "": a registered + requires a computationally-independent initial-value, and 100vw + (viewport-dependent) is not one. The real default lives on :root; an + opt-in scope (e.g. .sf-fluid-cq) overrides it to 100cqi so the whole + generative scale becomes container-relative without duplicating a single + formula. */ + @property --sf-fluid-width { syntax: "*"; inherits: true; } + /* ── Output token @property registrations ────────────────────────────────── Radius + space: syntax enables CSS transitions on layout values — border-radius/padding/gap etc. now interpolate instead of snapping. @@ -200,6 +216,7 @@ @property --sf-radius-full { syntax: ""; inherits: true; initial-value: 9999px; } @property --sf-radius-pill { syntax: ""; inherits: true; initial-value: 9999px; } @property --sf-radius-outer { syntax: ""; inherits: true; initial-value: 0; } + @property --sf-media-radius { syntax: ""; inherits: true; initial-value: 0; } @property --sf-space-none { syntax: ""; inherits: true; initial-value: 0; } @property --sf-space-px { syntax: ""; inherits: true; initial-value: 1px; } @@ -233,17 +250,22 @@ @property --sf-shadow-2xl { syntax: "*"; inherits: true; } @property --sf-shadow-inner { syntax: "*"; inherits: true; } @property --sf-text-shadow-none { syntax: "*"; inherits: true; } + @property --sf-text-shadow-xs { syntax: "*"; inherits: true; } @property --sf-text-shadow-s { syntax: "*"; inherits: true; } @property --sf-text-shadow-m { syntax: "*"; inherits: true; } @property --sf-text-shadow-l { syntax: "*"; inherits: true; } + @property --sf-text-shadow-xl { syntax: "*"; inherits: true; } + @property --sf-drop-shadow-xs { syntax: "*"; inherits: true; } @property --sf-drop-shadow-s { syntax: "*"; inherits: true; } @property --sf-drop-shadow-m { syntax: "*"; inherits: true; } @property --sf-drop-shadow-l { syntax: "*"; inherits: true; } + @property --sf-drop-shadow-xl { syntax: "*"; inherits: true; } @property --sf-shadow-glow { syntax: "*"; inherits: true; } :root { --sf-fluid-min-vw: 22.5; --sf-fluid-max-vw: 90; + --sf-fluid-width: 100vw; /* interpolation driver; 100cqi on a container scope → container-relative fluid (#497) */ --sf-text-ratio-min: 1.25; --sf-text-ratio-max: 1.333; --sf-text-base-min: 1; @@ -516,6 +538,17 @@ oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55) ); + /* Alt selection — the opposite scheme's treatment, for a surface + that deliberately inverts colours relative to the page (e.g. a + dark card on a light page) where the default pairing above + would land with poor contrast. Consumed by .sf-selection--alt + (optional/utilities.css). */ + --sf-color-selection-bg--alt: light-dark( + oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55), + oklch(from var(--sf-color-action-source-light) l c h / 0.28) + ); + --sf-color-selection-text--alt: inherit; + /* ---------------------------------------------------------- Status triplets — strong (direction-dependent, uses light-dark) danger = destructive actions, form validation errors, negative states @@ -982,7 +1015,7 @@ --sf-font-weight-interactive: var(--sf-font-weight-semibold); /* buttons, badges, nav */ --sf-font-weight-strong: var(--sf-font-weight-bold); /* / emphasis */ - /* State token — consumed by .is-current in states.css. + /* State token — consumed by .sf-is-current in states.css. Override to match your nav design (e.g. semibold for thin fonts). */ --sf-current-font-weight: var(--sf-font-weight-bold); @@ -996,20 +1029,20 @@ to --sf-text-m (N=0): 2xs=-3 … 4xl=5. Override the inputs above to reshape the whole scale; override an individual --sf-text-* to pin one. ---------------------------------------------------------- */ - --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); - --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); - --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); - --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); - --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); - --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); - --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); - --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); - --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); + --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); + --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); + --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); + --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); + --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); + --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); + --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); /* Display — reuses the type ratios with its own larger base (s=0, m=1, l=2). */ - --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); - --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); - --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); /* ---------------------------------------------------------- Line heights & letter spacing @@ -1134,15 +1167,15 @@ /* Spacing — fluid (generative). Same engine as type; space uses base_max=2 (more dramatic growth than type's 1.25). Step exponent N relative to --sf-space-m (N=0): 2xs=-3 … 4xl=5. */ - --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); - --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); - --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); - --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); - --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); - --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); - --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); - --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); - --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); + --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); + --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); + --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); + --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); + --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); + --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); + --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); /* ---------------------------------------------------------- UI sizes @@ -1259,6 +1292,13 @@ Maths: outer = inner + padding ⇒ aligned curves at corners. */ --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad)); + /* Opt-in global media radius — 0 by default (off). Set to a + --sf-radius-* value to round /
globally: + :root { --sf-media-radius: var(--sf-radius-m); } + Applied via :where() in core/base.css so it never fights + .sf-bg-layer or a component's own radius. */ + --sf-media-radius: 0; + /* ---------------------------------------------------------- Shadows — BEM consumer API Use in your own classes: .card { box-shadow: var(--sf-shadow-m); } @@ -1311,6 +1351,10 @@ --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both; --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both; --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-up: sf-slide-out-up var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-down: sf-slide-out-down var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-left: sf-slide-out-left var(--sf-duration-normal) var(--sf-ease-in) both; + --sf-animation-slide-out-right: sf-slide-out-right var(--sf-duration-normal) var(--sf-ease-in) both; --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite; --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite; --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite; @@ -1335,6 +1379,11 @@ --sf-scroll-timeline-range-start: entry 0%; --sf-scroll-timeline-range-end: cover 30%; + /* Exit counterpart — element is well into view before starting to + leave, finishing exactly as it scrolls fully out. */ + --sf-scroll-timeline-range-exit-start: cover 70%; + --sf-scroll-timeline-range-exit-end: exit 100%; + /* ---------------------------------------------------------- Mask scrim — edge-fade stops for scroll reels / overflow. Use in a mask-image gradient to fade content near an edge: @@ -1479,14 +1528,18 @@ Text shadows — share --sf-shadow-color / --sf-shadow-strength ---------------------------------------------------------- */ --sf-text-shadow-none: none; - --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)); - --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)); - --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)); - + --sf-text-shadow-xs: 0 0.5px 1px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1), 0.7)); + --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)); + --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)); + --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)); + --sf-text-shadow-xl: 0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)); + /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */ - --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7))); - --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7))); - --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7))); + --sf-drop-shadow-xs: drop-shadow(0 0.5px 1px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1), 0.7))); + --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7))); + --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7))); + --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7))); + --sf-drop-shadow-xl: drop-shadow(0 16px 32px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7))); } } @@ -1575,6 +1628,11 @@ link text. Override with your own glyph (include leading space if you want one): `:root { --sf-link-external-marker: " →" }`. */ --sf-link-external-marker: " \2197"; /* " ↗" — consumed by .sf-link-external */ + /* Accessible name for the marker glyph, via the CSS alt-text syntax + (`content: / `) — read by screen readers in place + of the glyph, appended after the link's own text. Localise or + disable (together with the marker) per-site. */ + --sf-link-external-label: "opens in a new window or external site"; /* ---------------------------------------------------------- Section padding @@ -1737,16 +1795,19 @@ --sf-grid-min-2xl: 28rem; /* ---------------------------------------------------------- - Equal columns (intrinsically responsive RAM) - min-col-N: minimum column width for the N-column preset. - Exact column count emerges from container ÷ min-col. + Equal columns — CSS multi-column flowing layout (not grid). + Content flows between columns like a newspaper: fills one + column top-to-bottom, continues at the top of the next. + Use for FAQ lists, prose, anything that should distribute + evenly without being pinned to discrete grid cells — reach + for .sf-grid/.sf-grid--fit instead when items need their own + fixed cell. ---------------------------------------------------------- */ - --sf-equal-min-col: 16rem; - --sf-equal-min-col-2: 28rem; - --sf-equal-min-col-3: 15rem; - --sf-equal-min-col-4: 16rem; - --sf-equal-min-col-6: 10rem; + --sf-equal-min-col: 16rem; --sf-equal-gap: var(--sf-gap); + --sf-equal-rule-width: 0; + --sf-equal-rule-style: solid; + --sf-equal-rule-color: var(--sf-color-border); /* ---------------------------------------------------------- Cover @@ -1760,15 +1821,20 @@ --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */ /* ---------------------------------------------------------- - Background layer (.sf-bg) — cover media behind sibling content. + Background layer (.sf-bg-layer) — cover media behind sibling content. 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; - --sf-bg-fit: cover; - --sf-bg-position: 50% 50%; - --sf-bg-radius: 0; - --sf-bg-z: -2; /* behind content; above the parent background */ + /* 0px, not bare 0 — .sf-bg-layer'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-layer to its intrinsic + size. */ + --sf-bg-layer-inset: 0px; + --sf-bg-layer-fit: cover; + --sf-bg-layer-position: 50% 50%; + --sf-bg-layer-radius: 0; + --sf-bg-layer-z: -2; /* behind content; above the parent background */ /* ---------------------------------------------------------- Icon (boxed variant) — extras for .sf-icon--boxed. @@ -1918,6 +1984,49 @@ shadow halo behind glyphs, consumed by .sf-text-protect. */ --sf-scrim-text-shadow: 0 1px 3px oklch(0 0 0 / 0.6); + /* ---------------------------------------------------------- + Named background surface preset — input set for the .sf-surface-bg + macro (issue #523). One reusable, named background bundling a base + colour fallback + image/gradient/pattern + sizing + an optional + overlay + an optional animation. Define a preset by setting these + on a scope, then apply .sf-surface-bg to compose them: + .hero-surface { + --sf-surface-bg-image: url("/hero.avif"); + --sf-surface-bg-overlay: var(--sf-scrim-gradient); // reuse the scrim + --sf-surface-bg-animation: sf-pan 40s linear infinite; + } +
+ -overlay layers ABOVE -image (first background-image layer). Builds on + the existing scrim + gradient tokens rather than new infra; for a + blurred backdrop compose .sf-scrim / a filter on top. Inert by default + (image/overlay/animation = none) so the bare class is a no-op until a + preset feeds it. ---------------------------------------------------- */ + --sf-surface-bg-color: transparent; + --sf-surface-bg-image: none; + --sf-surface-bg-overlay: none; + --sf-surface-bg-size: cover; + --sf-surface-bg-position: center; + --sf-surface-bg-repeat: no-repeat; + --sf-surface-bg-attachment: scroll; + --sf-surface-bg-animation: none; + + /* ---------------------------------------------------------- + Concave corner — radius and corner position for .sf-corner-scoop. + Override per element: style="--sf-corner-scoop-size: var(--sf-radius-3xl)" + (cut radius) or style="--sf-corner-scoop-at: 0 0" (which corner). + ---------------------------------------------------------- */ + --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); + } } @@ -2129,6 +2238,12 @@ block-size: auto; } + /* Zero-specificity so .sf-bg-layer, .sf-frame > img, and any component's own + radius (later layer or higher specificity) always win. */ + :where(img, figure) { + border-radius: var(--sf-media-radius); + } + img, video { object-fit: var(--sf-object-fit); object-position: var(--sf-object-position); @@ -2220,7 +2335,7 @@ /* SLASHED — core/themes.css @layer slashed.themes Light/dark activation, scoped theme color inheritance, LumLocker, and scheme-aware color adjustments. - Uses data-theme and token overrides. */ + Uses data-theme, .sf-theme-light/.sf-theme-dark aliases, and token overrides. */ @layer slashed.themes { @@ -2231,10 +2346,10 @@ } } - [data-theme="light"] { color-scheme: light; --sf-is-dark: 0; } - [data-theme="dark"] { color-scheme: dark; --sf-is-dark: 1; } + :is([data-theme="light"], .sf-theme-light) { color-scheme: light; --sf-is-dark: 0; } + :is([data-theme="dark"], .sf-theme-dark) { color-scheme: dark; --sf-is-dark: 1; } - :where([data-theme]:not(:root, html)) { + :where([data-theme]:not(:root, html), .sf-theme-light:not(:root, html), .sf-theme-dark:not(:root, html)) { background-color: var(--sf-color-bg); color: var(--sf-color-text); } @@ -2261,13 +2376,13 @@ ); } - :root[data-lumlocker] [data-theme="dark"] { + :root[data-lumlocker] :is([data-theme="dark"], .sf-theme-dark) { --sf-color-primary: oklch(from var(--sf-color-primary-source-dark, var(--sf-color-primary-source-light)) var(--sf-lumlocker) c h); --sf-color-secondary: oklch(from var(--sf-color-secondary-source-dark, var(--sf-color-secondary-source-light)) var(--sf-lumlocker) c h); --sf-color-tertiary: oklch(from var(--sf-color-tertiary-source-dark, var(--sf-color-tertiary-source-light)) var(--sf-lumlocker) c h); --sf-color-action: oklch(from var(--sf-color-action-source-dark, var(--sf-color-action-source-light)) var(--sf-lumlocker) c h); } - :root[data-lumlocker] [data-theme="light"] { + :root[data-lumlocker] :is([data-theme="light"], .sf-theme-light) { --sf-color-primary: oklch(from var(--sf-color-primary-source-light) var(--sf-lumlocker) c h); --sf-color-secondary: oklch(from var(--sf-color-secondary-source-light) var(--sf-lumlocker) c h); --sf-color-tertiary: oklch(from var(--sf-color-tertiary-source-light) var(--sf-lumlocker) c h); @@ -2298,7 +2413,7 @@ --sf-color-danger-source-dark: oklch(from var(--sf-color-danger-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h); } - [data-theme="dark"] { + :is([data-theme="dark"], .sf-theme-dark) { --sf-color-primary: var(--sf-color-primary-source-dark); --sf-color-secondary: var(--sf-color-secondary-source-dark); @@ -2341,9 +2456,22 @@ --sf-color-warning-strong: oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h); --sf-color-info-strong: oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h); --sf-color-danger-strong: oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h); + + /* :root-only aliases from tokens.css — var() resolves on the declaring + element, so without these re-declarations a nested [data-theme] + section inherits the :root-resolved (wrong-scheme) values (#496). */ + --sf-color-code-bg: var(--sf-color-inset); + --sf-color-bg--disabled: var(--sf-color-inset); + --sf-color-link--disabled: var(--sf-color-text--disabled); + --sf-scrollbar-thumb: var(--sf-color-neutral); + /* Non-sign() fallback: keeps inline code readable in engines with + oklch(from) but not sign() — the sign()-gated auto-contrast block + below overrides this with a proper per-background contrast calc + wherever sign() is supported. */ + --sf-color-code-text: var(--sf-color-text); } - [data-theme="light"] { + :is([data-theme="light"], .sf-theme-light) { --sf-color-primary: var(--sf-color-primary-source-light); --sf-color-secondary: var(--sf-color-secondary-source-light); @@ -2386,12 +2514,20 @@ --sf-color-warning-strong: oklch(from var(--sf-color-warning-source-light) calc(l - 0.25) c h); --sf-color-info-strong: oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h); --sf-color-danger-strong: oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h); + + /* :root-only aliases — see the matching note in the dark block (#496). */ + --sf-color-code-bg: var(--sf-color-inset); + --sf-color-bg--disabled: var(--sf-color-inset); + --sf-color-link--disabled: var(--sf-color-text--disabled); + --sf-scrollbar-thumb: var(--sf-color-neutral); + /* Non-sign() fallback — see matching note in the dark block (#496). */ + --sf-color-code-text: var(--sf-color-text); } } @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))) { - [data-theme="dark"] { + :is([data-theme="dark"], .sf-theme-dark) { --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); @@ -2402,8 +2538,12 @@ --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-inverse: oklch(from var(--sf-color-inverse) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); + /* Auto-contrast code text re-derived in-scope so it tracks the + per-scope --sf-color-code-bg above (#496). Same formula as + core/tokens.css — keep in sync. */ + --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); } - [data-theme="light"] { + :is([data-theme="light"], .sf-theme-light) { --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); @@ -2414,6 +2554,8 @@ --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); --sf-color-text--on-inverse: oklch(from var(--sf-color-inverse) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); + /* See matching note in the dark block (#496). */ + --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0); } } @@ -2464,6 +2606,17 @@ .sf-section-group > .sf-section + .sf-section { padding-block-start: 0; } + /* Header — block padding on the standard spacing scale. Headers usually + want lighter vertical rhythm than .sf-section, so these map to + --sf-space-* directly. Only block padding is set; any inline gutter + (from .sf-section--guttered, a container, or the element itself) is + left intact. */ + .sf-header--xs { padding-block: var(--sf-space-xs); } + .sf-header--s { padding-block: var(--sf-space-s); } + .sf-header--m { padding-block: var(--sf-space-m); } + .sf-header--l { padding-block: var(--sf-space-l); } + .sf-header--xl { padding-block: var(--sf-space-xl); } + /* Collapse modifier */ .sf-section--collapse:has(+ .sf-section--collapse) { padding-block-end: calc(var(--sf-section-pad) / 2); @@ -2496,7 +2649,18 @@ background: linear-gradient(to right, transparent, var(--sf-divider-color), transparent); } - + /* Divide — rules between direct children, reusing .sf-divider's tokens. + Unlike .sf-divider (a standalone separator element), .sf-divide is + applied to the container and needs no extra markup per gap. */ + .sf-divide > * + * { + border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color); + } + .sf-divide--vertical > * + * { + border-block-start: none; + border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color); + } + + /* Container */ .sf-container { container: sf-layout / inline-size; @@ -2516,7 +2680,62 @@ container-type: inline-size; } + /* Overlay — absolute-fill layout helper for overlays/backdrops. */ + .sf-overlay { + position: absolute; + inset: 0; + } + + /* .sf-fluid-cq: opt a subtree into a CONTAINER-relative fluid scale (issue + #497). By default --sf-text-* / --sf-space-* interpolate against 100vw (the + viewport); inside .sf-fluid-cq they interpolate against 100cqi (this box's + inline size), so type + spacing track the container the way the layout + primitives (.sf-grid-cols-*, .sf-content-grid, .sf-bento) already do. Use + on nested / narrow contexts (sidebars, cards, 1/3 columns) where a + viewport-driven scale would size text as if it spanned the full page. + + Why the tokens are RE-DECLARED (not just --sf-fluid-width flipped): the + --sf-text-* / --sf-space-* tokens are computed at :root, so their + var(--sf-fluid-width) is substituted there (100vw) and the *resolved* + length inherits down — overriding --sf-fluid-width lower in the tree can't + retroactively rewrite an already-computed inherited value. The scale is + therefore re-declared in-scope so var(--sf-fluid-width) re-resolves to + 100cqi. It is re-declared on the direct children, not the element itself: + a query container's own cq units resolve against its ANCESTOR container, so + the re-computation must happen one level in — children compute against + .sf-fluid-cq and deeper descendants inherit that resolved value. + + The formulas below are character-identical to their :root twins in + core/tokens.css (both read var(--sf-fluid-width)); keep the two copies in + sync if the generative formula ever changes (cf. themes.css SL-001). */ + .sf-fluid-cq { container-type: inline-size; } + .sf-fluid-cq > * { + --sf-fluid-width: 100cqi; + --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale)); + --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale)); + --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale)); + --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale)); + --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale)); + --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale)); + --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale)); + --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale)); + --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale)); + --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale)); + --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale)); + --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale)); + --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale)); + --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale)); + --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale)); + --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale)); + --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale)); + --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale)); + --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale)); + --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale)); + } + + /* Stack & gap */ .sf-stack { display: flex; @@ -2656,6 +2875,32 @@ .sf-grid--dense { grid-auto-flow: dense; } + /* Grid-flex — flexbox alternative to .sf-grid for item counts that + don't divide evenly: leftover last-row items stretch to fill the + row by default, or stay fixed-width and centered with --center. + CSS Grid keeps orphans pinned to their track; flex does not. */ + .sf-grid-flex { + display: flex; + flex-wrap: wrap; + gap: var(--sf-grid-gap); + } + .sf-grid-flex > * { + flex: 1 1 min(var(--sf-grid-min), 100%); + } + + .sf-grid-flex--center { justify-content: center; } + .sf-grid-flex--center > * { + flex-grow: 0; + flex-basis: min(var(--sf-grid-min), 100%); + } + + .sf-grid-flex--xs { --sf-grid-min: var(--sf-grid-min-xs); } + .sf-grid-flex--s { --sf-grid-min: var(--sf-grid-min-s); } + .sf-grid-flex--m { --sf-grid-min: var(--sf-grid-min-m); } + .sf-grid-flex--l { --sf-grid-min: var(--sf-grid-min-l); } + .sf-grid-flex--xl { --sf-grid-min: var(--sf-grid-min-xl); } + .sf-grid-flex--2xl { --sf-grid-min: var(--sf-grid-min-2xl); } + .sf-icon { display: inline-block; inline-size: var(--sf-icon-size, var(--sf-icon-m)); @@ -2728,41 +2973,41 @@ /* Background layer — absolutely-positioned cover media that sits behind a parent's content. The parent is auto-promoted to a positioned, isolated - stacking context via :has(), so .sf-bg composes natively under .sf-scrim - (background → scrim gradient → content). Works as a bare + stacking context via :has(), so .sf-bg-layer composes natively under .sf-scrim + (background → scrim gradient → content). Works as a bare or as a wrapper with a nested > img / > video. */ - :where(:has(> .sf-bg)) { + :where(:has(> .sf-bg-layer)) { position: relative; isolation: isolate; } - .sf-bg { + .sf-bg-layer { position: absolute; - inset: var(--sf-bg-inset); - inline-size: calc(100% - 2 * var(--sf-bg-inset)); - block-size: calc(100% - 2 * var(--sf-bg-inset)); - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); - z-index: var(--sf-bg-z); + inset: var(--sf-bg-layer-inset); + inline-size: calc(100% - 2 * var(--sf-bg-layer-inset)); + block-size: calc(100% - 2 * var(--sf-bg-layer-inset)); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); + z-index: var(--sf-bg-layer-z); pointer-events: none; } - .sf-bg > :is(img, video, picture) { + .sf-bg-layer > :is(img, video, picture) { inline-size: 100%; block-size: 100%; - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); pointer-events: none; } - .sf-bg > picture > img { + .sf-bg-layer > picture > img { inline-size: 100%; block-size: 100%; - object-fit: var(--sf-bg-fit); - object-position: var(--sf-bg-position); - border-radius: var(--sf-bg-radius); + object-fit: var(--sf-bg-layer-fit); + object-position: var(--sf-bg-layer-position); + border-radius: var(--sf-bg-layer-radius); pointer-events: none; } @@ -2877,14 +3122,16 @@ } .sf-equal { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-equal-min-col), 100%), 1fr)); - gap: var(--sf-equal-gap); + column-width: var(--sf-equal-min-col); + column-gap: var(--sf-equal-gap); + column-rule: var(--sf-equal-rule-width) var(--sf-equal-rule-style) var(--sf-equal-rule-color); } - .sf-equal--2 { --sf-equal-min-col: var(--sf-equal-min-col-2); } - .sf-equal--3 { --sf-equal-min-col: var(--sf-equal-min-col-3); } - .sf-equal--4 { --sf-equal-min-col: var(--sf-equal-min-col-4); } - .sf-equal--6 { --sf-equal-min-col: var(--sf-equal-min-col-6); } + .sf-equal > * { break-inside: avoid; } + + .sf-equal--2 { column-count: 2; } + .sf-equal--3 { column-count: 3; } + .sf-equal--4 { column-count: 4; } + .sf-equal--6 { column-count: 6; } .sf-content-grid { @@ -2907,7 +3154,6 @@ .sf-content-grid > .sf-breakout { grid-column: breakout; } .sf-content-grid > .sf-full-bleed { grid-column: full; } - .sf-bento { display: grid; grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr); @@ -3259,6 +3505,22 @@ .sf-scrim--bottom { --sf-scrim-direction: to top; } .sf-scrim--full { --sf-scrim-gradient: var(--sf-scrim-color); } + /* Named background surface preset (issue #523). Composes the + --sf-surface-bg-* token set (see tokens.macros.css) into one reusable, + named background: base colour + image/gradient/pattern, sizing, an + optional overlay layered above the image, and an optional animation. + Inert until a preset feeds the tokens. The overlay is the first + background-image layer so it paints ABOVE the image. */ + .sf-surface-bg { + background-color: var(--sf-surface-bg-color); + background-image: var(--sf-surface-bg-overlay), var(--sf-surface-bg-image); + background-size: var(--sf-surface-bg-size); + background-position: var(--sf-surface-bg-position); + background-repeat: var(--sf-surface-bg-repeat); + background-attachment: var(--sf-surface-bg-attachment); + animation: var(--sf-surface-bg-animation); + } + .sf-text-protect { text-shadow: var(--sf-scrim-text-shadow); } @@ -3275,6 +3537,14 @@ font-size: 0.85em; text-decoration: none; } + /* Alt-text form (accessible name for the glyph) — gated so a UA that + rejects the "value / string" grammar keeps the plain marker above + instead of dropping the whole declaration. */ + @supports (content: "x" / "y") { + .sf-link-external::after { + content: var(--sf-link-external-marker) / var(--sf-link-external-label); + } + } .sf-content-auto { content-visibility: auto; @@ -3285,6 +3555,14 @@ font-variant-numeric: var(--sf-font-numeric, tabular-nums); } + /* filter: drop-shadow() utilities — unlike box-shadow, follows the element's + actual alpha shape (SVG icons, PNG cutouts, transparent logos). */ + .sf-drop-shadow-xs { filter: var(--sf-drop-shadow-xs); } + .sf-drop-shadow-s { filter: var(--sf-drop-shadow-s); } + .sf-drop-shadow-m { filter: var(--sf-drop-shadow-m); } + .sf-drop-shadow-l { filter: var(--sf-drop-shadow-l); } + .sf-drop-shadow-xl { filter: var(--sf-drop-shadow-xl); } + .sf-link--subtle:link, .sf-link--subtle:visited { text-decoration-line: none; @@ -3304,35 +3582,85 @@ 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)); + } + } /* ─── core/states.css ─── */ /* SLASHED — core/states.css @layer slashed.states Generic runtime states toggled by JS or ARIA: visibility, interactivity, selection, busy/current. - Prefix: .is-*; not utilities or component-specific variants. */ + Prefix: .sf-is-*; not utilities or component-specific variants. */ @layer slashed.states { - /* VISIBILITY */ + /* -- VISIBILITY -- */ - .is-hidden { + .sf-is-hidden { display: none !important; } - .is-invisible { + .sf-is-invisible { visibility: hidden; } - .is-visible { + .sf-is-visible { visibility: visible; } - /* INTERACTIVITY */ + /* -- INTERACTIVITY -- */ /* cursor: not-allowed inherits to children with pointer-events: auto — - intentional, part of the .is-disabled API surface. */ - .is-disabled { + intentional, part of the .sf-is-disabled API surface. */ + .sf-is-disabled { opacity: var(--sf-opacity-disabled); pointer-events: none; @@ -3340,24 +3668,24 @@ user-select: none; } - .is-readonly { + .sf-is-readonly { pointer-events: none; user-select: none; } - /* LOADING / ASYNC FEEDBACK */ + /* -- LOADING / ASYNC FEEDBACK -- */ - .is-loading { + .sf-is-loading { color: transparent !important; pointer-events: none; position: relative; } - .is-loading > * { + .sf-is-loading > * { opacity: 0 !important; } - .is-loading::after { + .sf-is-loading::after { content: ""; position: absolute; inset-block-start: 50%; @@ -3373,22 +3701,22 @@ animation: var(--sf-animation-spin); } - .is-busy { + .sf-is-busy { cursor: progress; } - .is-pending { + .sf-is-pending { opacity: var(--sf-state-pending-opacity); cursor: progress; } - img.is-skeleton, - .is-skeleton img { + img.sf-is-skeleton, + .sf-is-skeleton img { opacity: 0 !important; } - .is-skeleton { + .sf-is-skeleton { color: transparent !important; background: linear-gradient( 90deg, @@ -3403,168 +3731,95 @@ user-select: none; } - /* ACTIVE / SELECTED / CURRENT */ + /* -- ACTIVE / SELECTED / CURRENT -- */ - .is-active { + .sf-is-active { --sf-is-active: 1; } - .is-selected { + .sf-is-selected { background-color: var(--sf-color-bg--selected); } - .is-current { + .sf-is-current { --sf-is-current: 1; font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold)); } - .is-highlighted { + .sf-is-highlighted { background-color: var(--sf-color-bg--focus); } - .is-pressed { + .sf-is-pressed { --sf-is-pressed: 1; background-color: var(--sf-color-bg--selected); } - /* OPEN / CLOSED */ + /* -- OPEN / CLOSED -- */ - .is-open { + .sf-is-open { --sf-is-open: 1; } - .is-collapsed { + .sf-is-collapsed { --sf-is-open: 0; } - .is-expanded { + .sf-is-expanded { --sf-is-open: 1; } - /* VALIDATION / FEEDBACK */ + /* -- VALIDATION / FEEDBACK -- */ - .is-valid, - .is-success { + .sf-is-valid, + .sf-is-success { --sf-field-border-color: var(--sf-color-success); --sf-field-text-color: var(--sf-color-success-strong); } - .is-invalid, - .is-error { + .sf-is-invalid, + .sf-is-error { --sf-field-border-color: var(--sf-color-danger); --sf-field-text-color: var(--sf-color-danger-strong); } - .is-warning { + .sf-is-warning { --sf-field-border-color: var(--sf-color-warning); --sf-field-text-color: var(--sf-color-warning-strong); } - .is-info { + .sf-is-info { --sf-field-border-color: var(--sf-color-info); --sf-field-text-color: var(--sf-color-info-strong); } - .is-danger { + .sf-is-danger { --sf-field-border-color: var(--sf-color-danger); --sf-field-text-color: var(--sf-color-danger-strong); } - /* POSITION / STICKINESS */ - - .is-sticky { - position: sticky; - inset-block-start: var(--sf-sticky-offset, 0); - z-index: var(--sf-z-sticky); - } - - .is-pinned { - position: sticky; - inset-block-start: 0; - z-index: var(--sf-z-fixed); - } - - .is-fixed { - - position: fixed; - } - - .is-fullscreen { - - position: fixed; - inset: 0; - z-index: var(--sf-z-overlay); - } - - .is-resizable { - resize: both; - overflow: auto; - min-inline-size: var(--sf-space-xl, 3.55rem); - min-block-size: var(--sf-space-xl, 3.55rem); - } - - /* OVERFLOW / CLIPPING */ + /* -- DRAG & DROP -- */ - .is-clipped { - overflow: hidden !important; - } - - .is-scrollable { - overflow: auto; - overscroll-behavior: contain; - } - - .is-truncated { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - /* DRAG & DROP */ - - .is-dragging { + .sf-is-dragging { opacity: var(--sf-opacity-muted); cursor: grabbing; } - .is-drop-target { + .sf-is-drop-target { outline: var(--sf-focus-ring-width) dashed var(--sf-color-action); outline-offset: var(--sf-focus-ring-offset); } - .is-draggable { + .sf-is-draggable { cursor: grab; } - /* OVERLAY / BACKDROP */ - - .is-overlay { - position: absolute; - inset: 0; - } - - /* FOCUS / INTERACTION MODIFIERS */ - - .is-clickable { - cursor: pointer; - } - - .is-unselectable { - user-select: none; - } + /* -- EMPTY STATE -- */ - .is-focused { - - outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color); - outline-offset: var(--sf-focus-ring-offset); - } - - /* EMPTY STATE */ - - .is-empty:empty { + .sf-is-empty:empty { display: none; } @@ -3589,8 +3844,8 @@ a, button, input, select, textarea, summary { transition-property: color, background-color, border-color, text-decoration-color, - fill, stroke, opacity, box-shadow, transform, filter, - backdrop-filter; + fill, stroke, opacity, box-shadow, transform, scale, translate, + filter, backdrop-filter; transition-duration: var(--sf-duration-fast); transition-timing-function: var(--sf-ease-out); } @@ -3646,6 +3901,35 @@ .sf-entrance--fade-right { animation-name: sf-slide-in-right; } .sf-entrance--scale-up { animation-name: sf-scale-up; } + + /* Exit counterpart of .sf-entrance--*: fires as the element scrolls + out of view. Every declaration below lives inside @supports — + unlike entrance (where a non-scroll fallback still ends visible), + an unconditional exit animation would leave content permanently + hidden on engines without animation-timeline: view() support, so + there is deliberately no fallback declaration outside @supports. */ + @supports (animation-timeline: view()) { + .sf-exit--fade, + .sf-exit--fade-up, + .sf-exit--fade-down, + .sf-exit--fade-left, + .sf-exit--fade-right, + .sf-exit--scale-down { + animation-duration: var(--sf-duration-slow); + animation-timing-function: var(--sf-ease-in); + animation-fill-mode: both; + animation-timeline: view(); + animation-range: var(--sf-scroll-timeline-range-exit-start, cover 70%) + var(--sf-scroll-timeline-range-exit-end, exit 100%); + } + .sf-exit--fade { animation-name: sf-fade-out; } + .sf-exit--fade-up { animation-name: sf-slide-out-up; } + .sf-exit--fade-down { animation-name: sf-slide-out-down; } + .sf-exit--fade-left { animation-name: sf-slide-out-left; } + .sf-exit--fade-right { animation-name: sf-slide-out-right; } + .sf-exit--scale-down { animation-name: sf-scale-down; } + } + } @keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } } @@ -3657,6 +3941,11 @@ @keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } } @keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } } + @keyframes sf-slide-out-up { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -1rem; } } + @keyframes sf-slide-out-down { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 1rem; } } + @keyframes sf-slide-out-left { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: -1rem 0; } } + @keyframes sf-slide-out-right { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 1rem 0; } } + @keyframes sf-spin { to { rotate: 360deg; } } @keyframes sf-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } @@ -3921,7 +4210,7 @@ color: CanvasText !important; } - .is-loading::after { + .sf-is-loading::after { border-color: ButtonText !important; border-block-start-color: Highlight !important; } @@ -3989,6 +4278,16 @@ } + /* Fluid --sf-h*-size tokens resolve via 100vw clamp()s, which have no + reliable meaning on a print page box. Pin print heading sizes to + static pt values instead. */ + h1 { font-size: 24pt; } + h2 { font-size: 20pt; } + h3 { font-size: 16pt; } + h4 { font-size: 14pt; } + h5 { font-size: 12pt; } + h6 { font-size: 11pt; } + p { orphans: 3; widows: 3; @@ -4308,8 +4607,8 @@ /* ─── optional/tokens.components.css ─── */ /* SLASHED — optional/tokens.components.css @layer slashed.tokens - Reserved component token surface for the optional component module. - Prefix: --sf-*; declarations stay commented until matching classes ship. */ + Component token surface for the optional component module: card, field, + and button knobs. Prefix: --sf-*. */ @layer slashed.tokens { :root { @@ -4317,29 +4616,31 @@ /* ============================================================ * CARD TOKENS * ============================================================ - * * Concentric radius math: outer = inner + padding. Keeps inner * elements that use the global --sf-radius-m visually - * proportional inside a padded card. - * - * --sf-card-padding: var(--sf-space-l); - * --sf-card-gap: var(--sf-space-m); - * --sf-card-radius: var(--sf-radius-m); - * --sf-card-radius-outer: calc(var(--sf-card-radius) + var(--sf-card-padding)); - * --sf-card-bg: var(--sf-color-surface); - * --sf-card-border-width: var(--sf-border-width-1); - * --sf-card-border-color: var(--sf-color-border); - * --sf-card-shadow: var(--sf-shadow-s); - * - * Smart-card subcomponents (.sf-card__media / __avatar / __title and the + * proportional inside a padded card. */ + --sf-card-padding: var(--sf-space-l); + --sf-card-gap: var(--sf-space-m); + --sf-card-radius: var(--sf-radius-m); + --sf-card-radius-outer: calc(var(--sf-card-radius) + var(--sf-card-padding)); + --sf-card-bg: var(--sf-color-surface); + --sf-card-border-width: var(--sf-border-width-1); + --sf-card-border-color: var(--sf-color-border); + --sf-card-shadow: var(--sf-shadow-s); + /* Shadow escalation for the --elevated and --interactive modifiers. + * Both default to the same --sf-shadow-l step (elevated's resting shadow + * and interactive's hover shadow read as the same "lifted" level) — two + * tokens so each modifier can still be tuned independently. */ + --sf-card-shadow--elevated: var(--sf-shadow-l); + --sf-card-shadow--hover: var(--sf-shadow-l); + + /* Smart-card subcomponents (.sf-card__media / __avatar / __title and the * scoped button font-size override). Media defaults to the 16/9 ratio - * token; inner media radius mirrors the card's inner radius. - * - * --sf-card-media-ratio: var(--sf-ratio-video); - * --sf-card-media-radius: var(--sf-card-radius, var(--sf-radius-m)); - * --sf-card-heading-size: var(--sf-text-xl); - * --sf-card-btn-font-size: var(--sf-text-s); - */ + * token; inner media radius mirrors the card's inner radius. */ + --sf-card-media-ratio: var(--sf-ratio-video); + --sf-card-media-radius: var(--sf-card-radius, var(--sf-radius-m)); + --sf-card-heading-size: var(--sf-text-xl); + --sf-card-btn-font-size: var(--sf-text-s); /* ============================================================ * FIELD TOKENS @@ -4358,12 +4659,15 @@ --sf-btn-radius: var(--sf-radius-m); --sf-btn-padding-block: var(--sf-space-xs); --sf-btn-padding-inline: var(--sf-space-m); + --sf-btn-gap: var(--sf-space-2xs); + --sf-btn-font-size: var(--sf-text-m); + --sf-btn-font-weight: var(--sf-font-weight-interactive); + --sf-btn-min-height: var(--sf-touch-target); + --sf-btn-border-width: var(--sf-border-width-1); /* - * --sf-btn-gap: var(--sf-space-2xs); - * --sf-btn-font-size: var(--sf-text-m); - * --sf-btn-font-weight: var(--sf-font-weight-interactive); - * --sf-btn-min-height: var(--sf-touch-target); - * --sf-btn-border-width: var(--sf-border-width-1); + * Size modifiers (.sf-btn--xs/--s/--l/--xl) and the colour family + * (--sf-btn-color / --sf-btn-color--hover / --sf-btn-on-color) are + * rule-local overrides set in optional/components.css, not global knobs. * * Full-width: .sf-btn--block is unconditional; .sf-btn--block-cq * stretches to 100% only inside a query container narrower than 20rem @@ -4372,117 +4676,18 @@ * there is no token for it. */ - /* ============================================================ - * BADGE TOKENS - * ============================================================ - * - * --sf-badge-pad-x: var(--sf-space-2xs); - * --sf-badge-pad-y: var(--sf-space-3xs, 0.125rem); - * --sf-badge-radius: var(--sf-radius-pill, 9999px); - * --sf-badge-font-size: var(--sf-text-2xs); - * --sf-badge-font-weight: var(--sf-font-weight-interactive); - * --sf-badge-line-height: var(--sf-leading-tight); - */ - - /* ============================================================ - * TAG TOKENS - * ============================================================ - * - * --sf-tag-pad-x: var(--sf-space-xs); - * --sf-tag-pad-y: var(--sf-space-3xs, 0.125rem); - * --sf-tag-radius: var(--sf-radius-s); - * --sf-tag-gap: var(--sf-space-2xs); - * --sf-tag-font-size: var(--sf-text-xs); - * --sf-tag-bg: var(--sf-color-inset); - * --sf-tag-border-width: var(--sf-border-width-1); - * --sf-tag-border-color: var(--sf-color-border); - */ - - /* ============================================================ - * ALERT TOKENS - * ============================================================ - * - * --sf-alert-pad: var(--sf-space-m); - * --sf-alert-radius: var(--sf-radius-m); - * --sf-alert-gap: var(--sf-space-s); - * --sf-alert-border-width: var(--sf-border-width-1); - * --sf-alert-icon-size: var(--sf-icon-m); - */ - - /* ============================================================ - * AVATAR TOKENS - * ============================================================ - * - * --sf-avatar-size: 2.5rem; - * --sf-avatar-radius: var(--sf-radius-full, 50%); - * --sf-avatar-bg: var(--sf-color-inset); - * --sf-avatar-font-weight: var(--sf-font-weight-semibold); - * --sf-avatar-border-width: 0; - * --sf-avatar-border-color: var(--sf-color-border); - */ - - /* ============================================================ - * MODAL TOKENS - * ============================================================ - * - * --sf-modal-pad: var(--sf-space-l); - * --sf-modal-gap: var(--sf-space-m); - * --sf-modal-radius: var(--sf-radius-l); - * --sf-modal-max-inline: min(90vw, 32rem); - * --sf-modal-bg: var(--sf-color-surface); - * --sf-modal-shadow: var(--sf-shadow-xl); - * --sf-modal-backdrop-bg: color-mix(in oklab, var(--sf-color-neutral) 60%, transparent); - * --sf-modal-z: var(--sf-z-modal); - */ - - /* ============================================================ - * SKELETON TOKENS - * ============================================================ - * - * --sf-skeleton-bg: var(--sf-color-inset); - * --sf-skeleton-shimmer: var(--sf-color-raised); - * --sf-skeleton-radius: var(--sf-radius-s); - * --sf-skeleton-duration: 1.5s; - * --sf-skeleton-line-height: 0.8em; - */ - - /* ============================================================ - * FORM LAYOUT HELPER TOKENS - * Gap tokens for .sf-field, .sf-form-row, .sf-form-col, - * and .sf-form-inline declared in optional/components.css. - * ============================================================ - * - * --sf-field-gap: var(--sf-space-2xs); - * --sf-form-row-gap: var(--sf-space-m); - * --sf-form-col-gap: var(--sf-space-s); - * --sf-form-inline-gap: var(--sf-space-s); - */ - - /* ============================================================ - * TABLE TOKENS - * Sizing tokens for .sf-table declared in - * optional/components.css. - * ============================================================ - * - * --sf-table-cell-pad-block: var(--sf-space-xs); - * --sf-table-cell-pad-inline: var(--sf-space-s); - * --sf-table-border-color: var(--sf-color-border); - * --sf-table-stripe-bg: var(--sf-color-inset); - * --sf-table-header-weight: var(--sf-font-weight-semibold); - * --sf-table-compact-pad: var(--sf-space-3xs, 0.125rem); - */ - } } /* ─── optional/components.css ─── */ /* SLASHED — optional/components.css @layer slashed.components - Reserved component class surface for button, card, badge, tag, alert, - avatar, modal, skeleton, form layout helpers, and table. + Component class surface: button and card. - STAGED — all definitions are commented out until v0.8. - Imports optional component tokens (tokens are live where noted). */ + .sf-btn and .sf-card are LIVE since v0.7.0. These are the only two + components SLASHED ships — the framework stays BEM-first and token-first + and does not carry a broad component library. + Imports optional component tokens. */ @layer slashed.components { @@ -4490,21 +4695,37 @@ /* ============================================================ * .sf-btn * Interactive call-to-action element. Apply to {/if} - {#if showRaw && rawDefault} + {#if showPicker} + + {:else if showRawText} { isEditing = true; }} + onfocus={() => { isEditingRaw = true; }} onblur={() => { - isEditing = false; - if (!rawDraft.trim()) onReset(); + isEditingRaw = false; + const v = rawDraft.trim(); + if (!v) backToVariable(); + else onRawSet?.(v); + }} + onkeydown={(e) => { + if (e.key === "Enter") (e.currentTarget as HTMLInputElement).blur(); }} oninput={(e) => { rawDraft = (e.target as HTMLInputElement).value; - const v = rawDraft.trim(); - if (v && onRawSet) onRawSet(v); }} - class="w-full bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-2 py-1.5 text-[11px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-500 focus:outline-none focus:border-indigo-500" + class="w-full bg-black/8 dark:bg-white/8 border border-indigo-500/50 rounded px-2 py-1.5 text-[11px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-500 focus:outline-none" /> + {#if canPick} + + {/if} {:else} - {#if rawDefault && !overridden} -

default: {rawDefault}

+ {#if hasVarInfo} + {#if canPick || currentRaw !== undefined} + + {:else} +

default: {rawDefault}

+ {/if} {/if} {/if} diff --git a/SLASHED-for-WP/admin-app/src/components/panels/BordersPanel.svelte b/SLASHED-for-WP/admin-app/src/components/panels/BordersPanel.svelte index f480d283..f018e8d8 100644 --- a/SLASHED-for-WP/admin-app/src/components/panels/BordersPanel.svelte +++ b/SLASHED-for-WP/admin-app/src/components/panels/BordersPanel.svelte @@ -3,6 +3,7 @@ import PowerKnobRow from '../inputs/PowerKnobRow.svelte'; import SliderRow from '../inputs/SliderRow.svelte'; import ColorInput from '../inputs/ColorInput.svelte'; + import { SPACE_SCALE, RADIUS_SCALE, BORDER_WIDTH_SCALE, type VarOption } from '../../lib/variableScales'; let { overrides, onSet, onReset }: { overrides: Record; @@ -23,13 +24,10 @@ { step: "2xl", name: "--sf-radius-2xl", default: 24, max: 80, step_size: 2, rawDefault: "calc(24px * var(--sf-radius-scale))" }, ]; - const COMPONENT_TOKENS = [ - { label: "Button radius", token: "--sf-btn-radius", unit: "rem", min: 0, max: 2, step: 0.05, default: 0.5, rawDefault: "var(--sf-radius-m)", help: "--sf-btn-radius" }, - { label: "Button padding block", token: "--sf-btn-padding-block", unit: "rem", min: 0, max: 1, step: 0.025, default: 0.375, rawDefault: "var(--sf-space-xs)", help: "--sf-btn-padding-block" }, - { label: "Button padding inline", token: "--sf-btn-padding-inline", unit: "rem", min: 0, max: 2, step: 0.025, default: 1, rawDefault: "var(--sf-space-m)", help: "--sf-btn-padding-inline" }, - { label: "Field radius", token: "--sf-field-radius", unit: "rem", min: 0, max: 2, step: 0.05, default: 0.5, rawDefault: "var(--sf-radius-m)", help: "--sf-field-radius" }, - { label: "Field padding block", token: "--sf-field-padding-block", unit: "rem", min: 0, max: 1, step: 0.025, default: 0.375, rawDefault: "var(--sf-space-xs)", help: "--sf-field-padding-block" }, - { label: "Field padding inline", token: "--sf-field-padding-inline", unit: "rem", min: 0, max: 2, step: 0.025, default: 0.75, rawDefault: "var(--sf-space-s)", help: "--sf-field-padding-inline" }, + const COMPONENT_TOKENS: Array<{ label: string; token: string; unit: string; min: number; max: number; step: number; default: number; rawDefault: string; help: string; variableOptions: VarOption[] }> = [ + { label: "Field radius", token: "--sf-field-radius", unit: "rem", min: 0, max: 2, step: 0.05, default: 0.5, rawDefault: "var(--sf-radius-m)", help: "--sf-field-radius", variableOptions: RADIUS_SCALE }, + { label: "Field padding block", token: "--sf-field-padding-block", unit: "rem", min: 0, max: 1, step: 0.025, default: 0.375, rawDefault: "var(--sf-space-xs)", help: "--sf-field-padding-block", variableOptions: SPACE_SCALE }, + { label: "Field padding inline", token: "--sf-field-padding-inline", unit: "rem", min: 0, max: 2, step: 0.025, default: 0.75, rawDefault: "var(--sf-space-s)", help: "--sf-field-padding-inline", variableOptions: SPACE_SCALE }, ]; const knobs = KNOBS_BY_DOMAIN["borders"] ?? []; @@ -41,8 +39,9 @@ let showFocusRing = $state(false); let showRadiusScale = $state(false); let showRadiusPreview = $state(false); + let showMediaRadius = $state(false); let showFineTune = $state(false); - let showComponents = $state(false); + let showFieldShape = $state(false); function parseNum(val: string | undefined, fallback: number, strip?: string): number { if (!val) return fallback; @@ -60,6 +59,7 @@ let borderStyle = $derived(overrides["--sf-border-style"] ?? "solid"); let focusRingColor = $derived(overrides["--sf-focus-ring-color"] ?? ""); let dividerColor = $derived(overrides["--sf-divider-color"] ?? ""); + let mediaRadius = $derived(parseNum(overrides["--sf-media-radius"]?.replace("rem",""), 0)); function getStyleCurrent(tokenName: string, defaultVal: string): string { return overrides[tokenName] ?? defaultVal; @@ -246,6 +246,7 @@ onChange={(v) => onSet("--sf-divider-width", `${v}px`)} onReset={() => onReset("--sf-divider-width")} rawDefault="var(--sf-border-width-1)" + variableOptions={BORDER_WIDTH_SCALE} currentRaw={overrides["--sf-divider-width"]} onRawSet={(v) => onSet("--sf-divider-width", v)} /> @@ -256,6 +257,7 @@ onChange={(v) => onSet("--sf-divider-gap", `${v}rem`)} onReset={() => onReset("--sf-divider-gap")} rawDefault="var(--sf-space-m)" + variableOptions={SPACE_SCALE} currentRaw={overrides["--sf-divider-gap"]} onRawSet={(v) => onSet("--sf-divider-gap", v)} /> @@ -380,17 +382,54 @@
- + +
+ + {#if showMediaRadius} +

+ --sf-media-radius — 0 by default (off). Rounds every <img>/<figure> globally via a + zero-specificity :where() rule; .sf-bg-layer and component-level radii still win. +

+ onSet("--sf-media-radius", `${v}rem`)} + onReset={() => onReset("--sf-media-radius")} + rawDefault="var(--sf-radius-m)" + variableOptions={RADIUS_SCALE} + currentRaw={overrides["--sf-media-radius"]} + onRawSet={(v) => onSet("--sf-media-radius", v)} + /> +
+
+
+ {/if} +
+ +
+ +
- {#if showComponents} + {#if showFieldShape}
{#each COMPONENT_TOKENS as t (t.token)} onSet(t.token, `${v}${t.unit}`)} onReset={() => onReset(t.token)} rawDefault={t.rawDefault} + variableOptions={t.variableOptions} currentRaw={overrides[t.token]} onRawSet={(v) => onSet(t.token, v)} /> diff --git a/SLASHED-for-WP/admin-app/src/components/panels/ComponentsPanel.svelte b/SLASHED-for-WP/admin-app/src/components/panels/ComponentsPanel.svelte new file mode 100644 index 00000000..86dcf165 --- /dev/null +++ b/SLASHED-for-WP/admin-app/src/components/panels/ComponentsPanel.svelte @@ -0,0 +1,478 @@ + + +
+ + +
+ + {#if showButton} + +
+
Variant (preview only)
+
+ {#each BTN_VARIANTS as v (v)} + + {/each} +
+
Style
+
+ {#each BTN_STYLES as s (s.id)} + + {/each} +
+
Size
+
+ {#each BTN_SIZES as s (s)} + + {/each} +
+
+ + +
+
+ + +
+ +
+ +
+ {#each BUTTON_TOKENS as t (t.token)} + onSet(t.token, `${v}${t.unit}`)} + onReset={() => onReset(t.token)} + rawDefault={t.rawDefault} + variableOptions={t.variableOptions} + currentRaw={overrides[t.token]} + onRawSet={(v) => onSet(t.token, v)} + /> + {/each} + +
+
Label size — --sf-btn-font-size
+
+ {#each TEXT_STEPS as step (step)} + {@const cur = currentStep("--sf-btn-font-size", "text", TEXT_STEPS, "m")} + + {/each} +
+
+ +
+
Label weight — --sf-btn-font-weight
+
+ {#each WEIGHT_OPTIONS as w (w)} + {@const cur = overrides["--sf-btn-font-weight"] ?? "600"} + + {/each} +
+
+
+ {/if} +
+ +
+ + +
+ + {#if showCard} +
+ + + +
+ + +
+
+
+
+

Card title

+
+
+

Body copy showing padding, gap, and background live.

+
+
+ +
+
+
+ +
+ {#each CARD_TOKENS as t (t.token)} + onSet(t.token, `${v}${t.unit}`)} + onReset={() => onReset(t.token)} + rawDefault={t.rawDefault} + variableOptions={t.variableOptions} + currentRaw={overrides[t.token]} + onRawSet={(v) => onSet(t.token, v)} + /> + {/each} + +
+ Radius (outer, computed) + {outerRadiusUnknown ? "depends on variable" : `${(cardRadius + cardPadding).toFixed(2)}rem`} +
+

+ --sf-card-radius-outer = radius + padding (concentric math) — not independently overridable, always computed from the two sliders above. +

+ +
+
Background — --sf-card-bg
+
+ {#each BG_PRESETS as p (p.step)} + {@const cur = currentStep("--sf-card-bg", "color", BG_PRESETS.map(x => x.step), "surface")} + + {/each} +
+
+ +
+
Border color — --sf-card-border-color
+
+ {#each BORDER_PRESETS as p (p.step)} + {@const cur = currentStep("--sf-card-border-color", "color", BORDER_PRESETS.map(x => x.step), "border")} + + {/each} +
+
+ +
+
Shadow — --sf-card-shadow
+
+ {#each SHADOW_STEPS as step (step)} + {@const cur = currentStep("--sf-card-shadow", "shadow", SHADOW_STEPS, "s")} + + {/each} +
+
+ +
+
Elevated shadow — --sf-card-shadow--elevated
+
+ {#each SHADOW_STEPS as step (step)} + {@const cur = currentStep("--sf-card-shadow--elevated", "shadow", SHADOW_STEPS, "l")} + + {/each} +
+
+ +
+
Hover shadow (interactive) — --sf-card-shadow--hover
+
+ {#each SHADOW_STEPS as step (step)} + {@const cur = currentStep("--sf-card-shadow--hover", "shadow", SHADOW_STEPS, "l")} + + {/each} +
+
+ +
+
Media aspect ratio — --sf-card-media-ratio
+
+ {#each RATIO_STEPS as step (step)} + {@const cur = currentStep("--sf-card-media-ratio", "ratio", RATIO_STEPS, "video")} + + {/each} +
+
+ +
+
Title size — --sf-card-heading-size
+
+ {#each TEXT_STEPS as step (step)} + {@const cur = currentStep("--sf-card-heading-size", "text", TEXT_STEPS, "xl")} + + {/each} +
+
+ +
+
Nested button size — --sf-card-btn-font-size
+
+ {#each TEXT_STEPS as step (step)} + {@const cur = currentStep("--sf-card-btn-font-size", "text", TEXT_STEPS, "s")} + + {/each} +
+
+ +
+
Media object-fit — --sf-object-fit (global, affects all img/video)
+
+ {#each ["cover", "contain"] as fit (fit)} + {@const cur = overrides["--sf-object-fit"] ?? "cover"} + + {/each} +
+
+
+ {/if} +
+
diff --git a/SLASHED-for-WP/admin-app/src/components/panels/EffectsPanel.svelte b/SLASHED-for-WP/admin-app/src/components/panels/EffectsPanel.svelte index 338fa6c2..d9ca4727 100644 --- a/SLASHED-for-WP/admin-app/src/components/panels/EffectsPanel.svelte +++ b/SLASHED-for-WP/admin-app/src/components/panels/EffectsPanel.svelte @@ -16,11 +16,15 @@ ]; const TEXT_SHADOW_TOKENS = [ - { label: "Small", token: "--sf-text-shadow-s", default: "0 1px 2px oklch(…)" }, - { label: "Medium", token: "--sf-text-shadow-m", default: "0 2px 4px oklch(…)" }, - { label: "Large", token: "--sf-text-shadow-l", default: "0 4px 8px oklch(…)" }, + { label: "Extra small", token: "--sf-text-shadow-xs", default: "0 0.5px 1px oklch(…)" }, + { label: "Small", token: "--sf-text-shadow-s", default: "0 1px 2px oklch(…)" }, + { label: "Medium", token: "--sf-text-shadow-m", default: "0 2px 4px oklch(…)" }, + { label: "Large", token: "--sf-text-shadow-l", default: "0 4px 8px oklch(…)" }, + { label: "Extra large", token: "--sf-text-shadow-xl", default: "0 8px 16px oklch(…)" }, ]; + const DROP_SHADOW_STEPS = ["xs", "s", "m", "l", "xl"]; + function parseNum(val: string | undefined, fallback: number, strip?: string): number { if (!val) return fallback; const v = parseFloat(strip ? val.replace(strip, "") : val); @@ -43,6 +47,7 @@ let showScrollbar = $state(false); let showScrollShadow = $state(false); let showTextShadow = $state(false); + let showDropShadow = $state(false);
@@ -286,8 +291,54 @@ placeholder={t.default} oninput={(e) => { const v = (e.target as HTMLInputElement).value; - const trimmed = v.trim(); - trimmed ? onSet(t.token, trimmed) : onReset(t.token); + v.trim() ? onSet(t.token, v) : onReset(t.token); + }} + class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" + /> + {#if t.token in overrides} + + {/if} +
+ {/each} +
+ {/if} + + +
+ + +
+ + {#if showDropShadow} +

+ filter: drop-shadow(…) — unlike box-shadow, follows the + element's actual alpha shape (SVG icons, PNG cutouts, transparent logos). Edit each token directly — + use none to disable. +

+
+ {#each [ + { label: "Extra small", token: "--sf-drop-shadow-xs", default: "drop-shadow(0 0.5px 1px oklch(…))" }, + { label: "Small", token: "--sf-drop-shadow-s", default: "drop-shadow(0 1px 2px oklch(…))" }, + { label: "Medium", token: "--sf-drop-shadow-m", default: "drop-shadow(0 4px 6px oklch(…))" }, + { label: "Large", token: "--sf-drop-shadow-l", default: "drop-shadow(0 8px 16px oklch(…))" }, + { label: "Extra large", token: "--sf-drop-shadow-xl", default: "drop-shadow(0 16px 32px oklch(…))" }, + ] as t (t.token)} +
+
{t.label}
+ { + const v = (e.target as HTMLInputElement).value; + v.trim() ? onSet(t.token, v) : onReset(t.token); }} class="flex-1 min-w-0 bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded px-1.5 py-1 text-[9px] font-mono text-slate-700 dark:text-slate-300 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500" /> @@ -297,6 +348,18 @@
{/each}
+ + +
+ {#each DROP_SHADOW_STEPS as step (step)} +
+ + {step} +
+ {/each} +
{/if}
diff --git a/SLASHED-for-WP/admin-app/src/components/panels/HomePanel.svelte b/SLASHED-for-WP/admin-app/src/components/panels/HomePanel.svelte index 6edb5ffc..0078d853 100644 --- a/SLASHED-for-WP/admin-app/src/components/panels/HomePanel.svelte +++ b/SLASHED-for-WP/admin-app/src/components/panels/HomePanel.svelte @@ -1,10 +1,11 @@