Skip to content

feat(ui): scaffold packages/ui with theme foundations#1030

Open
EhabY wants to merge 4 commits into
mainfrom
feat/devex-624-ui-scaffold
Open

feat(ui): scaffold packages/ui with theme foundations#1030
EhabY wants to merge 4 commits into
mainfrom
feat/devex-624-ui-scaffold

Conversation

@EhabY

@EhabY EhabY commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

🤖 This PR was created by Coder Agents on behalf of @EhabY.

Closes DEVEX-624.

Scaffolds the shared component library for the Workspaces webview work. Foundations only — no components yet (DEVEX-618/619/620 build on this).

  • packages/ui — React + TS package wired like existing packages (catalog deps, root Storybook/ESLint/typecheck globs pick it up automatically). Consumed as source like webview-shared, but shaped for a future standalone NPM split: proper exports map (., ./tokens.css, ./codicon.css), react as the only peer dependency, @vscode/codicons (asset-only) as the only runtime dependency, no workspace:* runtime deps. A short README documents the theming model and the rules that keep the package splittable.
  • tokens.css — semantic --ui-* tokens mapped to --vscode-* variables; the single place the library is theme-matched to VS Code. Same pattern as Microsoft's Webview UI Toolkit design-token layer, but pure CSS: components style against a small curated semantic set instead of scattering raw --vscode-* references (VS Code exposes 500+). Every token always resolves: variables VS Code may omit (hover/selection fills in high contrast themes, contrastBorder/contrastActiveBorder outside them) declare fallbacks, matching native behavior where contrast borders replace fills.
  • useVscodeTheme — the issue asked to move it from webview-shared, but no such hook existed anywhere in the repo, so it is implemented here instead: returns the active theme kind from the data-vscode-theme-kind body attribute VS Code maintains in webviews, synced via MutationObserver + useSyncExternalStore. No re-export from webview-shared since there are no consumers yet.
  • Storybook themes — the switcher now covers all four theme kinds (Light, Dark, High Contrast, High Contrast Light), and the decorator mirrors data-vscode-theme-kind on the body so theme-aware hooks behave as in real webviews. Theme data is no longer vendored: the two existing ~700-line theme files are deleted and all four themes import from @vscode-elements/webview-playground (the package the vendored data was originally copied from), pinned via the catalog. Net: this PR removes ~1,500 lines from the repo.
  • UI/Foundations story — enumerates every --ui-* token from the loaded stylesheets (cannot drift from tokens.css) and renders each as a swatch on a checkerboard with its resolved value, plus the live theme kind via the hook. Chromatic modes snapshot this story in all four themes (scoped to this story to keep snapshot counts down).

Testing

  • test/webview/ui/useVscodeTheme.test.ts — theme-kind mapping, missing-attribute default, re-render on theme change.
  • Story rendered headlessly against the production Storybook build in all four themes; all 16 tokens resolve to the expected values (e.g. --ui-active-selection-foreground is #000000/#ffffff in light/dark — VS Code really does snap selected-row text to full contrast) and fallbacks engage where variables are absent (HC dark omits selection fills; contrast borders take over).
  • pnpm typecheck, pnpm lint, pnpm format:check, pnpm test, pnpm build, pnpm storybook:ci all pass.

Note: webview-playground 1.9.0 carries newer VS Code theme captures than the previously vendored copy, so existing Chromatic baselines will show a one-time diff.

Implementation plan & decisions

Context

  • Monorepo already has packages/{shared,webview-shared,tasks,workspaces,netcheck,speedtest,mocks,storybook-utils} with pnpm catalog: deps (catalogMode: strict).
  • Root .storybook globs packages/*/src/**/*.stories.@(ts|tsx) — a packages/ui story is picked up with zero config.
  • Root typecheck:packages / build:webviews run pnpm -r --filter ./packages/* — packages without a build script are skipped (e.g. webview-shared, consumed as source via its exports map).

Finding: useVscodeTheme did not exist

The issue says "move useVscodeTheme from packages/webview-shared", but no such hook exists on main or any branch. Decision: implement it fresh in packages/ui (DOM data-vscode-theme-kind + MutationObserver via useSyncExternalStore).

Decisions

  1. useVscodeTheme: implement fresh (nothing to move).
  2. Build: source-consumed like webview-shared (no vite build), but kept easy to separate later — self-contained package, proper exports map, react peer dep, no workspace:* deps. A real build step only becomes necessary at the actual NPM split.
  3. Codicon export: added now (./codicon.css re-exporting @vscode/codicons) since follow-up issues will use it.
  4. webview-shared re-export: skipped — no consumers exist yet, and it would add a workspace dep.
  5. Semantic token layer (vs raw --vscode-* per component, as vscode-elements does): the deprecated @vscode/webview-ui-toolkit validated the token-layer pattern but implemented it with a JS design-token system; a pure-CSS layer gets the same single theming point without the machinery, and gives the future standalone package one place to define non-webview fallbacks.
  6. No sideEffects field: it was tried for NPM-split readiness, but this Vite/rolldown pipeline then tree-shook the storybook.preview.tstokens.css chain out of the production Storybook build (even with **/*.css declared). Dev mode masked it. Dropped as speculative; revisit at the actual split on a pipeline that honors it.
  7. Radix: deferred to DEVEX-619, which explicitly scopes adding the only Radix runtime deps.
  8. Theme data from npm, not vendored: @vscode-elements/webview-playground publishes the exact theme modules the repo had copied by hand. Importing them (untyped ESM, typed via a small wildcard .d.ts) deletes ~2,800 vendored lines across four themes and makes theme refreshes a pnpm update.

Deliverables

  • packages/ui/{package.json,tsconfig.json,storybook.preview.ts,README.md} + src/{index.ts,tokens.css,codicon.css,useVscodeTheme.ts,foundations.stories.tsx,css.d.ts}
  • @repo/ui aliases in vitest.config.ts (webview project) and test/tsconfig.json
  • Storybook: four-theme switcher sourced from webview-playground, body-attribute mirroring, per-theme Chromatic modes on the foundations story
  • Unit tests in test/webview/ui/

@linear-code

linear-code Bot commented Jul 10, 2026

Copy link
Copy Markdown

DEVEX-624

@EhabY EhabY self-assigned this Jul 12, 2026
Source all VS Code themes (light, dark, hc-dark, hc-light) from
@vscode-elements/webview-playground instead of maintaining local
copies, and add per-theme Chromatic snapshot modes.

Also drops the sideEffects field that broke CSS tree-shaking, and
fixes the Foundations story's token value column inheriting an
unrelated --vscode-textPreformat-background instead of the row's own
background, which made values unreadable in both high-contrast themes.
@EhabY EhabY force-pushed the feat/devex-624-ui-scaffold branch from 086eaf9 to b9d3bea Compare July 12, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant