docs(design): add CREWLI-DESIGN-TOKENS.md token inventory (Plan 2.5 Track B)
Per RFC-WS-PRIMEVUE-PLAN-2-5 Track B (§6). Inventories and classifies the design tokens live in the codebase (Brand-essential / Bespoke / Generic per RFC §6.2) and records the Typography decision register (AD-2.5-T1) end-to-end — including the historical Public Sans removal across both the CSS path (P2, commit41af1801) and the webfontloader JS path (P2-followup, commit641ca513). Inventory covers: - Tailwind v4 @theme + :root (font tokens + dark variant selector) - PrimeVue Aura preset (full 11-step Crewli teal primary palette + light/dark colorScheme bindings; everything else inherits Aura) - PrimeVue runtime config (darkModeSelector='.dark', cssLayer=false, empty pt defaults scaffold) - Iconify (Tabler set, dash-naming convention) - useShellUiStore runtime writers (.dark class, data-density) - Workspace gradient palette (8 pairs, deterministic per org id) - Brand-square recipe (32px / rounded-lg / px-4 / centring equation) - Density axis (comfortable | compact, axis present but no component-level reaction yet — backlog DENSITY-AWARE-SPACING) Drift items flagged for Plan 4 (no fix in P7 — read-only audit): - Workspace gradient palette uses Tailwind palette anchors, not derivations of Crewli's #0D9394 - User-avatar gradient hardcodes #f472b6 (Tailwind pink) + a fallback #0d9488 that's NOT Crewli's #0D9394 - --topbar-h referenced with fallback only, never declared in :root - 'density' axis attribute set but no component spacing reacts to it Remaining token decisions (surface tones, focus-ring, radius scale, font-size scale, spacing rhythm, density-aware spacing, shadow scale, secondary palette) explicitly deferred to Plan 4 per RFC §6.4. Read-only audit: zero code files touched (verified via git status). Foundation document for Plan 4's template-layer token work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
280
dev-docs/CREWLI-DESIGN-TOKENS.md
Normal file
280
dev-docs/CREWLI-DESIGN-TOKENS.md
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
# Crewli Design Tokens — Audit & Decision Register
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| ---------------------- | ------------------------------------------- |
|
||||||
|
| Source design system | crewli-starter / PrimeVue Aura |
|
||||||
|
| Aura preset version | `@primeuix/themes` 4.5.x |
|
||||||
|
| Tailwind version | v4 (CSS-first `@theme`, no `tailwind.config.ts`) |
|
||||||
|
| Audit date | 2026-05-21 |
|
||||||
|
| Phase-1 decided tokens | Typography (AD-2.5-T1) |
|
||||||
|
| Plan reference | RFC-WS-PRIMEVUE-PLAN-2-5 §6 (Track B) |
|
||||||
|
| Related docs | [PRIMEVUE_COMPONENTS.md](./PRIMEVUE_COMPONENTS.md), [RFC-WS-PRIMEVUE-PLAN-2-5.md](./RFC-WS-PRIMEVUE-PLAN-2-5.md) |
|
||||||
|
|
||||||
|
## 1. Purpose & scope
|
||||||
|
|
||||||
|
This document is the **inventory and decision register** for the design tokens currently live in the Crewli SPA. It is generated as **Track B** of `RFC-WS-PRIMEVUE-PLAN-2-5` and serves as the foundation for Plan 4's template-layer token work.
|
||||||
|
|
||||||
|
**What this doc does:**
|
||||||
|
- Inventories every token source (Tailwind v4 `@theme`, PrimeVue Aura preset, component-level CSS variables, store-managed runtime attributes).
|
||||||
|
- Classifies each token as **Brand-essential**, **Bespoke**, or **Generic** per the RFC §6.2 framework.
|
||||||
|
- Records the **Typography Decision Register** (AD-2.5-T1) end-to-end, including the historical Public Sans removal across both the CSS path and the webfontloader JS path.
|
||||||
|
- Flags drift, inconsistencies, and follow-up items for Plan 4.
|
||||||
|
|
||||||
|
**What this doc does NOT do:**
|
||||||
|
- Make new token decisions beyond Typography (per RFC §6.4, the rest is deferred to Plan 2.5b or absorbed into Plan 4).
|
||||||
|
- Refactor, consolidate, or move any token value. This is a read-only audit; the doc records state without changing it.
|
||||||
|
- Decide on density-aware component spacing, focus-ring tuning, surface-tone overrides, or radius-scale customization — all explicitly deferred.
|
||||||
|
|
||||||
|
If a token value changes after this audit date, update the relevant row + the **Audit date** field; do not silently let the inventory drift.
|
||||||
|
|
||||||
|
## 2. Classification scheme
|
||||||
|
|
||||||
|
Each token is one of (verbatim from RFC §6.2):
|
||||||
|
|
||||||
|
- **Brand-essential** — token carries Crewli brand identity. Keep current value. Examples: `--p-primary-color` (teal), gradient definitions, Crewli wordmark colors.
|
||||||
|
- **Bespoke** — token has a non-default value with deliberate design intent. Keep current value with rationale. Examples: the brand-square recipe (32px / `rounded-lg` / `px-4`), the density axis attribute, custom topbar height.
|
||||||
|
- **Generic** — token has a non-default value with no documented intent. Default decision: **revert to framework default**. Override requires explicit rationale.
|
||||||
|
|
||||||
|
## 3. Token sources
|
||||||
|
|
||||||
|
| Source | Type | Notes |
|
||||||
|
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| [`apps/app/src/assets/styles/tailwind.css`](../apps/app/src/assets/styles/tailwind.css) | Tailwind v4 `@theme` + `:root` | Single source for Tailwind tokens. Houses `--font-sans` (`@theme`), `--crewli-font-family` (`:root`), and the `@custom-variant dark` selector binding. |
|
||||||
|
| [`apps/app/src/plugins/primevue/theme.ts`](../apps/app/src/plugins/primevue/theme.ts) | PrimeVue Aura preset (programmatic) | `definePreset(Aura, {...})` — overrides ONLY the `semantic.primary` palette + the light/dark `colorScheme` bindings. Everything else inherits Aura defaults. |
|
||||||
|
| [`apps/app/src/plugins/primevue/index.ts`](../apps/app/src/plugins/primevue/index.ts) | PrimeVue runtime config | `darkModeSelector: '.dark'`, `cssLayer: false`, Dutch locale, empty `pt` defaults. |
|
||||||
|
| [`apps/app/src/plugins/iconify.ts`](../apps/app/src/plugins/iconify.ts) | Iconify runtime | Tabler set eager-loaded; icon name convention `tabler-<slug>` (dash, NOT Iconify standard colon). |
|
||||||
|
| [`apps/app/src/stores/useShellUiStore.ts`](../apps/app/src/stores/useShellUiStore.ts) | Pinia store (runtime DOM attribute writer) | Writes `<html class="dark">` (theme) and `<html data-density="...">` (density) via `applyDomAttributes()`. |
|
||||||
|
| [`apps/app/src/utils/v2/gradient.ts`](../apps/app/src/utils/v2/gradient.ts) | Component data | The 8-pair gradient palette consumed by the WorkspaceSwitcher avatar (per-org gradient via deterministic hash). |
|
||||||
|
| Component scoped CSS (`<style scoped>` blocks) | Per-component | Inset box-shadow recipe on the brand-square (RFC §7.4 last-resort — Tailwind has no inset directional utility). |
|
||||||
|
| `apps/app/src/@core/scss/template/libs/vuetify/_variables.scss` | Vuetify SCSS | Mirrors Inter via `$font-family-custom` so legacy Vuexy surfaces match the v2 shell during the F3–F6 window. |
|
||||||
|
|
||||||
|
## 4. Color tokens
|
||||||
|
|
||||||
|
### 4.1 Primary palette (Brand-essential)
|
||||||
|
|
||||||
|
Set by `CrewliPreset` in `plugins/primevue/theme.ts`. The full 11-step ramp is derived from Crewli teal `#0D9394`.
|
||||||
|
|
||||||
|
| Token | Value | Aura default | Classification | Decision | Note |
|
||||||
|
| ---------------- | --------- | ------------ | --------------- | -------- | --------------------------------------------------------------------- |
|
||||||
|
| `primary.50` | `#E6F4F4` | emerald-50 | Brand-essential | Keep | Tinted teal scale; replaces Aura emerald. |
|
||||||
|
| `primary.100` | `#CCE9EA` | emerald-100 | Brand-essential | Keep | |
|
||||||
|
| `primary.200` | `#99D3D4` | emerald-200 | Brand-essential | Keep | |
|
||||||
|
| `primary.300` | `#66BDBE` | emerald-300 | Brand-essential | Keep | |
|
||||||
|
| `primary.400` | `#33A7A8` | emerald-400 | Brand-essential | Keep | |
|
||||||
|
| **`primary.500`**| **`#0D9394`** | emerald-500 | Brand-essential | Keep | **The Crewli teal**. Single source of brand identity in the color system. |
|
||||||
|
| `primary.600` | `#0B7F80` | emerald-600 | Brand-essential | Keep | Mirrors Vuetify's `staticPrimaryDarkenColor` (lock-stepped during F3–F6). |
|
||||||
|
| `primary.700` | `#086B6C` | emerald-700 | Brand-essential | Keep | |
|
||||||
|
| `primary.800` | `#055758` | emerald-800 | Brand-essential | Keep | |
|
||||||
|
| `primary.900` | `#034344` | emerald-900 | Brand-essential | Keep | |
|
||||||
|
| `primary.950` | `#012F30` | emerald-950 | Brand-essential | Keep | |
|
||||||
|
|
||||||
|
### 4.2 Color-scheme bindings (Brand-essential)
|
||||||
|
|
||||||
|
| Token | Value | Classification | Note |
|
||||||
|
| ------------------------------ | ---------------------- | --------------- | ------------------------------------- |
|
||||||
|
| `light.primary.color` | `{primary.500}` | Brand-essential | Crewli teal as light-mode primary. |
|
||||||
|
| `light.primary.contrastColor` | `#ffffff` | Brand-essential | White text on teal. |
|
||||||
|
| `light.primary.hoverColor` | `{primary.600}` | Brand-essential | One step darker on hover. |
|
||||||
|
| `light.primary.activeColor` | `{primary.700}` | Brand-essential | |
|
||||||
|
| `dark.primary.color` | `{primary.400}` | Brand-essential | Lighter teal on dark surfaces. |
|
||||||
|
| `dark.primary.contrastColor` | `{surface.900}` | Brand-essential | Inherits Aura surface scale. |
|
||||||
|
| `dark.primary.hoverColor` | `{primary.300}` | Brand-essential | |
|
||||||
|
| `dark.primary.activeColor` | `{primary.200}` | Brand-essential | |
|
||||||
|
|
||||||
|
### 4.3 Dark-mode mechanism (Bespoke)
|
||||||
|
|
||||||
|
| Token | Value | Classification | Note |
|
||||||
|
| ---------------------------------------------- | -------------------------------------- | -------------- | --------------------------------------------------------------------------------------------- |
|
||||||
|
| PrimeVue `darkModeSelector` | `.dark` | Bespoke | Class-based, NOT `prefers-color-scheme`. AD-2.5-D1. |
|
||||||
|
| Tailwind `@custom-variant dark` | `(&:where(.dark, .dark *))` | Bespoke | Pins Tailwind `dark:` variants to the same class. Without this, PrimeVue and Tailwind would divergent (PrimeVue listens to class, Tailwind default listens to OS). |
|
||||||
|
| `useShellUiStore.applyDomAttributes()` write | `documentElement.classList.toggle('dark', theme==='dark')` | Bespoke | Runtime write — single source of truth for the class. |
|
||||||
|
|
||||||
|
### 4.4 Workspace gradient palette (Brand-essential pattern; values are data)
|
||||||
|
|
||||||
|
[`utils/v2/gradient.ts`](../apps/app/src/utils/v2/gradient.ts) exports `GRADIENT_PALETTE`, an 8-pair list keyed by deterministic hash of `org.id`. Each entry is a `[from, to]` tuple for a 135° linear gradient.
|
||||||
|
|
||||||
|
| Index | From | To | Tailwind palette anchor |
|
||||||
|
| ----- | --------- | --------- | ----------------------- |
|
||||||
|
| 0 | `#0d9488` | `#0f766e` | teal-600 → teal-700 |
|
||||||
|
| 1 | `#0891b2` | `#0e7490` | cyan-600 → cyan-700 |
|
||||||
|
| 2 | `#059669` | `#047857` | emerald-600 → emerald-700 |
|
||||||
|
| 3 | `#10b981` | `#059669` | emerald-500 → emerald-600 |
|
||||||
|
| 4 | `#0284c7` | `#0369a1` | sky-600 → sky-700 |
|
||||||
|
| 5 | `#14b8a6` | `#0d9488` | teal-500 → teal-600 |
|
||||||
|
| 6 | `#06b6d4` | `#0891b2` | cyan-500 → cyan-600 |
|
||||||
|
| 7 | `#34d399` | `#10b981` | emerald-400 → emerald-500 |
|
||||||
|
|
||||||
|
**Classification**: the pattern (per-org deterministic gradient) is Brand-essential. The specific palette values are **data**, not core design tokens — they sit in component code. **⚠ Drift**: the palette uses Tailwind palette anchors (teal/cyan/emerald/sky from Tailwind defaults) rather than derivations of Crewli's actual `#0D9394`. crewli-starter's reference markup uses pairs like `#0D9394, #075F60` (Crewli teal-500/-700 in the new ramp). Flagged for Plan 4 — see §10.
|
||||||
|
|
||||||
|
### 4.5 Avatar gradient leftover (drift)
|
||||||
|
|
||||||
|
`AppTopbar.vue` user avatar pt-style:
|
||||||
|
```
|
||||||
|
background: linear-gradient(135deg, #f472b6, var(--p-primary-500, #0d9488));
|
||||||
|
```
|
||||||
|
|
||||||
|
| Aspect | Value | Note |
|
||||||
|
| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------ |
|
||||||
|
| Hardcoded `#f472b6` | Tailwind pink-400 | Not a Crewli brand color. Vuexy-era leftover; mixed with brand teal in the gradient. |
|
||||||
|
| Fallback `#0d9488` | Tailwind teal-600 | NOT the Crewli teal `#0D9394`. Inert today (the `var()` resolves), but masks the brand color if the CSS var ever fails. |
|
||||||
|
| Classification | Generic (drift) | Flagged for Plan 4 cleanup; not in P7 scope to change. |
|
||||||
|
|
||||||
|
## 5. Typography tokens + Decision Register
|
||||||
|
|
||||||
|
### 5.1 Live state
|
||||||
|
|
||||||
|
| Token | Value | Source | Classification | Decision |
|
||||||
|
| ----------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------- | -------------- | -------- |
|
||||||
|
| Tailwind `--font-sans` (`@theme`) | `"Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif` | `apps/app/src/assets/styles/tailwind.css` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
| `:root --crewli-font-family` | same stack as above | `apps/app/src/assets/styles/tailwind.css` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
| `html, body { font-family }` | `var(--crewli-font-family)` | `apps/app/src/assets/styles/tailwind.css` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
| Vuetify `$font-family-custom` | `"Inter", ...` | `apps/app/src/@core/scss/template/libs/vuetify/_variables.scss` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
| Inter weights loaded | 400 / 500 / 600 / 700 (via `@fontsource/inter`) | `apps/app/src/main.ts` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
| Inter load mechanism | Local NPM package (`@fontsource/inter`) — no CDN | `apps/app/src/main.ts` | Brand-essential | Decided — AD-2.5-T1 |
|
||||||
|
|
||||||
|
### 5.2 Decision Register — AD-2.5-T1 (Inter via @fontsource/inter)
|
||||||
|
|
||||||
|
**Decision**: Inter is Crewli's sans-serif typography across PrimeVue, Tailwind, and Vuetify surfaces. Loaded locally via `@fontsource/inter` (weights 400 / 500 / 600 / 700). No Google Fonts CDN, no CDN of any kind for the body font.
|
||||||
|
|
||||||
|
**Status**: complete (decided + implemented + regression-locked).
|
||||||
|
|
||||||
|
**Rationale**:
|
||||||
|
1. Inter is PrimeVue's de-facto reference font — using it produces the cleanest visual default across the component set.
|
||||||
|
2. Local loading avoids external Google Fonts requests, which carry GDPR/privacy implications and add a third-party network dependency to first paint.
|
||||||
|
3. Single source — one font, three frameworks all reading from the same `--crewli-font-family` stack — prevents the dual-path drift that the initial Plan 2.5 P2 audit missed (see §5.3).
|
||||||
|
|
||||||
|
**History**:
|
||||||
|
|
||||||
|
| Date / commit | Event |
|
||||||
|
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Pre-Plan 2.5 | Codebase loaded **Public Sans** via two independent paths: (a) CSS `font-family` declarations in `tailwind.css` and Vuexy `_variables.scss`; (b) a JS `webfontloader` plugin (`apps/app/src/plugins/webfontloader.ts`) that fetched `Public+Sans:...&display=swap` from the Google Fonts CDN at runtime. The two paths were independent; neither knew about the other. |
|
||||||
|
| Plan 2.5 P2 (commit `41af1801`) | CSS path reverted to Inter. `tailwind.css` `@theme --font-sans` + `:root --crewli-font-family` + the `html, body` cascade all changed to the Inter stack. Vuetify `$font-family-custom` matched. Regression-lock spec `tests/unit/styles/typography.spec.ts` added — but it only inspected CSS file contents. |
|
||||||
|
| P5 manual smoke (2026-05-20) | The live DOM still carried `wf-publicsans-n4-active wf-active` classes on `<html>`. webfontloader was still fetching Public Sans from Google Fonts. The CSS-only regression-lock had missed the JS path. |
|
||||||
|
| P2-followup (commit `641ca513`) | The whole `webfontloader.ts` plugin removed (it loaded only Public Sans — no other families). `webfontloader@1.6.28` + `@types/webfontloader@1.6.38` removed from `package.json`. Regression-lock strengthened: `typography.spec.ts` now also scans every file in `src/plugins/` for any reference to `webfontloader`, `WebFont.load`, `Public Sans` (any spelling), or `fonts.googleapis.com`. The plugin's auto-registration via Vuexy's `registerPlugins()` glob is severed by the file deletion alone — no manual unregister site to clean up. |
|
||||||
|
|
||||||
|
**Regression lock** (current state):
|
||||||
|
|
||||||
|
| Spec | Asserts |
|
||||||
|
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||||
|
| `tests/unit/styles/typography.spec.ts` — `Typography regression lock (AD-2.5-T1)` describe | `tailwind.css` declares Inter first in every direct font stack; Vuexy `$font-family-custom` declares Inter first; no `Public Sans` (any case/separator) remains in either CSS file. |
|
||||||
|
| `tests/unit/styles/typography.spec.ts` — `JS font-loading path (AD-2.5-T1 completion)` describe | No file under `src/plugins/` references `webfontloader`, `WebFont.load`, `Public Sans` (any case/separator), or `fonts.googleapis.com`; the `webfontloader.ts` plugin file does NOT exist. |
|
||||||
|
|
||||||
|
If a future PR re-introduces any of these (a Vuexy template port that loads a Google Font, a Storybook addon that lazy-loads a webfontloader path, etc.) the spec catches it before merge.
|
||||||
|
|
||||||
|
### 5.3 Why Typography is the only fully-decided Phase-1 token
|
||||||
|
|
||||||
|
Per RFC §6.4: font choice has the largest visual surface impact (every text element in the app), the regression-lock pattern needed an anchor reference, and reverting from Public Sans to Inter was already a clear correctness call. Other tokens (focus-ring width, surface tones, density-aware spacing) are localized and benefit from waiting until more pages exist so visual impact is observable.
|
||||||
|
|
||||||
|
## 6. Spacing / sizing / radius tokens
|
||||||
|
|
||||||
|
### 6.1 The brand-square recipe (Bespoke)
|
||||||
|
|
||||||
|
The most consequential bespoke layout invariant in the shell. Encoded across SidebarHeader + WorkspaceSwitcher in P6-styling commits.
|
||||||
|
|
||||||
|
| Property | Value | Note |
|
||||||
|
| -------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Square size | `w-8 h-8` | 32px × 32px. Applies to the SidebarHeader brand mark AND the WorkspaceSwitcher avatar. |
|
||||||
|
| Square radius | `rounded-lg` | 8px. Set after manual smoke rejected `rounded-xl` (12px) as too soft. |
|
||||||
|
| Wrapper height | `h-[56px]` | Matches the topbar height; applied to the SidebarHeader brand row AND the WorkspaceSwitcher wrapper in collapsed state. |
|
||||||
|
| Wrapper horizontal padding | `px-4` | 16px each side. Constant across both states for both wrappers (collapse no longer toggles `justify-center`/`px-0`). |
|
||||||
|
| Inset shadow | `inset 0 -2px 0 rgb(0 0 0 / 8%)` | Scoped CSS in both components — no Tailwind utility for inset directional shadow at this granularity (RFC §7.4 last-resort). |
|
||||||
|
|
||||||
|
**Centring equation**: `collapsed rail (64px) = square (32px) + 2 × px-4 (2 × 16px)`. A left-aligned 32px square inside a `px-4` row is *also* centred when the row is 64px wide. This is what keeps the brand logo stationary through the rail's width animation — no `justify-content` re-centring during the transition.
|
||||||
|
|
||||||
|
### 6.2 Sidebar rail widths (Bespoke)
|
||||||
|
|
||||||
|
| Token | Value | Classification | Note |
|
||||||
|
| ---------------------- | ---------- | -------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
|
| Expanded rail width | `w-64` (256px) | Bespoke | Matches crewli-starter's `--sidebar-w`. |
|
||||||
|
| Collapsed rail width | `w-16` (64px) | Bespoke | Matches crewli-starter's `--sidebar-w-collapsed`. Locked to the brand-square recipe (see §6.1). |
|
||||||
|
| Width transition | `transition-[width] duration-200` | Bespoke | 200ms ease on the aside. |
|
||||||
|
| Aside overflow | `overflow-hidden` | Bespoke | Clips content during the width animation — prevents `whitespace-nowrap` wordmark overflow. |
|
||||||
|
|
||||||
|
### 6.3 Topbar height (Bespoke CSS var with fallback)
|
||||||
|
|
||||||
|
| Token | Value | Note |
|
||||||
|
| ------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `--topbar-h` | `var(--topbar-h, 56px)` | Consumed in AppTopbar's `pt.root` class. **⚠ Not declared in `:root` anywhere** — only used with fallback. Effectively a hard-coded 56px with the *option* of being overridden. Worth either declaring properly or removing the var indirection in Plan 4. |
|
||||||
|
|
||||||
|
### 6.4 Other component sizes (Generic — Tailwind defaults)
|
||||||
|
|
||||||
|
The shell uses Tailwind defaults for the rest: `h-[38px]` for icon buttons (search, density, dark, notifications) is a one-off literal; `w-7 h-7` for the inline collapse chevron; `gap-[10px]` between brand row children; etc. None of these carry brand intent — they're sized to match crewli-starter visual reference and otherwise inherit Tailwind's default scale. Classified Generic; Plan 4 may consolidate via a `--icon-btn-size` token if patterns recur.
|
||||||
|
|
||||||
|
### 6.5 PrimeVue surface tones, focus-ring, border-radius scale (DEFERRED)
|
||||||
|
|
||||||
|
Inherited from Aura defaults — no overrides in `CrewliPreset`. Surface scales (`{surface.0}` … `{surface.950}`), focus-ring width/style, default radius (`var(--p-border-radius)`) all come from `@primeuix/themes/aura` as-shipped. Plan 2.5 made no decisions here; deferred to Plan 4 (see §9).
|
||||||
|
|
||||||
|
## 7. Density tokens (Bespoke)
|
||||||
|
|
||||||
|
| Token | Value | Source | Note |
|
||||||
|
| ------------------------- | ------------------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| `density` enum | `'comfortable' \| 'compact'` (NOT `comfy`) | `useShellUiStore.ts` | Binary. The earlier RFC draft referenced a `comfy` value — incorrect; the runtime type is `comfortable`. |
|
||||||
|
| `<html data-density>` | written by `applyDomAttributes()` | `useShellUiStore.ts` | Single DOM read for any consumer that wants to react to density. |
|
||||||
|
| `toggleDensity()` action | binary flip + sync `applyDomAttributes` | `useShellUiStore.ts` (P6 Fix 10) | Topbar button delegates here. |
|
||||||
|
| Component-level reaction | **none yet** | — | No component CSS currently keys off `[data-density=compact]`. The attribute is set; the component-level spacing-aware styles are deferred (backlog `DENSITY-AWARE-SPACING`). |
|
||||||
|
|
||||||
|
**Classification**: the axis itself is Bespoke (Crewli design intent — toggle between two interface densities). The component-level spacing recipes that respond to it are deferred and become Plan 4 work.
|
||||||
|
|
||||||
|
## 8. Icon tokens (Bespoke convention)
|
||||||
|
|
||||||
|
| Token | Value | Note |
|
||||||
|
| ------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Icon naming | `tabler-<slug>` | Dash-separated. Deviates from Iconify standard `tabler:<slug>` (colon) because Crewli's `Icon.vue` bridge expects dashes — a Vuexy-era convention preserved for the v1→v2 bridge. |
|
||||||
|
| Iconify collection | `@iconify-json/tabler` | Full set eager-loaded at app boot (`addCollection` in `plugins/iconify.ts`). |
|
||||||
|
| Default size | per-call (`:size="N"`) | No global icon-size token. Each call sets its own size. |
|
||||||
|
|
||||||
|
**Classification**: dash convention is Bespoke (a documented Crewli deviation); the set choice (Tabler) and the eager-load strategy are Bespoke decisions (size trade-off accepted for CSP compatibility — Iconify's runtime CDN is blocked).
|
||||||
|
|
||||||
|
## 9. Classification summary
|
||||||
|
|
||||||
|
| Bucket | Approximate count | What this means for Plan 4 |
|
||||||
|
| ----------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| **Brand-essential** | ~20 (full primary palette + scheme bindings + Typography stack) | Sacred. Plan 4 should not change these without explicit brand-team sign-off. Inter and `#0D9394` ARE Crewli. |
|
||||||
|
| **Bespoke** | ~10 (dark-mode mechanism, brand-square recipe, rail widths, density axis, icon-dash convention, topbar height) | Intentional Crewli deviations from defaults. Plan 4 may refactor / consolidate, but the rationale stays. Each Bespoke row in this doc carries a one-line "why". |
|
||||||
|
| **Generic** | ~everything else (full Aura surface scale, default border radius, focus ring, font-size scale, spacing scale, default shadows, default transitions) | These are framework defaults used as-is. Plan 4 decides which of these to override with Crewli-specific values. Default decision per RFC §6.2 is "revert to default" unless rationale exists. |
|
||||||
|
|
||||||
|
## 10. Drift + follow-up items for Plan 4
|
||||||
|
|
||||||
|
These are not P7 decisions; they're flagged here so they don't get lost.
|
||||||
|
|
||||||
|
| Item | Severity | Source | Recommended action |
|
||||||
|
| ---- | -------- | ------ | ------------------ |
|
||||||
|
| Workspace gradient palette uses Tailwind palette anchors (teal-500/600, cyan-500/600, emerald, sky) rather than derivations of Crewli's primary `#0D9394`. | Low (visual; data tier) | `utils/v2/gradient.ts` line 14 | Re-derive the 8 pairs from the Crewli primary ramp (`primary.500`/`primary.700` plus complementary tints from a Crewli secondary palette, when Plan 4 defines one). |
|
||||||
|
| User-avatar gradient hardcodes `#f472b6` (Tailwind pink-400) mixed with `var(--p-primary-500)`. | Medium (off-brand color baked into chrome) | `AppTopbar.vue` line 385 + 400 | Replace pink with a Crewli secondary/accent (Plan 4 decision). At minimum, drop the var fallback `#0d9488` which is NOT the Crewli teal (`#0D9394`). |
|
||||||
|
| `--topbar-h` referenced with fallback only, never declared in `:root`. | Low (correctness) | `AppTopbar.vue` line 225 | Either declare `:root { --topbar-h: 56px; }` in `tailwind.css`, or replace the `var(--topbar-h, 56px)` with the literal `h-14`. The current form is a half-finished abstraction. |
|
||||||
|
| `density` axis is set on `<html>` but no component spacing reacts to it. | Medium (feature incomplete) | `useShellUiStore.ts` + every shell component | Plan 4 picks the spacing surfaces that should compress in `compact` mode (typically row heights in tables, sidebar nav item padding, topbar height). Tracked as `DENSITY-AWARE-SPACING`. |
|
||||||
|
| PrimeVue `pt` defaults file is empty. | None (scaffold, by design) | `apps/app/src/plugins/primevue/defaults.ts` | F4 sub-packages populate as each Vuetify surface migrates. Not an immediate Plan 4 concern, but worth knowing this central override site exists. |
|
||||||
|
| The `--crewli-font-family` CSS variable duplicates the Tailwind `@theme --font-sans` stack. | Low (redundancy) | `tailwind.css` | Intentional twin — `@theme` feeds Tailwind utilities; `--crewli-font-family` is the cascade-level named alias for non-Tailwind consumers (PrimeVue, Vuetify body inheritance). Plan 4 could collapse to a single source if the two-step is no longer needed after Vuetify is removed. |
|
||||||
|
|
||||||
|
## 11. Deferred to Plan 4
|
||||||
|
|
||||||
|
Per RFC §6.4, Plan 2.5 deliberately stops Phase-1 at Typography. The following are NOT decided in this audit:
|
||||||
|
|
||||||
|
| Deferred area | Why deferred | Where it'll land |
|
||||||
|
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||||
|
| Surface tone scale (`--p-surface-*`) | Localized impact; needs page surfaces beyond the dashboard to evaluate. | Plan 4 (or Plan 2.5b if priority bumps). |
|
||||||
|
| Focus-ring width / color | Aura default works visually; needs accessibility audit before customization. | Plan 4 + accessibility pass. |
|
||||||
|
| Border-radius scale (`--p-border-radius` family) | Single-value change cascades through every rounded surface. Wait until more components are migrated. | Plan 4. |
|
||||||
|
| Font-size scale (root rem base) | Touches the typographic system; needs Plan 4's full type-scale design. | Plan 4. |
|
||||||
|
| Spacing rhythm (`--p-spacing-*`) | Plan 2.5 used Tailwind defaults; Plan 4 may introduce Crewli-specific spacing tokens. | Plan 4. |
|
||||||
|
| Density-aware component spacing | The axis exists (`<html data-density>`) but nothing reads it. Plan 4 picks the surfaces. | Plan 4 (`DENSITY-AWARE-SPACING`). |
|
||||||
|
| Shadow scale | Aura defaults; one bespoke inset-shadow exception on the brand square (RFC §7.4 last-resort). | Plan 4 if shadow recipes recur. |
|
||||||
|
| Secondary / accent color palette | Crewli's brand currently has only the teal primary defined. A secondary is needed for callouts (e.g., the off-brand pink in §10). | Plan 4 + brand-team input. |
|
||||||
|
| Workspace gradient palette re-derivation | See §10. | Plan 4. |
|
||||||
|
|
||||||
|
## 12. Maintenance
|
||||||
|
|
||||||
|
When a token decision lands in a future PR:
|
||||||
|
|
||||||
|
1. Find the row in this doc; update the **Decision** column and the **Note** column.
|
||||||
|
2. Add a new row to §5.2-style "Decision Register" if the token gets its own register entry.
|
||||||
|
3. Update the **Audit date** field at the top.
|
||||||
|
4. Add the corresponding regression-lock spec if the decision is enforceable in code (CSS file contents, computed style, store action). Cross-link the spec path here.
|
||||||
|
|
||||||
|
When a new token source appears (e.g., a `pt` default block populates):
|
||||||
|
|
||||||
|
1. Add the source to §3.
|
||||||
|
2. Inventory its tokens in the relevant section.
|
||||||
|
3. Classify each one.
|
||||||
|
|
||||||
|
Drift catches: if a follow-up audit finds a token in the codebase not listed here, that's the doc to fix, not the codebase.
|
||||||
Reference in New Issue
Block a user