Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### Added

- **Macro `.sf-content-auto`** (`core/macros.css`) + **`--sf-content-intrinsic-size`**
token (`core/tokens.macros.css`, default `500px`) β€” sets
`content-visibility: auto` with a `contain-intrinsic-size` placeholder to
skip rendering offscreen content on long pages. Progressive enhancement;
unsupported engines render normally. Lands in the macros layer (a token-driven
recipe), not utilities. (#222)

- **Macro `.sf-tabular-nums`** (`core/macros.css`) β€” fixed-width digits
(`font-variant-numeric: var(--sf-font-numeric)`) for column-aligned numbers.
Complements the `--sf-font-numeric` token / number-input styling. (#214)

- **`--sf-transition-overlay`** (`core/tokens.css`) β€” reusable transition
building block for top-layer elements (`<dialog>`, `[popover]`). Uses
`transition-behavior: allow-discrete` on `overlay` and `display` so entry
Expand Down
32 changes: 32 additions & 0 deletions core/macros.css
Original file line number Diff line number Diff line change
Expand Up @@ -402,4 +402,36 @@
text-decoration: none;
}

/* -- Content visibility --------------------------------------
Skip rendering (layout + paint) for offscreen content until it
scrolls near the viewport β€” a large initial-render win on long
pages (product grids, long articles). contain-intrinsic-size
reserves a placeholder box so the scrollbar and scroll position
stay stable before a section is first rendered; the `auto`
keyword then caches each section's last-rendered size. Override
the placeholder per element:
style="--sf-content-intrinsic-size: 800px"

Unsupported engines (e.g. Safari < 18) simply ignore both
declarations and render normally β€” pure progressive enhancement.

Deliberately NOT paired with `will-change`: that pre-creates
compositing layers and usually HURTS performance when applied
broadly. `will-change` belongs on JS, set only while an element
is actively about to animate.
---------------------------------------------------------- */
.sf-content-auto {
content-visibility: auto;
contain-intrinsic-size: auto var(--sf-content-intrinsic-size, 500px);
}

/* -- Tabular figures -----------------------------------------
Fixed-width digits so numbers align in vertical columns
(price lists, totals, invoices, dashboards). Reads the public
--sf-font-numeric token. Universal browser support.
---------------------------------------------------------- */
.sf-tabular-nums {
font-variant-numeric: var(--sf-font-numeric, tabular-nums);
}

}
9 changes: 9 additions & 0 deletions core/tokens.macros.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,14 @@
---------------------------------------------------------- */
--sf-scroll-shadow-size: 2rem;

/* ----------------------------------------------------------
Content visibility β€” placeholder size for .sf-content-auto.
Feeds contain-intrinsic-size so offscreen sections reserve
space before their first render, keeping the scrollbar and
scroll position stable. Override per element:
style="--sf-content-intrinsic-size: 800px".
---------------------------------------------------------- */
--sf-content-intrinsic-size: 500px;

}
}
6 changes: 4 additions & 2 deletions docs/classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> **Generated** from source by `scripts/gen-class-reference.js` β€”
> run `npm run docs:classes` to refresh. Do not edit by hand.

**158 .sf-classes, 40 .is-classes.** Every `.sf-*` layout/macro class and `.is-*` state class.
**160 .sf-classes, 40 .is-classes.** Every `.sf-*` layout/macro class and `.is-*` state class.
See [architecture.md](architecture.md) for layer order and naming conventions.

## Layout primitives (`core/layout.css`)
Expand Down Expand Up @@ -130,11 +130,12 @@ See [architecture.md](architecture.md) for layer order and naming conventions.

## Macro classes (`core/macros.css`)

26 classes.
28 classes.

| Class |
|---|
| `.sf-aspect` |
| `.sf-content-auto` |
| `.sf-equal-height` |
| `.sf-flow` |
| `.sf-line-clamp-2` |
Expand All @@ -158,6 +159,7 @@ See [architecture.md](architecture.md) for layer order and naming conventions.
| `.sf-surface--success` |
| `.sf-surface--tertiary` |
| `.sf-surface--warning` |
| `.sf-tabular-nums` |
| `.sf-text-gradient` |
| `.sf-truncate` |

Expand Down
23 changes: 23 additions & 0 deletions docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -2459,6 +2459,29 @@ <h2>Macros</h2>
from layout primitives, which answer "where do my children go?".
</p>

<h3 style="margin-block-start: var(--sf-space-l)"><code>.sf-content-auto</code> β€” skip offscreen rendering</h3>
<p style="color: var(--sf-color-text--muted); font-size: var(--sf-text-s); max-inline-size: 60ch">
Sets <code>content-visibility: auto</code> so the browser skips layout and paint for this
section while it is offscreen on a long page. <code>contain-intrinsic-size</code> (token
<code>--sf-content-intrinsic-size</code>) reserves a placeholder box so the scrollbar
doesn't jump. Unsupported engines render it normally.
</p>
<section class="sf-content-auto" style="background: var(--sf-color-inset); padding: var(--sf-space-m); border-radius: var(--sf-radius-m); --sf-content-intrinsic-size: 200px">
<p>This block is render-deferred until it nears the viewport. On long product or article
pages, applying it to repeated sections measurably cuts initial render cost.</p>
</section>

<h3 style="margin-block-start: var(--sf-space-l)"><code>.sf-tabular-nums</code> β€” column-aligned figures</h3>
<p style="color: var(--sf-color-text--muted); font-size: var(--sf-text-s); max-inline-size: 60ch">
Fixed-width digits (<code>font-variant-numeric: var(--sf-font-numeric)</code>) so numbers
line up in columns β€” prices, totals, invoices.
</p>
<div class="sf-tabular-nums" style="background: var(--sf-color-inset); padding: var(--sf-space-s); border-radius: var(--sf-radius-s); max-inline-size: 12rem; text-align: end; line-height: 1.6">
<div>1,110.00</div>
<div>98.40</div>
<div>7,001.99</div>
</div>

<h3 style="margin-block-start: var(--sf-space-l)"><code>.sf-flow</code> β€” equal vertical rhythm</h3>
<div class="sf-flow" style="background: var(--sf-color-inset); padding: var(--sf-space-m); border-radius: var(--sf-radius-m); --sf-flow-space: var(--sf-space-s)">
<p>First child has no top margin (only siblings get the rhythm).</p>
Expand Down
53 changes: 53 additions & 0 deletions docs/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,59 @@ Just sets `-webkit-tap-highlight-color: transparent`. No tokens.

---

## `.sf-content-auto`

Skips rendering (layout + paint) for offscreen content until it scrolls
near the viewport β€” a large initial-render win on long pages (product
grids, long articles).

```html
<section class="sf-content-auto">…repeated long-page section…</section>

<!-- Override the reserved placeholder size -->
<section class="sf-content-auto" style="--sf-content-intrinsic-size: 800px">…</section>
```

Tokens:

| Token | Default | What it controls |
|---|---|---|
| `--sf-content-intrinsic-size` | `500px` | placeholder block size fed to `contain-intrinsic-size` |

Sets `content-visibility: auto` plus `contain-intrinsic-size: auto
var(--sf-content-intrinsic-size)`. The `auto` keyword caches each
section's last-rendered size; the token reserves space before first
render so the scrollbar and scroll position stay stable. Unsupported
engines (e.g. Safari < 18) ignore both declarations and render normally
β€” pure progressive enhancement.

> Deliberately not paired with `will-change`. `will-change` pre-creates
> compositing layers and usually *hurts* performance when applied
> broadly; set it from JS only while an element is actively animating.

---

## `.sf-tabular-nums`

Fixed-width digits so numbers align in vertical columns (price lists,
totals, invoices, dashboards).

```html
<table class="sf-tabular-nums">…numeric columns…</table>
```

Tokens:

| Token | Default | What it controls |
|---|---|---|
| `--sf-font-numeric` | `tabular-nums` | figure style (`core/tokens.css`) |

Sets `font-variant-numeric: var(--sf-font-numeric, tabular-nums)`. The
same token is applied to `<input type="number">` in `optional/forms.css`.
Universal browser support.

---

## `.sf-surface--*`

Contextual background + auto-contrast text color. Apply to any element
Expand Down
7 changes: 5 additions & 2 deletions docs/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"optional/theme-example.css"
],
"counts": {
"tokens": 805,
"sf_classes": 158,
"tokens": 806,
"sf_classes": 160,
"is_classes": 40
}
},
Expand Down Expand Up @@ -435,6 +435,7 @@
"--sf-container-prose",
"--sf-container-wide",
"--sf-content-gap",
"--sf-content-intrinsic-size",
"--sf-content-width",
"--sf-contrast-bias",
"--sf-contrast-threshold",
Expand Down Expand Up @@ -862,6 +863,7 @@
"sf-container--narrow",
"sf-container--prose",
"sf-container--wide",
"sf-content-auto",
"sf-content-grid",
"sf-cover",
"sf-cover--max",
Expand Down Expand Up @@ -989,6 +991,7 @@
"sf-switcher",
"sf-switcher--no-wrap",
"sf-switcher--vertical",
"sf-tabular-nums",
"sf-text-gradient",
"sf-truncate"
],
Expand Down
5 changes: 3 additions & 2 deletions docs/tokens.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> **Generated** from source by `scripts/gen-token-reference.js` β€”
> run `npm run docs:tokens` to refresh. Do not edit by hand.

**871 tokens.** Every `--sf-*` custom property and its default value, grouped by source file.
**872 tokens.** Every `--sf-*` custom property and its default value, grouped by source file.
Tokens defined in multiple files (e.g. shade aliases overridden in
`optional/tokens.palette.css`) are listed once per section β€” so this count is
higher than `docs/registry.json` (which deduplicates by name). See
Expand Down Expand Up @@ -580,11 +580,12 @@ rebrand workflow.

## Macro tokens (`core/tokens.macros.css`)

13 tokens.
14 tokens.

| Token | Default |
|---|---|
| `--sf-aspect` | `16 / 9` |
| `--sf-content-intrinsic-size` | `500px` |
| `--sf-flow-space` | `var(--sf-space-content)` |
| `--sf-line-clamp` | `3` |
| `--sf-prose-block-margin` | `var(--sf-space-m)` |
Expand Down
8 changes: 8 additions & 0 deletions plugins/SLASHED-for-WP/data/classes-hints.json
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,14 @@
"description": "Adds an external-link glyph after the link text. Opt-in per",
"category": "Macros"
},
"sf-content-auto": {
"description": "Skip rendering (layout + paint) for offscreen content until it",
"category": "Macros"
},
"sf-tabular-nums": {
"description": "Fixed-width digits so numbers align in vertical columns",
"category": "Macros"
},
"is-hidden": {
"description": "Hides the element (display: none). Toggled by JS or ARIA.",
"category": "States"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export const variableGroups = [
{ name: "--sf-ratio-{square,portrait,video,cinema,golden,3-2,4-3}", description: "Predefined aspect ratios for frames and containers." },
{ name: "--sf-aspect", description: "Generic aspect-ratio token read by the .sf-aspect macro (16/9)." },
{ name: "--sf-object-{fit,position}", description: "Default object-fit (cover) and object-position (50% 50%) for media elements." },
{ name: "--sf-content-intrinsic-size", description: "Placeholder size for the .sf-content-auto macro's contain-intrinsic-size (500px)." },
]
},
{
Expand Down Expand Up @@ -317,6 +318,8 @@ export const classGroups = [
{ name: ".sf-scroll-snap", description: "Vertical scroll-snap container." },
{ name: ".sf-overflow-fade", description: "End-edge horizontal fade for overflowing inline content." },
{ name: ".sf-no-tap-highlight", description: "Suppresses WebKit/Android tap highlight." },
{ name: ".sf-content-auto", description: "content-visibility:auto β€” skips rendering offscreen content (reads --sf-content-intrinsic-size)." },
{ name: ".sf-tabular-nums", description: "Fixed-width digits for column-aligned numbers (reads --sf-font-numeric)." },
{ name: ".sf-text-gradient", description: "Gradient text effect using background-clip." },
{ name: ".sf-clickable-parent / .sf-focus-parent", description: "Makes a whole card clickable via the first link inside; -focus mirrors focus ring to the parent." },
{ name: ".sf-link-external", description: "Adds external link indicator marker." },
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"_meta": {
"source": "source (core/ + optional/ token and class files)",
"counts": {
"variables": 805,
"sf_classes": 158,
"variables": 806,
"sf_classes": 160,
"is_classes": 40
}
},
Expand Down Expand Up @@ -417,6 +417,7 @@
"--sf-container-prose",
"--sf-container-wide",
"--sf-content-gap",
"--sf-content-intrinsic-size",
"--sf-content-width",
"--sf-contrast-bias",
"--sf-contrast-threshold",
Expand Down Expand Up @@ -844,6 +845,7 @@
"sf-container--narrow",
"sf-container--prose",
"sf-container--wide",
"sf-content-auto",
"sf-content-grid",
"sf-cover",
"sf-cover__center",
Expand Down Expand Up @@ -971,6 +973,7 @@
"sf-switcher",
"sf-switcher--no-wrap",
"sf-switcher--vertical",
"sf-tabular-nums",
"sf-text-gradient",
"sf-truncate"
],
Expand Down
1 change: 1 addition & 0 deletions tests/token-api.snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@
"--sf-container-prose",
"--sf-container-wide",
"--sf-content-gap",
"--sf-content-intrinsic-size",
"--sf-content-width",
"--sf-contrast-bias",
"--sf-contrast-threshold",
Expand Down