word-addin: replace comment-enforced design parity with mechanisms

🟢 open · #396 · open-legal-products/mike ← amal66/mike · opened 12d ago by amal66 · +125-12 across 6 files · ↗ on GitHub

From the PR description

Summary

Two invariants in the Word add-in were enforced only by comments, and both had quietly stopped being true:

  1. word-addin/src/shared/styles/tokens.css hand-duplicates 121 design tokens from the web's globals.css, guarded by "Keep values in sync with globals.css." Nothing verified it, and four light-mode surface tokens drifted when the web palette moved (--app-background, --app-surface-hover, --app-surface-active, --app-floating) - the pane has been rendering slightly different surfaces than the web app it mirrors.
  2. word-addin/src/shared/ui/tab-pill-button.tsx was forked from the web's pill with "Only the cn import path differs." Also no longer true: the web version moved to the shared LiquidGlassUI constants and gained a focus-visible ring; the fork kept a stale hardcoded shadow and had no visible keyboard-focus state at all, against AGENTS.md's own accessibility baseline.

This PR turns both comments into mechanisms:

  • scripts/check-token-drift.mjs: parses both CSS files and requires every token the add-in declares (:root, .dark, @theme inline) to match globals.css exactly (web-only additions allowed - the add-in is a subset). Fails naming each drifted token, add-in vs web value; also fails if it parses zero tokens, so a file refactor can't silently no-op the check. Wired as npm run check:tokens inside npm run typecheck, which npm run build and the existing word-addin CI workflow already run - drift now fails every build and PR with zero new CI wiring. The four drifted values are fixed to the web's palette (web is the source of truth; it moved first, deliberately).
  • TabPillButton converges on the web implementation: same focus-visible ring cluster, same LIQUID_GLASS_SUBTLE_CLASS/LIQUID_GLASS_HOVER_CLASS constants, imported via a new @mike/liquid-glass-ui alias (the established webpack+tsconfig pattern for frontend/src/shared/ui modules). No new duplication - the pane already imports LiquidGlassUI.css and lists LiquidGlassUI.ts as a Tailwind @source; the fork just predated them.

Reproduce on main

# The guard is a comment, and it failed - four tokens differ from globals.css:
grep -n "Keep values in sync" word-addin/src/shared/styles/tokens.css
git checkout olp-pr/tokens-mechanism -- word-addin/scripts/check-token-drift.mjs
node word-addin/scripts/check-token-drift.mjs   # exits 1, names the 4 drifted tokens

# The pill has no focus indicator:
grep -c "focus-visible" word-addin/src/shared/ui/tab-pill-button.tsx   # 0

Verify on this branch

npm run check:tokens --prefix word-addin      # 121 add-in tokens match globals.css
npm run typecheck --prefix word-addin         # tsc app + e2e + token check, all green
grep -c "focus-visible" word-addin/src/shared/ui/tab-pill-button.tsx   # 1 (ring cluster present)

Keyboard check: build/serve the pane, Tab to the Add Documents modal's tab bar - each pill now shows the blue focus ring the web shows.

Demo

Live recording: the new checker run against main's tokens.css (4 drifted tokens named) -> this branch (121 match) -> the typecheck wiring -> the pill's focus-visible count, main vs branch:

PR #396 demo: token drift check and focus ring

Testing performed

  • npm run typecheck --prefix word-addin - green (tsc app, tsc e2e, token check).
  • npm run build:e2e --prefix word-addin - production webpack build succeeds with the new alias; verified the built bundle contains the liquid-glass-* classes, the focus-visible:ring-* utilities, and the corrected token values (--app-background: #f9fafb light / #0b0f14 dark).
  • Negative test: running the checker against main's tokens.css exits 1 and names exactly the four drifted tokens (recorded in the demo below).

Tradeoffs & design decisions

  • Check, not code-generation. Generating tokens.css from globals.css would remove the duplication entirely, but the add-in file is a deliberate adaptation (own header, font-variable notes, @custom-variant line), and a generator would need to own that structure. A fail-loud equality check gets the same guarantee with a fraction of the machinery; generation remains the natural upgrade if the token set starts churning.
  • Subset semantics. Web-only tokens don't fail the check - the add-in intentionally declares only what the pane uses. The cost: a web token rename shows up as "not defined in web" only because the add-in still carries the old name, which is the desired failure anyway.
  • The pill stays a duplicated file. Moving it into frontend/src/shared/ui/ per AGENTS.md's cross-target rule is the full fix, but the web copy imports from @/app/components/ui context and the move touches web consumers - out of scope for a mechanisms PR. The comment now states the real contract (shared constants, byte-identical class strings) instead of a false one.
  • Other hardcoded liquid-glass shadows remain in the pane (InitialView, ModalForm, ChatInput) - same fork pattern, not regressed by this PR, and each needs its own visual verification to converge; deliberately not batched in here.
  • Class-string parity for the pill is still comment-enforced (the token check does not read .tsx files). Extending the checker to diff the two pill files' class strings would be the next mechanism if this drifts again.

🤖 Generated with Claude Code

https://claude.ai/code/session_01S4VRDD1DZat4SLYtt2q2eU

Our analysis

Enforce Word add-in design parity — read the full analysis →

Think the analysis missed something the PR description covers?

Capture this PR into my fork

Download a Markdown prompt that tells Claude how to port every commit in this PR into your working tree. Run it via claude -p < capture-pull-396.md from inside the repo you want the changes in.

⬇ Download capture-pull-396.md