Files
vmall/openspec/changes/archive/2026-09-22-adopt-tailwind-design-system/design.md
T
Chengdong Zhang 0d0e10b97b feat(ui): adopt Tailwind v4 design system and archive change
- Add tailwindcss v4 + @tailwindcss/vite to mall, shop-admin, admin
- Add @vmall/shared/theme.css tokens with html[data-accent] presets
- Add @vmall/ui kit (VBtn/VBadge/VField/VInput/VCard/VPanel/VTable/VPage,
  VAccentSwatch, useAccent) as a Nuxt module
- Convert all three apps to kit + utilities; delete ui.css/mall.css and
  every <style scoped>; consoles get accent presets, mall locked to red
- Fix VCard boolean prop default (padding) and PDP/store stale
  useAsyncData keys on param navigation
- Archive adopt-tailwind-design-system; new frontend-ui capability spec
2026-09-22 18:22:50 +08:00

3.8 KiB

Context

Three Nuxt 3 apps share @vmall/shared (API types, locales, ui.css) and otherwise style themselves with global CSS plus Vue <style scoped> (~4k lines, mostly mall). There is no Tailwind/PostCSS today. Mall is a classic B2B2C PC chrome (#ca151e, 1200px, 450px hero); admin consoles are a blue SaaS shell. Operators asked for a full rewrite onto Tailwind, one design system, a visual refresh that keeps mall IA, red as the default accent, and preset accent switching in shop-admin and platform admin only.

Goals / Non-Goals

Goals:

  • Replace ui.css, mall.css, and all scoped CSS with Tailwind v4 utilities plus a small Vue kit.
  • One token set (red default) across mall, shop-admin, and platform admin.
  • Console accent presets (red | blue | teal | violet) persisted locally; mall locked to red.
  • Preserve mall shell behavior required by frontend-mall.

Non-Goals:

  • Dark mode, free hex picker, per-shop customer-facing branding, theming the public mall.
  • Element Plus or other component libraries.
  • Backend/API work; catalog module split.

Decisions

  1. Tailwind v4 via @tailwindcss/vite, not @nuxtjs/tailwindcss. Apps already run Nuxt 3 + Vite 7; v4 is CSS-first (@theme) and avoids a PostCSS/v3-oriented module.

  2. Tokens in @vmall/shared/theme.css, replacing ui.css. Shared stays the contract package (types, API, locales, theme). Vue is not added to shared.

  3. New @vmall/ui for VBtn, VBadge, VField/VInput, VCard/VPanel, VTable, VPage, and useAccent(). Mall-only chrome (mega-menu, carousel, w1200 shell, product-card lift) stays in apps/mall. Admin sidebar layouts stay in each admin app.

    Alternative considered: put Vue primitives in shared — rejected so the API contract package stays Vue-free. Alternative: utilities only, no kit — rejected; repeating .btn / .mbtn as 10-class strings will rot.

  4. No @apply leftover stylesheets. Layout = utilities; repeating chrome = kit. Spec-locked sizes become theme keys (max-w-mall, h-hero) rather than scattered w-[1200px].

  5. Accent via html[data-accent] mapping CSS variables that @theme aliases to primary. useAccent() writes localStorage (vmall-accent per app origin) and sets the dataset. Mall sets data-accent="red" at boot and does not render a switcher. Invalid stored values fall back to red.

    Alternative considered: user-profile API — out of scope. Alternative: class="dark" — not in this change.

  6. Phased conversion, then delete. Dual-load old CSS until each app is converted so screens are never unstyled; then remove globals and scoped blocks.

Risks / Trade-offs

  • [Mall visual regression] → Convert shell first; browser-pass spec flows (home hero, mega-menu, search grid, PDP); keep 1200px / 450px / #ca151e as tokens.
  • [Tailwind v4 + Nuxt 3 plugin friction] → Spike on admin app first; fall back to documented Vite plugin wiring, not a new CSS engine.
  • [Accent contrast on badges/tables] → Presets only; each preset defines primary + hover + soft background, not a raw hex.
  • [Shared package build gate] → theme.css change still requires all three app builds.
  • [Scope creep vs catalog split] → Separate OpenSpec change; do not mix PRs.

Migration Plan

  1. Scaffold theme.css + @vmall/ui + Vite plugin; keep loading ui.css / mall.css.
  2. Convert platform admin, then shop-admin (introduce accent swatches).
  3. Convert mall shell, then mall pages (largest last).
  4. Delete ui.css, mall.css, remaining <style scoped>; switch shared export to theme.css; update AGENTS.md.
  5. Rollback: git revert the change; no DB/API migration.

Open Questions

None that block implementation. If @tailwindcss/vite cannot register cleanly in a given Nuxt 3 minor, document the working plugin hook in this change rather than switching to UnoCSS.