You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/rfcs/rfc-scaling.md

136 lines
8.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: RFC — The `scaling` axis (global zoom), separate from density
type: rfc
audience: human + agent
status: implemented
source: migrated from src/uix/eidos/SCALING_RFC.md (2026-07-02, docs-book F7.4)
---
# RFC — The `scaling` axis (global zoom), separate from density
> **Status: ✅ IMPLEMENTED.** (designed 2026-06-01 · implemented 2026-06-02)
> Born from P1-2: density-driven typography (`content-scale`) was wrongly framed.
> Density and zoom are different things; this RFC introduces zoom as its own axis.
>
> **Code**: `lib/config-types.ts` (`SCALING_KEYS` / `ScalingKey` / `DEFAULT_SCALING` /
> **`ScalingParticipationMap`** — the axis definition, added 2026-07-06),
> `lib/primitives/static.ts` (`STATIC_SCALING` + **`STATIC_SCALING_PARTICIPATION`**,
> the canonical per-family defaults), `lib/render-css.ts`
> (`appendScalingDeclarations` / `appendScaledMetricDeclarations` / `renderScalingBlocks`
> — the emitters consume the map, they never decide participation),
> `active-eidos.svelte.ts` (`scaling` / `scalingSource` / `getScaling()` / `data-scaling`).
> Doc consolidated in [`theming/reference.md`](../theming/reference.md) §23.
> `--density-scale` and `--density-content-scale` were removed in this sprint.
>
> **2026-07-06 — participation became model data.** Which families the zoom
> multiplies is now DECLARED per element (`primitives.scaling`, partial maps
> merge over the canonical defaults) instead of hardcoded in the emitters.
> This also reconciles an unrecorded drift: commit `845d6579` (2026-06-22)
> had made radius "scaling-responsive" against this RFC without updating any
> doc — radius participation is now canonically `false` (chrome family), and
> the original "as in Radix" justification below was corrected (Radix DOES
> scale its radius; crispness is our own rationale).
## 0. Motivation
`scaling` in Radix Themes (90/95/100/105/110%) is a **global zoom** that scales
space, typography, heights and line-box with ONE factor — accessibility/zoom, not
density. Eidos had a dead `--density-content-scale` (±4%) that tried to scale
typography under the density axis, which **conflates density with zoom**:
- **Density** (`compact` / `comfortable` / `spacious`): tightens layout (space +
control height), **text stays stable** (legibility — Material/Carbon). Already done.
- **Scaling** (this RFC): zoom — scales the px metrics **including typography**.
The two axes are **orthogonal** and **compose** (you can have `compact` + `110%`).
## 1. Decisions
- **Discrete**, Radix-class axis: `90 / 95 / 100 / 105 / 110` (%). Default `100`.
Projected as `[data-scaling='90']` (blocks, cacheable) + `--scaling`.
- **The axis is DEFINED by a per-element participation map** (user decision
2026-07-06): every orthogonal scale family declares whether the zoom
multiplies it — `ScalingParticipationMap` (`config-types.ts`), canonical
defaults in `STATIC_SCALING_PARTICIPATION` (`primitives/static.ts`). The
generator derives ALL emission from the map; participation is model data
(introspectable, guarded), never an emitter side effect. Canonical taxonomy:
- **METRIC families scale** (what things occupy): `space`,
`control-height`, `font-size`, `icon-size`, `blur` (blur joined
2026-06-15, §29).
- **CHROME families stay crisp** (what draws the edges): `radius`,
`border-width`, `shadow` — design-integer px under zoom.
- Excluded outright: `line-height` (a **unitless ratio**; it already
scales via font-size — multiplying it would **double** the effect),
`z-index`, `opacity`, `motion` (durations/eases; `motion-distance`
declares `false` in the map).
- **Corrected reference note**: Radix Themes DOES scale its radius
(`calc(base × scaling × radius-factor)`). Keeping radius crisp is OUR
decision on our own rationale (chrome crispness + canonical axis
semantics), not Radix parity. Radius keeps its own magnitude knob
(`--radius-factor`) — roundness is themeable; zoom participation is a
system-definition decision (config → regeneration), not a runtime theme
knob.
- Map boundaries: it governs DIRECT multiplication of raw px only — a
value referencing a participating token (a gap on `var(--space-*)`)
scales through the reference; `shadow` is declared for completeness but
is structurally fixed (composite values can't wrap in one `calc()`).
- **Composition with density** (multiplicative) on the 2 shared metrics:
`--space-N: calc(base × var(--density-space-scale) × var(--scaling))`,
`--control-height-N: calc(base × var(--density-control-scale) × var(--scaling))`.
- **Prune** `--density-scale` (master) + `--density-content-scale`: they were the
half-baked attempt at what this axis does properly. Reconciles §20.1 (density =
stable text; scaling = zoom includes text).
## 2. Design
**Primitive** (`lib/primitives/static.ts`):
```ts
export const STATIC_SCALING = { '90': 0.9, '95': 0.95, '100': 1, '105': 1.05, '110': 1.1 };
```
Active default: `100`.
**Types** (`config-types.ts`): `SCALING_KEYS = ['90','95','100','105','110']`,
`ScalingKey`. The density primitive loses `scale` + `contentScale`.
**Emission** (`render-css.ts`):
- `:root` declares `--scaling-90: 0.9 … --scaling-110: 1.1` (constants) and
`--scaling: var(--scaling-100)` (active).
- Blocks `[data-scaling='90'] { --scaling: var(--scaling-90); }` … (the ≠100 ones).
- `font-size` + `icon-size` move from verbatim to `calc(value × var(--scaling))`.
- `space` + `control-height` add `× var(--scaling)` to the `calc` they already have.
- `line-height`, `radius`, `border`, `shadow`, etc.: **unchanged**.
**Runtime** (`active-eidos.svelte.ts`): new `scalingSource` (get/onChange,
mirror of `densitySource`); `apply()` writes `data-scaling` on the host (next to
`data-mode` / `data-density`); `dispose()` cleans it up. `EIDOS_SCALING_ATTR`.
**Contract** (`contract.ts`): emits `--scaling` + the constants as knobs.
## 3. Conflicts (resolved)
| Point | Resolution |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Density × scaling on space/control-height | Multiplicative composition in the `calc`. Orthogonal in intent. |
| line-height (ratio) | Do NOT scale — scales via font-size. |
| radius/border/shadow/z/opacity/motion | Do NOT scale — chrome/structural families per the participation map. (The original "Radix parity" claim was wrong: Radix scales its radius; this is our own crispness decision.) |
| Dead `--density-scale` + `content-scale` | Pruned — superseded by this axis. |
| `size` prop | Orthogonal — `size` picks the tier, scaling multiplies the tier's value. Composes. |
| Browser zoom | Composes multiplicatively (both px). App-level, distinct. |
| §20.1 (typography doesn't scale with density) | Still true — density doesn't scale it; **scaling does**. Two axes. |
## 4. Implementation plan
1. `STATIC_SCALING` + remove `scale`/`contentScale` from `STATIC_DENSITY`.
2. Types (`SCALING_KEYS`/`ScalingKey`; adjust `DensityPrimitiveSet`).
3. `render-css`: scaling emission + blocks + `× var(--scaling)` on the 4 families;
prune density scale/content-scale emission.
4. `ActiveEidos`: `scalingSource` + `data-scaling` + cleanup.
5. `contract`: scaling tokens; remove density scale/content-scale.
6. Regenerate `generated/base.css`.
7. Tests: update the shape of `--space-4`/`--control-height` (× scaling); scaled
font-size test; `[data-scaling]` block; `scalingSource` test.
8. Verify (`check` + `vitest src/uix/eidos` + browser) + doc in theming reference.

Powered by TurnKey Linux.