From 7690273b1ad6dd1f92bbb07bae33244389988fe5 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:12:50 -0700 Subject: [PATCH 1/8] docs: cockpit polish design (PR 1 of 3-PR sequence) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First in a three-PR sequence (cockpit polish → chat lib polish → cockpit vs website style alignment). Renames @ngaf/chat theme attribute from data-ngaf-chat-theme to data-theme for consistency. Replaces hardcoded rgba color literals in cockpit.css with --ds-* tokens. Standardizes cockpit border styling on Tailwind arbitrary values. Drops shadcn-style alias vars in cockpit.css in favor of direct --ds-* consumption. Co-Authored-By: Claude Opus 4.7 --- .../specs/2026-05-13-cockpit-polish-design.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-13-cockpit-polish-design.md diff --git a/docs/superpowers/specs/2026-05-13-cockpit-polish-design.md b/docs/superpowers/specs/2026-05-13-cockpit-polish-design.md new file mode 100644 index 000000000..3b1375980 --- /dev/null +++ b/docs/superpowers/specs/2026-05-13-cockpit-polish-design.md @@ -0,0 +1,115 @@ +# Cockpit Polish — Design + +**Date:** 2026-05-13 +**Status:** Spec — pending implementation plan +**Spec series:** First in a three-PR sequence (cockpit polish → chat lib polish → cockpit ↔ website style alignment) + +## Goal + +Tighten cockpit's dark-mode-correctness, divider/border patterns, and standardize the chat library's theme-attribute API. Land the pilot's chrome-MCP-surfaced fixes in a single reviewable PR, and use the work as a forcing function to surface chat lib follow-ups for PR 2. + +Out of scope: +- Chat lib internals beyond renaming `data-ngaf-chat-theme` → `data-theme` (the text-wrap bug, message-bubble width handling, and other chat lib polish are tracked for PR 2) +- Marketing website alignment (PR 3) +- New design-tokens entries (use existing tokens; accept tiny alpha shifts for consistency) +- Migration of `cockpit///angular/` example apps beyond the timeline pilot (Stage 2 of the examples theme sync work, separate spec) + +## Decisions + +| # | Decision | Choice | +|---|---|---| +| 1 | Chat lib theme attribute | Rename `[data-ngaf-chat-theme]` → `[data-theme]`. Keep the `prefers-color-scheme: dark` cascade. No JS API. | +| 2 | Border/divider convention in cockpit | Tailwind arbitrary values (`border-b border-[var(--ds-border)]`). Migrate the ~5 inline-style holdouts. | +| 3 | Hardcoded color mapping in `cockpit.css` | Use existing `--ds-*` tokens. Accept ≤0.04 alpha shifts. No new tokens added. | +| 4 | Backwards compatibility | None. Drop shadcn-style aliases (`--muted`, `--border`, `--input`) in favor of `--ds-*` references at consumer sites. | +| 5 | `installEmbeddedTheme()` cleanup | Remove the redundant `data-ngaf-chat-theme` set added in the uncommitted in-flight change. After the rename, `data-theme` is sufficient. | + +## Architecture + +**Chat lib three-layer cascade** (consumer-facing contract): + +```css +/* L1: Default fallback */ +:root { /* LIGHT_TOKENS */ } + +/* L2: OS preference — kicks in only if L3 is unset */ +@media (prefers-color-scheme: dark) { + :root { /* DARK_TOKENS */ } +} + +/* L3: Programmatic override — highest specificity, always wins */ +[data-theme="light"] { /* LIGHT_TOKENS */ } +[data-theme="dark"] { /* DARK_TOKENS */ } +``` + +`[data-theme="dark"]` selector specificity (0,1,0,0) > `:root` (0,0,0,0). Programmatic override always wins regardless of source order or OS preference. Consumers who don't wire up `data-theme` get OS-preference behavior automatically. + +**Cockpit token discipline:** + +- All cockpit-app colors must be `var(--ds-*)` references or theme-flipping computed colors. No `rgba(0, 64, 144, X)` literals (the navy is light-theme accent — flips to bright blue in dark via `--ds-accent`). No `rgba(26, 27, 38, X)` literals (the dark canvas — flips to light surface via `--ds-surface`). +- Drop the shadcn-style alias vars (`--muted`, `--border`, `--input`) currently defined in `cockpit.css :root`. Migrate consumers to `--ds-*` directly. + +**Border pattern:** + +- Cockpit uses Tailwind arbitrary values for borders. Pattern: `className="border-b border-[var(--ds-border)]"`. +- No inline `style={{ borderBottom: '1px solid var(--ds-border)' }}` in cockpit components. +- This pattern was already dominant; the change is migrating the small number of inline-style holdouts. + +## Package changes + +**`@ngaf/chat`:** patch bump +- `src/lib/styles/chat-tokens.ts`: rename `[data-ngaf-chat-theme="light"]` → `[data-theme="light"]`, same for dark. The `:root` and `prefers-color-scheme` blocks remain unchanged. +- No other chat lib changes (text-wrap bug etc. → PR 2). + +**`@ngaf/example-layouts`:** patch bump +- `src/lib/install-embedded-theme.ts`: remove the `document.documentElement.dataset.ngafChatTheme = theme;` line and its comment. The existing `data-theme` set now drives both design-tokens and chat lib via the renamed selector. +- Update `theme.css` if any namespace bridge references need adjusting (probably none — bridge stays at `:root` since it's now redundant for chat lib's own theming but still useful for visual cohesion if/when PR 3 unifies palettes). + +**`apps/cockpit`:** +- `src/components/cockpit-shell.tsx`: keep the edge-to-edge content padding change. Migrate the inline `style={{ borderBottom: '1px solid var(--ds-border)' }}` to Tailwind class. +- `src/components/sidebar/cockpit-sidebar.tsx`: migrate any inline border styles to Tailwind classes. +- `src/app/cockpit.css`: + - Drop `--muted`, `--border`, `--input` aliases from `:root` block. + - Replace all `rgba(0, 64, 144, X)` literals with `var(--ds-accent-surface)` (alpha 0.04 / 0.06 sites) or `var(--ds-accent-border)` (alpha 0.12 / 0.15 sites). + - Replace `rgba(26, 27, 38, 0.95)` (line 142, dark-assumed) with `var(--ds-surface)`. + +## Per-file changes (summary table) + +| File | Change | +|---|---| +| `libs/chat/src/lib/styles/chat-tokens.ts` | Attribute selector rename only | +| `libs/example-layouts/src/lib/install-embedded-theme.ts` | Remove `dataset.ngafChatTheme` line | +| `apps/cockpit/src/components/cockpit-shell.tsx` | Border inline → Tailwind class; preserve edge-to-edge padding | +| `apps/cockpit/src/components/sidebar/cockpit-sidebar.tsx` | Border inline → Tailwind class wherever present | +| `apps/cockpit/src/app/cockpit.css` | Replace literals with tokens; drop shadcn aliases | +| Any cockpit consumer of `--muted` / `--border` / `--input` | Migrate to `--ds-*` (grep to find) | +| `libs/chat/package.json` | Patch bump | +| `libs/example-layouts/package.json` | Patch bump | + +## Verification + +**Unit:** `pnpm nx test chat example-layouts cockpit` — green. + +**Visual smoke (chrome MCP):** +1. Cockpit on 3000, timeline angular on 4507 (pilot already wired) +2. Navigate to `chat/core-capabilities/timeline/overview/python` (the chat-timeline capability) +3. Dark mode (default): all surfaces use design-tokens dark palette, no white callouts bleeding through +4. Toggle to light: same — no dark-assumed code-block backgrounds bleeding through +5. Chat input area (in iframe) follows host theme — bg matches surrounding shell +6. No inline `style={{ borderBottom }}` survives anywhere in cockpit components (grep gate) +7. No `rgba(0, 64, 144` or `rgba(26, 27, 38` literals in `cockpit.css` (grep gate) + +## Risks and mitigations + +- **Chat lib attribute rename is breaking** for any external consumer that explicitly set `data-ngaf-chat-theme`. Acceptable per "no backwards compat" decision. Patch bump signal is sufficient for now; we're 0.0.x. +- **Shadcn alias removal** could break code that references `--muted` / `--border` / `--input` directly. Mitigated by grep sweep before removing, plus typecheck (CSS vars are runtime not type-checked, but TS/TSX files that reference them via `style` or `className` would still work — they're just strings). Visual smoke catches regressions. +- **Alpha shifts** (0.04 → 0.06, 0.12 → 0.15) might look slightly different in some callouts. Mitigated by visual smoke; tiny shifts at low-alpha values are imperceptible. +- **`theme.css` namespace bridge becomes partially redundant** in the embedded apps after the rename — chat lib's own `[data-theme="dark"]` rule now takes priority over the bridge's `:root` mapping. Chat appears in its OWN dark palette (not design-tokens dark). Acceptable for this PR; visual unification is PR 3 scope. + +## Out-of-scope follow-ups (track but defer) + +- Chat lib text-wrap bug (`"hello"` rendering as `hel`/`lo`) — PR 2 +- Chat lib internal palette migration to `var(--ds-*)` — PR 3 or later +- Doc h1/h2/h3 size tokenization (style analysis recommendation #4) — PR 3 +- Website migration to `cssVars('light')` from hardcoded hex (style analysis recommendation #3) — PR 3 +- Stage 2 (remaining 31 example apps) — separate plan/spec already drafted in earlier spec From c1e16a11852b1f561cea21b3f411a3eb203192ad Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:20:18 -0700 Subject: [PATCH 2/8] docs: cockpit polish implementation plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6 tasks: chat lib data-theme rename, installEmbeddedTheme cleanup, cockpit.css token migration + dead-code drop, inline border → Tailwind arbitrary values across 6 component files, version bumps, PR ship. Co-Authored-By: Claude Opus 4.7 --- .../plans/2026-05-13-cockpit-polish.md | 482 ++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-13-cockpit-polish.md diff --git a/docs/superpowers/plans/2026-05-13-cockpit-polish.md b/docs/superpowers/plans/2026-05-13-cockpit-polish.md new file mode 100644 index 000000000..557a1c160 --- /dev/null +++ b/docs/superpowers/plans/2026-05-13-cockpit-polish.md @@ -0,0 +1,482 @@ +# Cockpit Polish 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:** Ship cockpit polish — chat lib theme-attribute rename, cockpit.css hardcoded-color cleanup, border-pattern standardization on Tailwind arbitrary values, and revert of the in-flight `data-ngaf-chat-theme` shim in `installEmbeddedTheme`. + +**Architecture:** The chat lib already implements a three-layer cascade (default → `prefers-color-scheme` → programmatic override) — rename only the override selector to `[data-theme]`. Cockpit's `cockpit.css` has dead `@theme inline` + shadcn alias blocks (no Tailwind utilities consume them); replace with direct `var(--ds-*)` references. Border styles standardize on Tailwind arbitrary-value classes (`border-b border-[var(--ds-border)]`). + +**Tech Stack:** Tailwind v4, Angular 21, Vitest, Nx, npm workspaces. + +**Spec:** `docs/superpowers/specs/2026-05-13-cockpit-polish-design.md` + +--- + +## File Map + +| Action | File | Responsibility | +|---|---|---| +| Modify | `libs/chat/src/lib/styles/chat-tokens.ts` | Rename `data-ngaf-chat-theme` → `data-theme` (4 selectors + 2 doc comments) | +| Modify | `libs/example-layouts/src/lib/install-embedded-theme.ts` | Remove `dataset.ngafChatTheme` line + comment (added in-flight, no longer needed) | +| Modify | `apps/cockpit/src/app/cockpit.css` | Drop `@theme inline` + shadcn alias block; replace 12 hardcoded rgba literals with `var(--ds-*)` | +| Modify | `apps/cockpit/src/components/cockpit-shell.tsx` | Border inline → Tailwind class (already keeps edge-to-edge padding fix from in-flight) | +| Modify | `apps/cockpit/src/components/mobile-nav-overlay.tsx` | Border inline → Tailwind class | +| Modify | `apps/cockpit/src/components/api-mode/api-mode.tsx` | `borderBottomColor` inline → Tailwind class | +| Modify | `apps/cockpit/src/components/sidebar/cockpit-sidebar.tsx` | `borderRightColor` inline → Tailwind class | +| Modify | `apps/cockpit/src/components/sidebar/navigation-groups.tsx` | `borderLeft` inline → conditional Tailwind class | +| Modify | `apps/cockpit/src/components/code-mode/code-mode.tsx` | Hardcoded `rgba(255,255,255,0.06)` → `var(--ds-border)` | +| Modify | `libs/chat/package.json` | Patch bump | +| Modify | `libs/example-layouts/package.json` | Patch bump | + +--- + +## Task 1: Chat lib attribute rename + +**Files:** +- Modify: `libs/chat/src/lib/styles/chat-tokens.ts` + +The chat lib already has the right three-layer cascade — only the override selector needs renaming. Six instances of the literal string `data-ngaf-chat-theme` exist in the file (4 in CSS selectors, 2 in JSDoc comments). + +- [ ] **Step 1: Read current file to confirm exact line content** + +```bash +grep -n "data-ngaf-chat-theme" libs/chat/src/lib/styles/chat-tokens.ts +``` + +Expected output: six lines (175, 177, 190, 191, 192, 193). + +- [ ] **Step 2: Rename the attribute everywhere in this file** + +Use Edit's `replace_all` flag on the literal string `data-ngaf-chat-theme` → `data-theme`. + +After the edit, the file should contain selectors: + +```ts +:root[data-theme="light"], +[data-theme="light"] { ${LIGHT_TOKENS} } +:root[data-theme="dark"], +[data-theme="dark"] { ${DARK_TOKENS} } +``` + +And JSDoc references like `[data-theme="dark"]` and `[data-theme="light"]`. + +- [ ] **Step 3: Verify no other files in the chat lib reference the old attribute** + +```bash +rg "data-ngaf-chat-theme" libs/chat +``` + +Expected: no matches. + +- [ ] **Step 4: Run chat lib tests** + +```bash +pnpm nx test chat +``` + +Expected: all green. (No test currently asserts the old attribute string, so the rename should be invisible to tests. If a test does fail because it asserts the literal `data-ngaf-chat-theme`, update the assertion to `data-theme`.) + +- [ ] **Step 5: Commit** + +```bash +git add libs/chat/src/lib/styles/chat-tokens.ts +git commit -m "refactor(chat): rename theme attribute data-ngaf-chat-theme → data-theme" +``` + +--- + +## Task 2: Drop redundant `dataset.ngafChatTheme` in `installEmbeddedTheme` + +**Files:** +- Modify: `libs/example-layouts/src/lib/install-embedded-theme.ts` + +After Task 1's rename, the chat lib reads `data-theme` directly — no need to also set `data-ngaf-chat-theme`. Revert the in-flight change. + +- [ ] **Step 1: Edit the file** + +Open `libs/example-layouts/src/lib/install-embedded-theme.ts`. The current `apply` function (after the in-flight change) looks like: + +```ts +const apply = (theme: Theme) => { + document.documentElement.dataset.theme = theme; + // @ngaf/chat keys its internal tokens off `data-ngaf-chat-theme`, + // not `data-theme`. Set both so the chat lib flips alongside the + // design-tokens palette. + document.documentElement.dataset.ngafChatTheme = theme; + const vars = cssVars(theme) as Record; + for (const [key, value] of Object.entries(vars)) { + document.documentElement.style.setProperty(key, value); + } +}; +``` + +Remove the comment block and the `dataset.ngafChatTheme` line. Result: + +```ts +const apply = (theme: Theme) => { + document.documentElement.dataset.theme = theme; + const vars = cssVars(theme) as Record; + for (const [key, value] of Object.entries(vars)) { + document.documentElement.style.setProperty(key, value); + } +}; +``` + +Also update the docblock above the function. Current: + +```ts +/** + * Bootstraps an embedded example app's theme. Call once before the + * framework (Angular, Vue, etc.) bootstraps. + * + * Behavior: + * 1. Applies the default theme synchronously: sets `data-theme` and + * `data-ngaf-chat-theme` on `` (the latter tells `@ngaf/chat` + * to use its matching dark/light token set), plus every `--ds-*` + * CSS variable on the same element. + * ... +``` + +Replace the relevant paragraph with: + +```ts +/** + * Bootstraps an embedded example app's theme. Call once before the + * framework (Angular, Vue, etc.) bootstraps. + * + * Behavior: + * 1. Applies the default theme synchronously: sets `data-theme` on + * `` (which both `@ngaf/design-tokens`-aware code and + * `@ngaf/chat` honor) plus every `--ds-*` CSS variable on the + * same element. + * ... +``` + +- [ ] **Step 2: Run example-layouts tests** + +```bash +pnpm nx test example-layouts +``` + +Expected: all green. The existing test asserts `data-theme` is set; it never asserted `data-ngaf-chat-theme`, so removing that line doesn't break the test surface. + +- [ ] **Step 3: Commit** + +```bash +git add libs/example-layouts/src/lib/install-embedded-theme.ts +git commit -m "refactor(example-layouts): drop redundant data-ngaf-chat-theme set" +``` + +--- + +## Task 3: Rewrite `cockpit.css` — drop dead aliases, migrate literals to tokens + +**Files:** +- Modify: `apps/cockpit/src/app/cockpit.css` + +Two things in this task: +- **Drop dead code**: the `@theme inline` block (lines 3-15) and the shadcn `:root` alias block (lines 17-30). Grep confirms no Tailwind utility in cockpit consumes `bg-background`, `bg-card`, `text-muted`, etc. — they're entirely unused. +- **Replace 12 hardcoded rgba literals** with `var(--ds-*)` tokens. + +- [ ] **Step 1: Read the current file** + +```bash +cat apps/cockpit/src/app/cockpit.css +``` + +Note the exact line numbers of `@theme inline` (around 3-15), `:root` block (17-30), and the rgba literals. + +- [ ] **Step 2: Delete the `@theme inline` block and the `:root` shadcn alias block** + +Replace the top of the file from `@import "tailwindcss";` through the end of the `:root` block: + +```css +@import "tailwindcss"; +``` + +The `@theme inline` block goes away (those `--color-*` vars had no consumers). The `:root` block with `--background`, `--foreground`, `--primary`, `--card`, `--muted`, `--border`, `--input`, `--ring` goes away (also no consumers). + +- [ ] **Step 3: Replace `rgba(0, 64, 144, 0.04)` literals** + +In `cockpit.css`, find lines containing `rgba(0, 64, 144, 0.04)` (callout backgrounds, two sites). Replace with `var(--ds-accent-surface)`. + +- [ ] **Step 4: Replace `rgba(0, 64, 144, 0.06)` literal** + +Find the line containing `rgba(0, 64, 144, 0.06)` (was the shadcn `--muted` alias — but if the alias block is gone, this literal may not appear anymore; verify with grep). If it does appear elsewhere, replace with `var(--ds-accent-surface)`. + +- [ ] **Step 5: Replace `rgba(0, 64, 144, 0.08)` literal** + +Find `rgba(0, 64, 144, 0.08)` (the bottom border around line 227). Replace with `var(--ds-accent-border)`. + +- [ ] **Step 6: Replace `rgba(0, 64, 144, 0.12)` literals** + +Find each `rgba(0, 64, 144, 0.12)` (callout borders, divider). Replace with `var(--ds-accent-border)`. + +- [ ] **Step 7: Replace `rgba(0, 64, 144, 0.15)` literals** + +Find each `rgba(0, 64, 144, 0.15)` (active state highlights, around lines 108 and 149). Replace with `var(--ds-accent-border)`. + +- [ ] **Step 8: Replace the dark-assumed `rgba(26, 27, 38, 0.95)`** + +Find the single occurrence of `rgba(26, 27, 38, 0.95)` (code-block header bg, around line 142). Replace with `var(--ds-surface)`. + +- [ ] **Step 9: Verify no rgba literals remain** + +```bash +grep -nE "rgba\(0, 64, 144|rgba\(26, 27, 38" apps/cockpit/src/app/cockpit.css +``` + +Expected: no matches. + +- [ ] **Step 10: Verify no orphaned references to dropped shadcn aliases** + +```bash +grep -nE "var\(--(background|foreground|primary|card|muted|border|input|ring)\)\b" apps/cockpit/src/app/cockpit.css +``` + +Expected: no matches. (If any remain — they referenced the now-deleted block — replace with appropriate `var(--ds-*)`. The bottom `border-bottom: 1px solid var(--border)` should become `border-bottom: 1px solid var(--ds-border)`.) + +- [ ] **Step 11: Run cockpit build to confirm CSS still compiles** + +```bash +pnpm nx build cockpit +``` + +Expected: clean build. Any unresolved CSS variable would surface as a build warning. + +- [ ] **Step 12: Commit** + +```bash +git add apps/cockpit/src/app/cockpit.css +git commit -m "refactor(cockpit): drop dead shadcn aliases, migrate hardcoded rgba to --ds-* tokens" +``` + +--- + +## Task 4: Migrate inline border styles to Tailwind arbitrary values + +**Files:** +- Modify: `apps/cockpit/src/components/cockpit-shell.tsx` +- Modify: `apps/cockpit/src/components/mobile-nav-overlay.tsx` +- Modify: `apps/cockpit/src/components/api-mode/api-mode.tsx` +- Modify: `apps/cockpit/src/components/sidebar/cockpit-sidebar.tsx` +- Modify: `apps/cockpit/src/components/sidebar/navigation-groups.tsx` +- Modify: `apps/cockpit/src/components/code-mode/code-mode.tsx` + +Six files have inline border styles. Migrate each to the Tailwind arbitrary-value pattern (`border-b border-[var(--ds-border)]`). Note that some preserve the in-flight padding fix in `cockpit-shell.tsx`. + +- [ ] **Step 1: `cockpit-shell.tsx`** + +Currently in the `
` element (around line 84): + +```tsx +
+``` + +Replace with: + +```tsx +
+``` + +(The `style={...}` prop and its contents go away. The `px-4 py-3` from the in-flight padding fix is preserved.) + +- [ ] **Step 2: `mobile-nav-overlay.tsx`** + +Find the line with `style={{ borderBottom: '1px solid var(--ds-border)' }}`. Migrate the same way: add `border-b border-[var(--ds-border)]` to the element's `className`, drop the `style` prop. + +- [ ] **Step 3: `api-mode/api-mode.tsx`** + +Find the line containing `borderBottomColor: 'var(--ds-accent-border)'`. The element likely has `style={{ borderBottom: '1px solid', borderBottomColor: '...' }}` or similar. Read context (3-5 lines before/after) to understand the full inline-style, then replace with: + +```tsx +className=" border-b border-[var(--ds-accent-border)]" +``` + +Drop the inline `borderBottom`/`borderBottomColor`/`borderBottomStyle` properties from `style={}`. If the only `style` prop entries were the border ones, drop the entire `style` prop. + +- [ ] **Step 4: `sidebar/cockpit-sidebar.tsx`** + +Find the line containing `borderRightColor: 'var(--ds-border-strong)'`. Read context — likely paired with `borderRight: '1px solid'` or similar. Replace inline with: + +```tsx +className=" border-r border-[var(--ds-border-strong)]" +``` + +- [ ] **Step 5: `sidebar/navigation-groups.tsx`** + +Find the line containing `borderLeft: isActive ? '2px solid var(--ds-accent)' : 'none'`. This is a conditional pattern — needs to stay conditional. Replace with Tailwind classes via conditional: + +```tsx +className={` ${isActive ? 'border-l-2 border-[var(--ds-accent)]' : 'border-l-2 border-transparent'}`} +``` + +The `border-l-2 border-transparent` on the inactive branch reserves the layout space so the active state doesn't shift the row. (If the existing inline style was using `2px` only on active and `none` on inactive, content shifts when active toggles. Preserving the transparent border keeps layout stable. Verify visually after edit.) + +If the existing behavior was intentionally shifting on activation, omit the `border-transparent` and use empty string on the inactive branch. + +- [ ] **Step 6: `code-mode/code-mode.tsx`** + +Find the line `borderBottom: '1px solid rgba(255,255,255,0.06)'`. This is a dark-mode-assumed hardcoded color. Replace with token-driven Tailwind: + +```tsx +className=" border-b border-[var(--ds-border)]" +``` + +Drop the inline `borderBottom` from `style={}`. If `style` is now empty, drop it. + +- [ ] **Step 7: Verify no inline border styles remain in cockpit components** + +```bash +rg -g '*.tsx' "borderBottom|borderRight|borderTop|borderLeft" apps/cockpit/src/components +``` + +Expected: no matches. + +- [ ] **Step 8: Run cockpit build** + +```bash +pnpm nx build cockpit +``` + +Expected: clean build. + +- [ ] **Step 9: Commit** + +```bash +git add apps/cockpit/src/components +git commit -m "refactor(cockpit): migrate inline border styles to Tailwind arbitrary-value classes" +``` + +--- + +## Task 5: Version bumps + full check stack + +**Files:** +- Modify: `libs/chat/package.json` (patch bump) +- Modify: `libs/example-layouts/package.json` (patch bump) + +- [ ] **Step 1: Read current versions** + +```bash +grep '"version"' libs/chat/package.json libs/example-layouts/package.json +``` + +- [ ] **Step 2: Bump patch versions** + +Increment the last digit of each. Patch-only release rule applies — never bump to 0.1.x. + +- [ ] **Step 3: Run the full check stack** + +```bash +pnpm nx run-many -t lint,test -p design-tokens,ui-react,example-layouts,chat,cockpit +``` + +Expected: all green. + +```bash +pnpm nx e2e cockpit +``` + +Expected: all green. + +```bash +pnpm nx build website +``` + +Expected: green. + +```bash +pnpm nx build cockpit-chat-timeline-angular +``` + +Expected: green. + +If any failure surfaces: +- **Chat lib tests** — likely a test asserted the literal `data-ngaf-chat-theme` somewhere. Update assertion to `data-theme`. +- **Cockpit tests** — possibly a snapshot test or a CSS-class-presence test referencing the old `--muted` / `--border` aliases. Update the test to use `--ds-*` references. +- **Cockpit e2e** — if any test asserted a specific color value, it may shift slightly (alpha 0.04 → 0.06 etc.). Update the assertion to match the new computed color. +- **Cockpit build** — unresolved CSS var; grep cockpit.css to find which `var(--*)` reference doesn't resolve. + +- [ ] **Step 4: Commit version bumps** + +```bash +git add libs/chat/package.json libs/example-layouts/package.json +git commit -m "chore: bump chat and example-layouts patch versions" +``` + +--- + +## Task 6: Open PR + merge on green + +- [ ] **Step 1: Push branch** + +```bash +git push -u origin cockpit-polish +``` + +- [ ] **Step 2: Open PR** + +```bash +gh pr create --title "refactor(cockpit): polish — chat lib data-theme, token migration, border standardization" --body "$(cat <<'EOF' +## Summary + +First in a three-PR sequence (spec: \`docs/superpowers/specs/2026-05-13-cockpit-polish-design.md\`). + +- **Chat lib theme attribute rename:** \`[data-ngaf-chat-theme]\` → \`[data-theme]\`. Aligns with Tailwind v4 / shadcn / Storybook conventions. The lib's existing three-layer cascade (default → \`prefers-color-scheme\` → programmatic override) is preserved — only the override selector renames. +- **\`installEmbeddedTheme\` cleanup:** drop the in-flight \`dataset.ngafChatTheme\` set. After the chat lib rename, \`data-theme\` is sufficient. +- **\`cockpit.css\` token migration:** drop dead \`@theme inline\` block and unused shadcn alias \`:root\` block (no Tailwind utility in cockpit consumed them). Replace 12 hardcoded \`rgba(0, 64, 144, X)\` and \`rgba(26, 27, 38, 0.95)\` literals with \`--ds-*\` tokens — code blocks and callouts now theme correctly in light AND dark. +- **Border standardization:** migrate ~6 inline \`style={{ borderBottom: ... }}\` sites to Tailwind arbitrary-value classes (\`border-b border-[var(--ds-border)]\`). Consistent with cockpit's dominant Tailwind-arbitrary-value pattern. +- Preserves edge-to-edge content padding in \`cockpit-shell.tsx\` (from in-flight pre-spec change). + +PR 2 (chat lib polish — text-wrap bug + bubble width handling) and PR 3 (cockpit ↔ website style alignment) follow. + +## Test plan + +- [x] \`pnpm nx run-many -t lint,test -p design-tokens,ui-react,example-layouts,chat,cockpit\` — green +- [x] \`pnpm nx e2e cockpit\` — green +- [x] \`pnpm nx build website\` — green +- [x] \`pnpm nx build cockpit-chat-timeline-angular\` — green +- [ ] Manual chrome MCP smoke: cockpit + timeline pilot in light + dark — no white callouts in dark, no dark code-block headers in light, chat input flips with host theme + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +EOF +)" +``` + +- [ ] **Step 3: Wait for green CI** + +```bash +gh pr checks --watch +``` + +- [ ] **Step 4: Squash-merge** + +```bash +gh pr merge --squash --delete-branch +``` + +--- + +## Self-review + +**Spec coverage:** +- ✅ Decision 1 (chat lib attribute rename) → Task 1 +- ✅ Decision 2 (border/divider Tailwind convention) → Task 4 +- ✅ Decision 3 (hardcoded color mapping) → Task 3 +- ✅ Decision 4 (no backwards compat — drop shadcn aliases) → Task 3 (drop `@theme inline` + `:root` blocks) +- ✅ Decision 5 (installEmbeddedTheme cleanup) → Task 2 + +**Adjustments from spec during plan-prep exploration:** +1. **Bonus dead-code finding:** the `@theme inline` Tailwind v4 token block in `cockpit.css` is also unused — no `bg-background`/`bg-card`/`text-muted` utilities in cockpit source. Dropping the whole `@theme inline` block alongside the `:root` aliases (spec only mentioned the `:root` block, but plan extends the cleanup since they're paired and equally dead). +2. **`code-mode.tsx` has its own hardcoded color** — `rgba(255,255,255,0.06)` border. Not in spec's 12-literal count but discovered during plan-prep and added to Task 4 Step 6. +3. **Two more inline-border sites than spec estimated** — `api-mode.tsx` borderBottomColor and `navigation-groups.tsx` borderLeft (conditional). All covered in Task 4. + +**Placeholder scan:** No "TBD" / "TODO". One conditional pattern in Task 4 Step 5 (the `border-transparent` for layout stability) is described with reasoning, not gestured at. + +**Type consistency:** No new types. Attribute name `data-theme` consistent across Tasks 1 and 2. CSS var names `--ds-accent-surface`, `--ds-accent-border`, `--ds-border`, `--ds-border-strong`, `--ds-accent` consistent across Tasks 3 and 4. From ad81dcbb661bed1e4ba56b6d9be8e3790f86aaff Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:25:31 -0700 Subject: [PATCH 3/8] =?UTF-8?q?refactor(chat):=20rename=20theme=20attribut?= =?UTF-8?q?e=20data-ngaf-chat-theme=20=E2=86=92=20data-theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/chat/src/lib/styles/chat-tokens.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/chat/src/lib/styles/chat-tokens.ts b/libs/chat/src/lib/styles/chat-tokens.ts index 7496c9a64..82acafd76 100644 --- a/libs/chat/src/lib/styles/chat-tokens.ts +++ b/libs/chat/src/lib/styles/chat-tokens.ts @@ -172,9 +172,9 @@ const REDUCED_MOTION_STYLES = ` * * Theme switching: * - `prefers-color-scheme: dark` → dark by default. - * - `[data-ngaf-chat-theme="dark"]` on `` / `` / any wrapper + * - `[data-theme="dark"]` on `` / `` / any wrapper * forces dark. - * - `[data-ngaf-chat-theme="light"]` forces light. + * - `[data-theme="light"]` forces light. */ export const ROOT_TOKEN_STYLES = ` @layer ngaf-chat { @@ -187,10 +187,10 @@ export const ROOT_TOKEN_STYLES = ` @media (prefers-color-scheme: dark) { :root { ${DARK_TOKENS} } } - :root[data-ngaf-chat-theme="light"], - [data-ngaf-chat-theme="light"] { ${LIGHT_TOKENS} } - :root[data-ngaf-chat-theme="dark"], - [data-ngaf-chat-theme="dark"] { ${DARK_TOKENS} } + :root[data-theme="light"], + [data-theme="light"] { ${LIGHT_TOKENS} } + :root[data-theme="dark"], + [data-theme="dark"] { ${DARK_TOKENS} } } ${KEYFRAMES} ${REDUCED_MOTION_STYLES} From 05c73d36f5432979d66771895ac9e635b7761d75 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:34:29 -0700 Subject: [PATCH 4/8] refactor(example-layouts): drop redundant data-ngaf-chat-theme set --- libs/example-layouts/src/lib/install-embedded-theme.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/example-layouts/src/lib/install-embedded-theme.ts b/libs/example-layouts/src/lib/install-embedded-theme.ts index 4f68cb9a2..1f4ef977d 100644 --- a/libs/example-layouts/src/lib/install-embedded-theme.ts +++ b/libs/example-layouts/src/lib/install-embedded-theme.ts @@ -5,8 +5,10 @@ import { cssVars, type Theme } from '@ngaf/design-tokens'; * framework (Angular, Vue, etc.) bootstraps. * * Behavior: - * 1. Applies the default theme synchronously (sets `data-theme` and - * every `--ds-*` CSS variable on `document.documentElement`). + * 1. Applies the default theme synchronously: sets `data-theme` on + * `` (which both `@ngaf/design-tokens`-aware code and + * `@ngaf/chat` honor) plus every `--ds-*` CSS variable on the + * same element. * 2. Posts `{ type: 'ngaf:theme-request' }` to `window.parent` so the * host (cockpit's ``) replies with the current theme * even if its broadcast ran before this iframe mounted. From cba6dc8b9b103bc302dde500528cfe11e54ea056 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:38:47 -0700 Subject: [PATCH 5/8] =?UTF-8?q?refactor(chat):=20rename=20data-ngaf-chat-t?= =?UTF-8?q?heme=20=E2=86=92=20data-theme=20in=20chat.css=20(missed=20in=20?= =?UTF-8?q?Task=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/chat/src/lib/styles/chat.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/chat/src/lib/styles/chat.css b/libs/chat/src/lib/styles/chat.css index bc0b5107b..db51d592f 100644 --- a/libs/chat/src/lib/styles/chat.css +++ b/libs/chat/src/lib/styles/chat.css @@ -145,7 +145,7 @@ } @media (prefers-color-scheme: dark) { - :root:not([data-ngaf-chat-theme="light"]) { + :root:not([data-theme="light"]) { --ngaf-chat-bg: rgb(17, 17, 17); --ngaf-chat-surface: rgb(28, 28, 28); --ngaf-chat-surface-alt: rgb(44, 44, 44); @@ -165,7 +165,7 @@ } } -[data-ngaf-chat-theme="dark"] { +[data-theme="dark"] { --ngaf-chat-bg: rgb(17, 17, 17); --ngaf-chat-surface: rgb(28, 28, 28); --ngaf-chat-surface-alt: rgb(44, 44, 44); From 6e32f8c5d8a4dc1de69ede80ea6452bf5d42b7b5 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:44:01 -0700 Subject: [PATCH 6/8] refactor(cockpit): drop dead shadcn aliases, migrate hardcoded rgba to --ds-* tokens --- apps/cockpit/src/app/cockpit.css | 49 +++++++------------------------- 1 file changed, 10 insertions(+), 39 deletions(-) diff --git a/apps/cockpit/src/app/cockpit.css b/apps/cockpit/src/app/cockpit.css index 7983dcbad..56e71943e 100644 --- a/apps/cockpit/src/app/cockpit.css +++ b/apps/cockpit/src/app/cockpit.css @@ -1,34 +1,5 @@ @import "tailwindcss"; -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); -} - -:root { - /* shadcn semantic vars — light palette */ - --background: #f8f9fc; - --foreground: #1a1a2e; - --primary: #004090; - --primary-foreground: #ffffff; - --card: rgba(255, 255, 255, 0.45); - --card-foreground: #1a1a2e; - --muted: rgba(0, 64, 144, 0.06); - --muted-foreground: #555770; - --border: rgba(0, 64, 144, 0.15); - --input: rgba(0, 64, 144, 0.15); - --ring: #004090; -} - /* Shiki code blocks — preserve dark background from theme */ pre.shiki { padding: 1rem; @@ -41,8 +12,8 @@ pre.shiki { /* ── Doc components ────────────────────────────────────────── */ .doc-summary { - background: rgba(0, 64, 144, 0.04); - border: 1px solid rgba(0, 64, 144, 0.12); + background: var(--ds-accent-surface); + border: 1px solid var(--ds-accent-border); border-radius: 0.5rem; padding: 0.75rem 1rem; margin-bottom: 1.5rem; @@ -67,8 +38,8 @@ pre.shiki { } .doc-callout__content { color: var(--ds-text-secondary); } .doc-callout--tip { - background: rgba(0, 64, 144, 0.04); - border: 1px solid rgba(0, 64, 144, 0.12); + background: var(--ds-accent-surface); + border: 1px solid var(--ds-accent-border); } .doc-callout--tip .doc-callout__label { color: var(--ds-accent); } .doc-callout--note { @@ -105,7 +76,7 @@ pre.shiki { .doc-step__line { width: 2px; flex: 1; - background: rgba(0, 64, 144, 0.15); + background: var(--ds-accent-border); margin: 0.375rem 0; min-height: 1rem; } @@ -127,7 +98,7 @@ pre.shiki { .doc-step__content pre.shiki { margin: 0.5rem 0; border-radius: 0.5rem; } .doc-codeblock { - border: 1px solid rgba(0, 64, 144, 0.12); + border: 1px solid var(--ds-accent-border); border-radius: 0.5rem; overflow: hidden; margin: 0.75rem 0; @@ -139,14 +110,14 @@ pre.shiki { gap: 0.5rem; padding: 0.4rem 0.75rem; border-bottom: 1px solid rgba(138, 170, 214, 0.12); - background: rgba(26, 27, 38, 0.95); + background: var(--ds-surface); font-size: 0.7rem; } .doc-codeblock__file { color: #a9b1d6; font-family: var(--font-mono); } .doc-codeblock__lang { padding: 0.1rem 0.35rem; border-radius: 0.2rem; - background: rgba(0, 64, 144, 0.15); + background: var(--ds-accent-border); color: var(--ds-accent-light); font-size: 0.6rem; } @@ -220,11 +191,11 @@ pre.shiki { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.06em; - border-bottom: 1px solid var(--border); + border-bottom: 1px solid var(--ds-border); } .doc-api-table td { padding: 0.5rem 0.75rem; - border-bottom: 1px solid rgba(0, 64, 144, 0.08); + border-bottom: 1px solid var(--ds-accent-border); color: var(--ds-text-secondary); } .doc-api-table code { From 5032db552b6599120b8a7073445dcfef01a92c66 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 13 May 2026 16:51:15 -0700 Subject: [PATCH 7/8] refactor(cockpit): migrate inline border styles to Tailwind arbitrary-value classes --- apps/cockpit/src/components/api-mode/api-mode.tsx | 3 +-- apps/cockpit/src/components/cockpit-shell.tsx | 5 ++--- apps/cockpit/src/components/code-mode/code-mode.tsx | 5 +++-- apps/cockpit/src/components/mobile-nav-overlay.tsx | 3 +-- apps/cockpit/src/components/sidebar/cockpit-sidebar.tsx | 3 +-- apps/cockpit/src/components/sidebar/navigation-groups.tsx | 4 ++-- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/apps/cockpit/src/components/api-mode/api-mode.tsx b/apps/cockpit/src/components/api-mode/api-mode.tsx index 342e5758b..cf2884580 100644 --- a/apps/cockpit/src/components/api-mode/api-mode.tsx +++ b/apps/cockpit/src/components/api-mode/api-mode.tsx @@ -37,10 +37,9 @@ function DocArticle({ section }: { section: DocSection }) { }} >
diff --git a/apps/cockpit/src/components/cockpit-shell.tsx b/apps/cockpit/src/components/cockpit-shell.tsx index a0cfcde54..273fdd273 100644 --- a/apps/cockpit/src/components/cockpit-shell.tsx +++ b/apps/cockpit/src/components/cockpit-shell.tsx @@ -79,10 +79,9 @@ export function CockpitShell({ onClose={() => setIsSidebarOpen(false)} /> -
+