diff --git a/apps/website/content/docs/theming/token-reference.mdx b/apps/website/content/docs/theming/token-reference.mdx index 81753246..a1de068e 100644 --- a/apps/website/content/docs/theming/token-reference.mdx +++ b/apps/website/content/docs/theming/token-reference.mdx @@ -1,13 +1,13 @@ --- title: Token reference -description: "The 24-token --pretable-* surface, with shape, default, and purpose for each." +description: "The 34-token --pretable-* surface, with shape, default, and purpose for each." nav: Theming order: 8 --- -Pretable's public theming surface is 24 CSS variables, all `--pretable-*` prefixed. Each theme defines all 24 at `:root`. Excel and Material 3 ship preset values; consumers override individual tokens at `:root` in their own stylesheet (see [Override tokens](/docs/theming/override-tokens)). +Pretable's public theming surface is 34 CSS variables, all `--pretable-*` prefixed. Each theme defines all 34 at `:root`. Excel and Material 3 ship preset values; consumers override individual tokens at `:root` in their own stylesheet (see [Override tokens](/docs/theming/override-tokens)). -> Two tokens — `--pretable-row-height` and `--pretable-header-height` — are read by the engine in JavaScript via the `useResolvedHeights` hook. The other 22 are CSS-only. +> Two tokens — `--pretable-row-height` and `--pretable-header-height` — are read by the engine in JavaScript via the `useResolvedHeights` hook. The other 32 are CSS-only. ## Surfaces (5) @@ -72,6 +72,23 @@ The first two are read by the engine in JavaScript. The other four are CSS-only. | `--pretable-font-sans` | Primary sans-serif family stack | font | `"Aptos Narrow", "Aptos", "Segoe UI", -apple-system, …` | `"Roboto Flex", "Roboto", system-ui, …` | | `--pretable-font-mono` | Monospace family stack (numeric cells, code) | font | `ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace` | `"Roboto Mono", ui-monospace, monospace` | +## Grid controls (10) + +These tokens control the visual appearance of interactive grid elements: row-selection checkboxes, cell-range selection overlays, column resize handles, and column reorder drag-and-drop indicators. Most derive from the theme's `--pretable-accent`, `--pretable-bg-grid`, `--pretable-rule-strong`, and `--pretable-bg-header`, so overriding those upstream tokens recolors the controls coherently; `--pretable-checkbox-checked-fg` and `--pretable-reorder-ghost-shadow` are standalone constants (`#fff` and a box-shadow value) that you must override explicitly if needed. + +| Token | Description | Type | Excel | Material 3 (light) | +| ----------------------------------- | ------------------------------------------------------------ | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `--pretable-selection-bg` | Range-selection overlay background (`[aria-selected]` cells) | color | `color-mix(in srgb, var(--pretable-accent) 8%, transparent)` | `color-mix(in srgb, var(--pretable-accent) 8%, transparent)` | +| `--pretable-checkbox-bg` | Row-select checkbox background | color | `var(--pretable-bg-grid)` | `var(--pretable-bg-grid)` | +| `--pretable-checkbox-border` | Row-select checkbox border | color | `var(--pretable-rule-strong)` | `var(--pretable-rule-strong)` | +| `--pretable-checkbox-checked-bg` | Row-select checkbox background when checked | color | `var(--pretable-accent)` | `var(--pretable-accent)` | +| `--pretable-checkbox-checked-fg` | Row-select checkmark color | color | `#fff` | `#fff` | +| `--pretable-resize-handle` | Column resize handle (idle) | color | `transparent` | `transparent` | +| `--pretable-resize-handle-hover` | Column resize handle (hover/dragging) | color | `var(--pretable-accent)` | `var(--pretable-accent)` | +| `--pretable-reorder-ghost-bg` | Column drag ghost background | color | `var(--pretable-bg-header)` | `var(--pretable-bg-header)` | +| `--pretable-reorder-ghost-shadow` | Column drag ghost shadow | shadow | `0 4px 12px rgb(0 0 0 / 0.12)` | `0 4px 12px rgb(0 0 0 / 0.12)` | +| `--pretable-reorder-drop-indicator` | Column drop-position indicator | color | `var(--pretable-accent)` | `var(--pretable-accent)` | + ## Stability `@pretable/ui` is at version 0.0.x. Token names may rename or remove in any patch release. Each release's `CHANGELOG.md` describes the deltas. Override at your own risk; the contract solidifies post-1.0. diff --git a/docs/superpowers/plans/2026-06-07-grid-token-consolidation.md b/docs/superpowers/plans/2026-06-07-grid-token-consolidation.md new file mode 100644 index 00000000..7447a0bf --- /dev/null +++ b/docs/superpowers/plans/2026-06-07-grid-token-consolidation.md @@ -0,0 +1,310 @@ +# Grid Token Consolidation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Move the 11 grid-control colors `grid.css` depends on out of the undocumented `--pt-color-*` namespace into the documented `--pretable-*` theme contract (theme-derived), so the documented `theme.css + grid.css` recipe yields a fully-themed grid. + +**Architecture:** Define 10 new `--pretable-*` grid-control tokens in each theme (derived from that theme's existing tokens), repoint `grid.css` to them (deduping `--pt-color-focus-ring` onto the existing `--pretable-focus-ring`), delete the dead `--pt-color-*` from `tokens.css`, and extend `contract.test.ts` so the contract stays complete. Pre-1.0, no backcompat. + +**Tech Stack:** CSS custom properties + `color-mix`, vitest (jsdom) for `@pretable/ui`, Next.js MDX docs. + +**Spec:** `docs/superpowers/specs/2026-06-07-grid-token-consolidation-design.md` + +**The 10 new tokens** (each defined in both themes, derived): + +```css +--pretable-selection-bg: color-mix( + in srgb, + var(--pretable-accent) 8%, + transparent +); +--pretable-checkbox-bg: var(--pretable-bg-grid); +--pretable-checkbox-border: var(--pretable-rule-strong); +--pretable-checkbox-checked-bg: var(--pretable-accent); +--pretable-checkbox-checked-fg: #fff; +--pretable-resize-handle: transparent; +--pretable-resize-handle-hover: var(--pretable-accent); +--pretable-reorder-ghost-bg: var(--pretable-bg-header); +--pretable-reorder-ghost-shadow: 0 4px 12px rgb(0 0 0 / 0.12); +--pretable-reorder-drop-indicator: var(--pretable-accent); +``` + +(`--pt-color-focus-ring` is NOT replaced by a new token — it dedupes onto the existing `--pretable-focus-ring`.) + +--- + +### Task 1: Define the grid-control tokens in both themes (test-first via contract.test.ts) + +**Files:** + +- Modify: `packages/ui/src/__tests__/contract.test.ts` (the `TOKENS` array, ~lines 5-30) +- Modify: `packages/ui/src/themes/material.css` (`:root` block) +- Modify: `packages/ui/src/themes/excel.css` (`:root` block) + +- [ ] **Step 1: Extend the `TOKENS` list (makes the existing contract test fail)** + +In `packages/ui/src/__tests__/contract.test.ts`, the `TOKENS` array lists the 24 existing token names (without the `--` prefix), e.g. `"pretable-bg-grid"`. Append these 10: + +```ts + "pretable-selection-bg", + "pretable-checkbox-bg", + "pretable-checkbox-border", + "pretable-checkbox-checked-bg", + "pretable-checkbox-checked-fg", + "pretable-resize-handle", + "pretable-resize-handle-hover", + "pretable-reorder-ghost-bg", + "pretable-reorder-ghost-shadow", + "pretable-reorder-drop-indicator", +``` + +(Add them inside the `const TOKENS = [ … ]` array, before the closing `];`.) + +- [ ] **Step 2: Run the contract test to verify it fails** + +Run: `pnpm --filter @pretable/ui exec vitest run src/__tests__/contract.test.ts` +Expected: FAIL — `excel.css: --pretable-selection-bg is empty` (and the other 9) for both themes, because the themes don't define them yet. + +- [ ] **Step 3: Add the 10 derived tokens to each theme's `:root`** + +In `packages/ui/src/themes/material.css`, inside the `:root { … }` block (e.g. right after the `/* Accent */` group), add: + +```css +/* Grid controls — derived from this theme's tokens so they recolor coherently */ +--pretable-selection-bg: color-mix( + in srgb, + var(--pretable-accent) 8%, + transparent +); +--pretable-checkbox-bg: var(--pretable-bg-grid); +--pretable-checkbox-border: var(--pretable-rule-strong); +--pretable-checkbox-checked-bg: var(--pretable-accent); +--pretable-checkbox-checked-fg: #fff; +--pretable-resize-handle: transparent; +--pretable-resize-handle-hover: var(--pretable-accent); +--pretable-reorder-ghost-bg: var(--pretable-bg-header); +--pretable-reorder-ghost-shadow: 0 4px 12px rgb(0 0 0 / 0.12); +--pretable-reorder-drop-indicator: var(--pretable-accent); +``` + +Add the **identical block** to `packages/ui/src/themes/excel.css`'s `:root { … }` block. Do NOT add them to Material's `[data-theme="dark"]` block — they derive from tokens the dark block already overrides, so they adapt automatically. + +- [ ] **Step 4: Run the contract test + full ui suite to verify pass** + +Run: `pnpm --filter @pretable/ui exec vitest run` +Expected: PASS — `contract.test.ts` now confirms both themes define all 34 tokens (incl. dark mode + density tiers for the pre-existing ones); `density.test.ts`/`css-cascade.test.ts`/`build-config.test.ts` unaffected. + +Run: `pnpm exec prettier --write packages/ui/src/themes/material.css packages/ui/src/themes/excel.css packages/ui/src/__tests__/contract.test.ts` + +- [ ] **Step 5: Commit** + +```bash +git add packages/ui/src/themes/material.css packages/ui/src/themes/excel.css packages/ui/src/__tests__/contract.test.ts +git commit -m "feat(ui): add grid-control tokens to the --pretable-* theme contract + +Defines 10 grid-control tokens (checkbox/selection/resize/reorder) in both themes, +derived from each theme's accent/bg/rule so they recolor coherently and adapt to +dark mode. Extends contract.test.ts so every theme must define them." +``` + +--- + +### Task 2: Repoint grid.css to the new tokens + guard the consolidation + +**Files:** + +- Modify: `packages/ui/src/grid.css` (the 16 `var(--pt-color-*)` references) +- Modify: `packages/ui/src/__tests__/contract.test.ts` (var-resolution test → both themes; new no-`--pt-color-` assertion) + +- [ ] **Step 1: Strengthen the tests first (they should fail against the current grid.css)** + +In `packages/ui/src/__tests__/contract.test.ts`: + +(a) Add a structural assertion that `grid.css` no longer references the old namespace. Add this test inside the `describe("token contract", …)` block: + +```ts +test("grid.css references no --pt-color-* tokens (consolidated into --pretable-*)", () => { + const gridCss = fs.readFileSync(GRID_CSS, "utf8"); + const stale = [...gridCss.matchAll(/var\((--pt-color-[a-z-]+)/g)].map( + (m) => m[1], + ); + expect(stale, `grid.css still references ${stale.join(", ")}`).toEqual([]); +}); +``` + +(b) The existing test "grid.css has no unresolved var(--pretable-\*) references when excel.css is loaded" loads only `excel.css`. Generalize it to BOTH themes. Replace that single test with a loop: + +```ts +for (const themeFile of ["excel.css", "material.css"]) { + test(`grid.css has no unresolved var(--pretable-*) refs under ${themeFile}`, () => { + const themeCleanup = loadCSS(path.join(THEMES_DIR, themeFile)); + const gridCss = fs.readFileSync(GRID_CSS, "utf8"); + const refs = new Set( + Array.from(gridCss.matchAll(/var\((--pretable-[a-z-]+)/g)).map( + (m) => m[1], + ), + ); + expect( + refs.size, + "grid.css references zero --pretable-* vars; this is suspicious", + ).toBeGreaterThan(0); + const computed = getComputedStyle(document.documentElement); + for (const ref of refs) { + expect( + computed.getPropertyValue(ref).trim(), + `grid.css references unresolved ${ref} under ${themeFile}`, + ).not.toBe(""); + } + themeCleanup(); + }); +} +``` + +- [ ] **Step 2: Run to verify the new tests fail** + +Run: `pnpm --filter @pretable/ui exec vitest run src/__tests__/contract.test.ts` +Expected: FAIL — the no-`--pt-color-` test reports grid.css still references `--pt-color-checkbox-bg` etc.; and the `--pretable-*` resolution test fails on `--pretable-selection-bg`/etc. being unresolved (grid.css still uses the old names, which aren't `--pretable-*`). (The new `--pretable-*` grid tokens ARE defined by the themes from Task 1, so once grid.css is repointed they resolve.) + +- [ ] **Step 3: Repoint grid.css (single rename — the new names mirror the old suffixes)** + +The 10 new tokens are `--pretable-` mirroring `--pt-color-`, and `--pt-color-focus-ring` maps to the existing `--pretable-focus-ring`, so one substitution does it all: + +```bash +sed -i '' 's/--pt-color-/--pretable-/g' packages/ui/src/grid.css +``` + +(`sed -i ''` is macOS/BSD; on Linux use `sed -i`.) Verify: `grep -n 'pt-color' packages/ui/src/grid.css` → nothing. Skim the diff — every change should be a `var(--pt-color-X)` → `var(--pretable-X)` inside the existing `@layer pretable { :where(…) }` rules; nothing else. + +- [ ] **Step 4: Run the ui suite to verify pass** + +Run: `pnpm --filter @pretable/ui exec vitest run` +Expected: PASS — both new/generalized tests pass (grid.css has no `--pt-color-`; all its `--pretable-*` refs resolve under excel AND material), and the existing suite stays green. + +Run: `pnpm exec prettier --write packages/ui/src/grid.css packages/ui/src/__tests__/contract.test.ts` + +- [ ] **Step 5: Commit** + +```bash +git add packages/ui/src/grid.css packages/ui/src/__tests__/contract.test.ts +git commit -m "feat(ui): repoint grid.css to the --pretable-* grid-control tokens + +Renames the 16 var(--pt-color-*) refs to --pretable-* (focus-ring dedupes onto +the existing --pretable-focus-ring). Adds a guard that grid.css references no +--pt-color-*, and verifies grid.css's --pretable-* deps resolve under BOTH themes +(was excel-only) — proving the documented recipe yields a fully-themed grid." +``` + +--- + +### Task 3: Delete the dead grid-control tokens from tokens.css + +**Files:** + +- Modify: `packages/ui/src/tokens.css` (remove the grid-control `--pt-color-*` declarations) + +- [ ] **Step 1: Confirm no remaining consumers** + +Run: `grep -rn 'pt-color-\(selection\|focus-ring\|resize\|reorder\|checkbox\)' packages apps --include='*.css' --include='*.ts' --include='*.tsx' | grep -v node_modules | grep -v dist` +Expected: only `packages/ui/src/tokens.css` (the declarations themselves). If `grid.css` appears, Task 2 was not completed — stop. (The only other `--pt-color-*` consumer is `heroGrid.module.css` via `--pt-color-warning`, which this grep does not match and must stay.) + +- [ ] **Step 2: Remove the dead declarations** + +In `packages/ui/src/tokens.css`, delete these four comment groups and their declarations entirely: + +```css +/* Cell-range selection (Phase 3 — derived from accent) */ +--pt-color-selection-bg: rgb(234 88 12 / 0.08); +--pt-color-selection-border: rgb(234 88 12 / 0.6); +--pt-color-focus-ring: var(--pt-accent); + +/* Column resize handle (sub-project C) */ +--pt-color-resize-handle: transparent; +--pt-color-resize-handle-hover: var(--pt-color-selection-border); + +/* Column reorder ghost + drop indicator (sub-project C) */ +--pt-color-reorder-ghost-bg: var(--pt-color-surface, #fff); +--pt-color-reorder-ghost-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); +--pt-color-reorder-drop-indicator: var(--pt-color-focus-ring); + +/* Row-selection checkbox column (Phase 4) */ +--pt-color-checkbox-bg: #fff; +--pt-color-checkbox-border: #d1d5db; +--pt-color-checkbox-checked-bg: var(--pt-accent); +--pt-color-checkbox-checked-fg: #fff; +``` + +Leave everything else in `tokens.css` untouched (Alpenglow palette, type scale, layout tokens, and any `--pt-color-*` NOT in the list above, e.g. `--pt-color-warning` if present). + +- [ ] **Step 3: Verify** + +Run: `grep -nE 'pt-color-(selection|focus-ring|resize|reorder|checkbox)' packages/ui/src/tokens.css` → nothing. +Run: `pnpm --filter @pretable/ui exec vitest run` +Expected: PASS (the grid-control tokens now come from the themes; tokens.css no longer needs them). + +Run: `pnpm exec prettier --write packages/ui/src/tokens.css` + +- [ ] **Step 4: Commit** + +```bash +git add packages/ui/src/tokens.css +git commit -m "chore(ui): drop dead grid-control tokens from tokens.css + +The grid-control --pt-color-* (checkbox/selection/resize/reorder/focus-ring) now +live in the --pretable-* theme contract; remove their now-unreferenced +declarations from the Alpenglow palette file. Non-grid --pt-color-* (e.g. warning) +are untouched." +``` + +--- + +### Task 4: Document the grid-control tokens + +**Files:** + +- Modify: `apps/website/content/docs/theming/token-reference.mdx` + +- [ ] **Step 1: Add the grid-control tokens to the reference table** + +`apps/website/content/docs/theming/token-reference.mdx` documents the token contract in a table (the "Full 24-token table"). Read it first to match its column structure (likely: token | description | example value), then add a "Grid controls" group of 10 rows for the new tokens. Use these descriptions: + +| Token | Description | +| ----------------------------------- | ------------------------------------------------------------ | +| `--pretable-selection-bg` | Range-selection overlay background (`[aria-selected]` cells) | +| `--pretable-checkbox-bg` | Row-select checkbox background | +| `--pretable-checkbox-border` | Row-select checkbox border | +| `--pretable-checkbox-checked-bg` | Row-select checkbox background when checked | +| `--pretable-checkbox-checked-fg` | Row-select checkmark color | +| `--pretable-resize-handle` | Column resize handle (idle) | +| `--pretable-resize-handle-hover` | Column resize handle (hover/dragging) | +| `--pretable-reorder-ghost-bg` | Column drag ghost background | +| `--pretable-reorder-ghost-shadow` | Column drag ghost shadow | +| `--pretable-reorder-drop-indicator` | Column drop-position indicator | + +Note in the prose that these derive from the theme's `--pretable-accent`/`-bg-grid`/`-rule-strong` by default, so overriding those recolors the controls coherently. If the doc states a token count ("24 tokens"), update it to 34. + +- [ ] **Step 2: Format + run website docs-validation tests** + +Run: `pnpm exec prettier --write apps/website/content/docs/theming/token-reference.mdx` +Run: `pnpm --filter @pretable/app-website exec vitest run lib/docs app/llms.txt` +Expected: PASS (21 tests). If it errors on `@pretable/core` resolution, run `pnpm install --frozen-lockfile` first (stale local link), then re-run. + +- [ ] **Step 3: Commit** + +```bash +git add apps/website/content/docs/theming/token-reference.mdx +git commit -m "docs(theming): document the 10 grid-control tokens in the token reference" +``` + +--- + +## Final verification (after all tasks) + +- [ ] `pnpm --filter @pretable/ui exec vitest run` → pass (contract 34 tokens both themes, both-theme grid.css resolution, no-`--pt-color-` guard, css-cascade, density, build-config). +- [ ] `grep -rnE 'var\(--pt-color-(selection|focus-ring|resize|reorder|checkbox)' packages apps --include='*.css' | grep -v node_modules | grep -v dist` → nothing (no grid-control `--pt-color-*` consumed anywhere). +- [ ] `pnpm --filter @pretable/app-website exec vitest run lib/docs app/llms.txt` → 21 pass. +- [ ] Optional visual sanity: `pnpm bench:matrix --adapters=pretable --scenarios=S2 --scripts=initial --scale=dev --repeats=1` completes (the bench grid now resolves its control colors from excel.css; previously undefined). +- [ ] Open a PR; let CI (test/typecheck/lint/format/build/packaging/api-report) gate the merge. + +## Out of scope (do NOT implement here) + +Brand-primitive derivation layer; dark mode for Excel; Tailwind bridge aliases for the grid-control tokens. diff --git a/docs/superpowers/specs/2026-06-07-grid-token-consolidation-design.md b/docs/superpowers/specs/2026-06-07-grid-token-consolidation-design.md new file mode 100644 index 00000000..ba13e9ea --- /dev/null +++ b/docs/superpowers/specs/2026-06-07-grid-token-consolidation-design.md @@ -0,0 +1,90 @@ +# Grid Token Consolidation — Design + +**Date:** 2026-06-07 +**Status:** Approved (pending spec review) +**Scope:** Move the grid-control colors that `grid.css` depends on out of the undocumented `--pt-color-*` namespace into the documented `--pretable-*` theme contract, so the documented consumer recipe (`theme.css` + `grid.css`) yields a fully-themed grid. Fixes a real contract bug and delivers the "one complete token set to brand against" goal. + +## Problem + +`packages/ui/src/grid.css` consumes **two** token namespaces: + +- `--pretable-*` — the documented 24-token theme contract, defined by each theme file (`material.css`, `excel.css`) and verified by `contract.test.ts`. +- `--pt-color-*` — 11 grid-control colors (checkbox ×4, selection overlay, focus-ring, resize ×2, reorder ×3) that are **defined only in `packages/ui/src/tokens.css`** (the Alpenglow website palette), are **not in any theme file**, **not in the documented contract**, and **not imported by the documented recipe**. + +The documented consumer recipe (per `grid.css`'s own header) is `@import "@pretable/ui/themes/.css"; @import "@pretable/ui/grid.css";`. It does NOT import `tokens.css`. So a consumer following the recipe gets a grid where the checkbox is unstyled, range selection has no background, the resize handle is invisible, and the reorder ghost is broken — all 11 `--pt-color-*` are undefined. **Verified:** the bench app (`apps/bench/src/app.css`) imports `excel.css` + `grid.css` but not `tokens.css`, and its `cool-slate-tokens.css` defines zero `--pt-color-*` — so the bench grid's controls are already running on undefined values. Only the **website** works, by accident, because it also imports `tokens.css`. + +pretable is pre-1.0 with no external consumers (`feedback_no_backcompat`), so the token namespace can change freely — this is the moment to make the grid's full styling contract coherent and documented. + +## Goal + +Every color `grid.css` needs is a documented `--pretable-*` token defined by each theme. A consumer importing only a theme + `grid.css` gets a fully, correctly themed grid. Because the new tokens derive from the theme's existing `--pretable-*` (accent, rule, bg-grid, …), recoloring those few cascades coherently to the grid controls — the real payoff behind the "brand alias layer" idea, grounded in a real contract instead of a speculative derivation layer. + +## Design + +### 1. Consolidated token set (10 new + 1 dedupe) + +Rename, in `grid.css`, the `--pt-color-*` references to new `--pretable-*` tokens: + +| New `--pretable-*` token | Replaces `--pt-color-*` | +| ----------------------------------- | ------------------------ | +| `--pretable-selection-bg` | `selection-bg` | +| `--pretable-checkbox-bg` | `checkbox-bg` | +| `--pretable-checkbox-border` | `checkbox-border` | +| `--pretable-checkbox-checked-bg` | `checkbox-checked-bg` | +| `--pretable-checkbox-checked-fg` | `checkbox-checked-fg` | +| `--pretable-resize-handle` | `resize-handle` | +| `--pretable-resize-handle-hover` | `resize-handle-hover` | +| `--pretable-reorder-ghost-bg` | `reorder-ghost-bg` | +| `--pretable-reorder-ghost-shadow` | `reorder-ghost-shadow` | +| `--pretable-reorder-drop-indicator` | `reorder-drop-indicator` | + +**Dedupe (no new token):** `--pt-color-focus-ring` (used by the `[role="gridcell"]` focus rule) → reuse the existing `--pretable-focus-ring`. The duplicate is dropped. + +`--pretable-selection-bg` (range / `[role="gridcell"][aria-selected]` overlay) is kept **distinct** from the existing `--pretable-bg-selected` (themed `[data-pretable-selected]` cell fill) — they style different selectors/states; collapsing them would change the rendered selection appearance. + +Net: the documented contract grows **24 → 34** `--pretable-*` tokens; `--pt-color-*` disappears from `grid.css`'s dependency graph. + +### 2. Definition, rename, and cleanup + +**Define the 10 in each theme** (`material.css`, `excel.css`), derived from that theme's existing `--pretable-*` so they stay coherent and auto-adapt (incl. Material's `[data-theme="dark"]` block, since they reference tokens the dark block already overrides): + +```css +--pretable-selection-bg: color-mix( + in srgb, + var(--pretable-accent) 8%, + transparent +); +--pretable-checkbox-bg: var(--pretable-bg-grid); +--pretable-checkbox-border: var(--pretable-rule-strong); +--pretable-checkbox-checked-bg: var(--pretable-accent); +--pretable-checkbox-checked-fg: #fff; +--pretable-resize-handle: transparent; +--pretable-resize-handle-hover: var(--pretable-accent); +--pretable-reorder-ghost-bg: var(--pretable-bg-header); +--pretable-reorder-ghost-shadow: 0 4px 12px rgb(0 0 0 / 0.12); +--pretable-reorder-drop-indicator: var(--pretable-accent); +``` + +Add them once to each theme's base `:root` block (they derive, so no per-density or per-dark duplication is required). `color-mix` is baseline ~2023, consistent with the `@layer`/`:where()` modernity from #160. + +**`grid.css`:** rename the 16 `var(--pt-color-*)` references to the new `--pretable-*` names; the `[role="gridcell"]` focus rule switches to `--pretable-focus-ring`. Keep the `@layer pretable { :where(…) }` wrapping (#160) and the `data-pretable-*` selectors (#169) intact. + +**`tokens.css` cleanup:** remove the now-dead grid-control `--pt-color-*` declarations — the 11 that `grid.css` referenced plus `--pt-color-selection-border` (which only fed the old `resize-handle-hover` derivation and is unreferenced once that repoints to `--pretable-accent`): `selection-bg`, `selection-border`, `focus-ring`, `resize-handle`, `resize-handle-hover`, `reorder-ghost-bg`, `reorder-ghost-shadow`, `reorder-drop-indicator`, `checkbox-bg`, `checkbox-border`, `checkbox-checked-bg`, `checkbox-checked-fg`. **Safe:** the only other `--pt-color-*` consumer is `apps/website/app/components/heroGrid/heroGrid.module.css`, which uses `--pt-color-warning` (unrelated) — leave that and any other non-grid `--pt-color-*` untouched. + +**`tailwind.css` bridge:** unchanged. The 10 are grid-internal controls, not consumer surface/text colors, so they don't need Tailwind utility aliases (YAGNI). + +### 3. The dedupe detail + +`tokens.css` currently has internal derivations among the `--pt-color-*` set (e.g. `resize-handle-hover: var(--pt-color-selection-border)`, `reorder-drop-indicator: var(--pt-color-focus-ring)`). After consolidation those derivations are re-expressed against `--pretable-*` in the theme files (e.g. `resize-handle-hover: var(--pretable-accent)`), so no `--pt-color-*` remains referenced by the grid. + +## Testing + +1. **Extend `contract.test.ts` `TOKENS`** with the 10 new tokens → the existing "every theme defines every token at `:root`" test now guarantees `material.css` and `excel.css` (and Material dark) define them. A theme missing one fails CI. This is the direct fix that keeps the contract complete. +2. **Run the grid.css var-resolution test against BOTH themes.** `contract.test.ts` already asserts "grid.css has no unresolved `var(--pretable-*)` references when excel.css is loaded"; after consolidation that automatically covers the grid-control tokens. Extend it to also load `material.css` and assert the same — proving each theme satisfies grid.css's full dependency set (the precise guard that the documented recipe yields a fully-themed grid). +3. **New assertion: `grid.css` contains no `--pt-color-*`** (read the file as text; assert no match). Locks the consolidation against a future reintroduction of the split namespace — mirrors the structural guards from #160/#169. + +**Expected to stay green:** `css-cascade.test.ts` (cascade structure unchanged — this touches token names/values, not `@layer`/`:where()`), the density test (these aren't density tokens), `api:check` (no exported TS symbols change). The bench renders correct grid-control colors for free (previously undefined) — no bench code change. + +## Out of scope (tracked separately) + +Brand-primitive derivation layer (map ~6 inputs → the full set) — now largely unnecessary, since deriving the grid-control tokens from `--pretable-accent`/`-bg-grid`/etc. already gives consumers the "recolor a few, get coherent controls" behavior. Dark mode for Excel. diff --git a/packages/ui/src/__tests__/contract.test.ts b/packages/ui/src/__tests__/contract.test.ts index 1fc045f9..711705a8 100644 --- a/packages/ui/src/__tests__/contract.test.ts +++ b/packages/ui/src/__tests__/contract.test.ts @@ -27,6 +27,16 @@ const TOKENS = [ "pretable-font-size-header", "pretable-font-sans", "pretable-font-mono", + "pretable-selection-bg", + "pretable-checkbox-bg", + "pretable-checkbox-border", + "pretable-checkbox-checked-bg", + "pretable-checkbox-checked-fg", + "pretable-resize-handle", + "pretable-resize-handle-hover", + "pretable-reorder-ghost-bg", + "pretable-reorder-ghost-shadow", + "pretable-reorder-drop-indicator", ]; const THEMES_DIR = path.resolve(__dirname, "../themes"); @@ -95,25 +105,35 @@ describe("token contract", () => { cleanup(); }); - test("grid.css has no unresolved var(--pretable-*) references when excel.css is loaded", () => { - const themeCleanup = loadCSS(path.join(THEMES_DIR, "excel.css")); + test("grid.css references no --pt-color-* tokens (consolidated into --pretable-*)", () => { const gridCss = fs.readFileSync(GRID_CSS, "utf8"); - const refs = new Set( - Array.from(gridCss.matchAll(/var\((--pretable-[a-z-]+)/g)).map( - (m) => m[1], - ), + const stale = [...gridCss.matchAll(/var\((--pt-color-[a-z-]+)/g)].map( + (m) => m[1], ); - expect( - refs.size, - "grid.css references zero --pretable-* vars; this is suspicious", - ).toBeGreaterThan(0); - const computed = getComputedStyle(document.documentElement); - for (const ref of refs) { - expect( - computed.getPropertyValue(ref).trim(), - `grid.css references unresolved ${ref}`, - ).not.toBe(""); - } - themeCleanup(); + expect(stale, `grid.css still references ${stale.join(", ")}`).toEqual([]); }); + + for (const themeFile of ["excel.css", "material.css"]) { + test(`grid.css has no unresolved var(--pretable-*) refs under ${themeFile}`, () => { + const themeCleanup = loadCSS(path.join(THEMES_DIR, themeFile)); + const gridCss = fs.readFileSync(GRID_CSS, "utf8"); + const refs = new Set( + Array.from(gridCss.matchAll(/var\((--pretable-[a-z-]+)/g)).map( + (m) => m[1], + ), + ); + expect( + refs.size, + "grid.css references zero --pretable-* vars; this is suspicious", + ).toBeGreaterThan(0); + const computed = getComputedStyle(document.documentElement); + for (const ref of refs) { + expect( + computed.getPropertyValue(ref).trim(), + `grid.css references unresolved ${ref} under ${themeFile}`, + ).not.toBe(""); + } + themeCleanup(); + }); + } }); diff --git a/packages/ui/src/grid.css b/packages/ui/src/grid.css index 6358ce4f..0a6144fd 100644 --- a/packages/ui/src/grid.css +++ b/packages/ui/src/grid.css @@ -111,11 +111,11 @@ * a usable visual. */ :where([role="gridcell"][aria-selected="true"]) { - background: var(--pt-color-selection-bg); + background: var(--pretable-selection-bg); } :where([role="gridcell"][data-pretable-focused="true"]) { - box-shadow: inset 0 0 0 2px var(--pt-color-focus-ring); + box-shadow: inset 0 0 0 2px var(--pretable-focus-ring); } /* Numeric cells (opt-in via [data-pretable-numeric="true"]) */ @@ -150,8 +150,8 @@ ) { width: 16px; height: 16px; - border: 1px solid var(--pt-color-checkbox-border); - background: var(--pt-color-checkbox-bg); + border: 1px solid var(--pretable-checkbox-border); + background: var(--pretable-checkbox-bg); border-radius: 3px; cursor: pointer; padding: 0; @@ -160,23 +160,23 @@ justify-content: center; font-size: 11px; line-height: 1; - color: var(--pt-color-checkbox-checked-fg); + color: var(--pretable-checkbox-checked-fg); } :where( button[data-pretable-row-select][aria-checked="true"], button[data-pretable-row-select-all][aria-checked="true"] ) { - background: var(--pt-color-checkbox-checked-bg); - border-color: var(--pt-color-checkbox-checked-bg); - color: var(--pt-color-checkbox-checked-fg); + background: var(--pretable-checkbox-checked-bg); + border-color: var(--pretable-checkbox-checked-bg); + color: var(--pretable-checkbox-checked-fg); } :where( button[data-pretable-row-select][aria-checked="mixed"], button[data-pretable-row-select-all][aria-checked="mixed"] ) { - background: var(--pt-color-checkbox-checked-bg); - border-color: var(--pt-color-checkbox-checked-bg); - color: var(--pt-color-checkbox-checked-fg); + background: var(--pretable-checkbox-checked-bg); + border-color: var(--pretable-checkbox-checked-bg); + color: var(--pretable-checkbox-checked-fg); } /* Column resize handle (sub-project C) */ @@ -187,7 +187,7 @@ width: 4px; height: 100%; cursor: col-resize; - background: var(--pt-color-resize-handle); + background: var(--pretable-resize-handle); z-index: 2; user-select: none; touch-action: none; @@ -196,15 +196,15 @@ [data-pretable-resize-handle]:hover, [data-pretable-resize-handle][data-pretable-dragging="true"] ) { - background: var(--pt-color-resize-handle-hover); + background: var(--pretable-resize-handle-hover); } /* Column reorder gesture (sub-project C) */ :where([data-pretable-reorder-ghost]) { position: fixed; pointer-events: none; - background: var(--pt-color-reorder-ghost-bg); - box-shadow: var(--pt-color-reorder-ghost-shadow); + background: var(--pretable-reorder-ghost-bg); + box-shadow: var(--pretable-reorder-ghost-shadow); opacity: 0.6; z-index: 10; user-select: none; @@ -213,7 +213,7 @@ position: absolute; top: 0; width: 2px; - background: var(--pt-color-reorder-drop-indicator); + background: var(--pretable-reorder-drop-indicator); z-index: 9; pointer-events: none; } diff --git a/packages/ui/src/themes/excel.css b/packages/ui/src/themes/excel.css index 8b87bdd7..91eacecd 100644 --- a/packages/ui/src/themes/excel.css +++ b/packages/ui/src/themes/excel.css @@ -34,6 +34,22 @@ /* Accent */ --pretable-accent: #107c41; /* Excel brand green */ + /* Grid controls — derived from this theme's tokens so they recolor coherently */ + --pretable-selection-bg: color-mix( + in srgb, + var(--pretable-accent) 8%, + transparent + ); + --pretable-checkbox-bg: var(--pretable-bg-grid); + --pretable-checkbox-border: var(--pretable-rule-strong); + --pretable-checkbox-checked-bg: var(--pretable-accent); + --pretable-checkbox-checked-fg: #fff; + --pretable-resize-handle: transparent; + --pretable-resize-handle-hover: var(--pretable-accent); + --pretable-reorder-ghost-bg: var(--pretable-bg-header); + --pretable-reorder-ghost-shadow: 0 4px 12px rgb(0 0 0 / 0.12); + --pretable-reorder-drop-indicator: var(--pretable-accent); + /* Density — natural default = compact (Excel "Compact" tier) */ --pretable-row-height: 20px; /* 15pt at 96 DPI */ --pretable-header-height: 24px; diff --git a/packages/ui/src/themes/material.css b/packages/ui/src/themes/material.css index 2ac81ec7..afa43908 100644 --- a/packages/ui/src/themes/material.css +++ b/packages/ui/src/themes/material.css @@ -37,6 +37,22 @@ /* Accent */ --pretable-accent: #0061a4; /* primary — M3 baseline blue */ + /* Grid controls — derived from this theme's tokens so they recolor coherently */ + --pretable-selection-bg: color-mix( + in srgb, + var(--pretable-accent) 8%, + transparent + ); + --pretable-checkbox-bg: var(--pretable-bg-grid); + --pretable-checkbox-border: var(--pretable-rule-strong); + --pretable-checkbox-checked-bg: var(--pretable-accent); + --pretable-checkbox-checked-fg: #fff; + --pretable-resize-handle: transparent; + --pretable-resize-handle-hover: var(--pretable-accent); + --pretable-reorder-ghost-bg: var(--pretable-bg-header); + --pretable-reorder-ghost-shadow: 0 4px 12px rgb(0 0 0 / 0.12); + --pretable-reorder-drop-indicator: var(--pretable-accent); + /* Density — natural default = standard */ --pretable-row-height: 48px; --pretable-header-height: 52px; diff --git a/packages/ui/src/tokens.css b/packages/ui/src/tokens.css index 47e16044..679b997b 100644 --- a/packages/ui/src/tokens.css +++ b/packages/ui/src/tokens.css @@ -63,26 +63,6 @@ --pt-fs-eyebrow: 11px; --pt-fs-data: 12.5px; - /* Cell-range selection (Phase 3 — derived from accent) */ - --pt-color-selection-bg: rgb(234 88 12 / 0.08); - --pt-color-selection-border: rgb(234 88 12 / 0.6); - --pt-color-focus-ring: var(--pt-accent); - - /* Column resize handle (sub-project C) */ - --pt-color-resize-handle: transparent; - --pt-color-resize-handle-hover: var(--pt-color-selection-border); - - /* Column reorder ghost + drop indicator (sub-project C) */ - --pt-color-reorder-ghost-bg: var(--pt-color-surface, #fff); - --pt-color-reorder-ghost-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); - --pt-color-reorder-drop-indicator: var(--pt-color-focus-ring); - - /* Row-selection checkbox column (Phase 4) */ - --pt-color-checkbox-bg: #fff; - --pt-color-checkbox-border: #d1d5db; - --pt-color-checkbox-checked-bg: var(--pt-accent); - --pt-color-checkbox-checked-fg: #fff; - /* Layout tokens */ --pt-page-max: 1440px; --pt-header-h: 60px;