- 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
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
-
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. -
Tokens in
@vmall/shared/theme.css, replacingui.css. Shared stays the contract package (types, API, locales, theme). Vue is not added to shared. -
New
@vmall/uiforVBtn,VBadge,VField/VInput,VCard/VPanel,VTable,VPage, anduseAccent(). Mall-only chrome (mega-menu, carousel,w1200shell, product-card lift) stays inapps/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/.mbtnas 10-class strings will rot. -
No
@applyleftover stylesheets. Layout = utilities; repeating chrome = kit. Spec-locked sizes become theme keys (max-w-mall,h-hero) rather than scatteredw-[1200px]. -
Accent via
html[data-accent]mapping CSS variables that@themealiases toprimary.useAccent()writeslocalStorage(vmall-accentper app origin) and sets the dataset. Mall setsdata-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. -
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 /
#ca151eas 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.csschange still requires all three app builds. - [Scope creep vs catalog split] → Separate OpenSpec change; do not mix PRs.
Migration Plan
- Scaffold
theme.css+@vmall/ui+ Vite plugin; keep loadingui.css/mall.css. - Convert platform admin, then shop-admin (introduce accent swatches).
- Convert mall shell, then mall pages (largest last).
- Delete
ui.css,mall.css, remaining<style scoped>; switch shared export totheme.css; updateAGENTS.md. - 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.