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
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-22
|
||||
@@ -0,0 +1,56 @@
|
||||
## 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.
|
||||
@@ -0,0 +1,33 @@
|
||||
## Why
|
||||
|
||||
Styling is split across `ui.css`, `mall.css`, and ~4k lines of Vue scoped CSS, with two palettes (mall red vs admin blue) and no shared primitives. Adopting Tailwind v4 plus one token/kit package lets us rewrite every surface, refresh visuals, and give consoles accent presets without a second CSS stack.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING**: Remove `@vmall/shared/ui.css` and `apps/mall/assets/mall.css`. Shared contract export becomes `theme.css`.
|
||||
- Add Tailwind v4 (`@tailwindcss/vite`) to mall, shop-admin, and platform admin.
|
||||
- Add `@vmall/ui`: Vue primitives (`VBtn`, `VBadge`, `VField`/`VInput`, `VCard`/`VPanel`, `VTable`, `VPage`) plus `useAccent()`.
|
||||
- One design system: commerce red (`#ca151e`) default; same radius/type/spacing on all three apps.
|
||||
- Mall keeps classic PC chrome (1200px grid, dark nav, mega-menu, 450px home hero) with refreshed tokens.
|
||||
- Shop-admin and platform admin: accent presets `red | blue | teal | violet`, persisted in `localStorage`. Mall stays red with no switcher.
|
||||
- Delete all Vue `<style scoped>` in the three apps after conversion.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `frontend-ui`: Shared Tailwind theme, UI kit, and console accent presets.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `frontend-mall`: Storefront MUST use the shared design system; visual IA (1200px / `#ca151e` / hero / mega-menu) stays; no public accent switcher.
|
||||
- `frontend-admin`: Platform console MUST use the shared kit and expose accent presets.
|
||||
- `frontend-shop-admin`: Merchant console MUST use the shared kit and expose accent presets.
|
||||
|
||||
## Impact
|
||||
|
||||
`packages/shared` (`ui.css` → `theme.css`), new `packages/ui`, three Nuxt apps (all SFCs), `AGENTS.md` / OpenSpec config (shared styles path). No API or money-handling changes. Three-app `pnpm` builds plus a mall visual pass.
|
||||
|
||||
## Non-goals
|
||||
|
||||
Dark mode, free hex picker, per-shop customer-facing branding, theming the public mall, Element Plus, mixing this with the catalog-module split.
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared design system in the platform console
|
||||
The platform admin console SHALL use `@vmall/ui` primitives and `@vmall/shared` theme tokens for chrome (buttons, fields, cards, tables, page headers, nav). It MUST NOT depend on Element Plus or the removed `ui.css` stylesheet.
|
||||
|
||||
#### Scenario: console chrome from the kit
|
||||
- **WHEN** an authenticated platform admin opens the dashboard
|
||||
- **THEN** primary actions and page chrome render through the shared kit with the current accent preset
|
||||
|
||||
### Requirement: Accent preset switcher
|
||||
Platform admin SHALL expose the shared accent preset control (`red`, `blue`, `teal`, `violet`) in the authenticated shell header.
|
||||
|
||||
#### Scenario: switcher visible
|
||||
- **WHEN** an authenticated platform admin opens the console
|
||||
- **THEN** they can select an accent preset without leaving the current page
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: B2B2C mall-style PC storefront shell
|
||||
The mall SHALL render a buyer-facing desktop shell modeled on a classic B2B2C PC mall: a 30px utility bar, logo/search/cart header, dark primary navigation with a hover category mega-menu, a 1200px content grid, and a value-proposition footer. The header SHALL remain fully visible while scrolling; no part of the shell SHALL auto-hide based on scroll position. The category mega-menu SHALL appear as a hover dropdown under the navigation "All Categories" entry on every page except the home page, where it is instead pinned in the hero row. The visual language SHALL use `#ca151e` for brand/price/active states, `#f5f5f5` section backgrounds, gray hairline borders, compact controls, and product-card hover lift/shadow. The implementation SHALL use the shared Tailwind theme and `@vmall/ui` primitives plus Nuxt-native semantic components, SHALL NOT depend on Element Plus, and SHALL NOT expose a theme-accent switcher.
|
||||
|
||||
#### Scenario: shopper opens any mall page
|
||||
- **WHEN** a shopper navigates to a buyer-facing route
|
||||
- **THEN** the shared desktop shell wraps the route content and its nav/search/cart controls are usable
|
||||
|
||||
#### Scenario: header persists while scrolling
|
||||
- **WHEN** a shopper scrolls any mall page
|
||||
- **THEN** the logo/search/cart header bar remains visible and is never collapsed or hidden by scroll position
|
||||
|
||||
#### Scenario: hover categories on a non-home page
|
||||
- **WHEN** a shopper hovers "All Categories" in the navigation on a page other than `/`
|
||||
- **THEN** the category mega-menu dropdown appears below the navigation and hides again on mouse leave
|
||||
|
||||
#### Scenario: mall has no accent switcher
|
||||
- **WHEN** a shopper inspects the mall shell
|
||||
- **THEN** there is no control to change brand accent and prices/active states remain `#ca151e`
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared design system on the storefront
|
||||
Mall pages SHALL style layout with Tailwind utilities and shared primitives. Mall-only chrome (mega-menu, carousel crop, 1200px shell, product-card hover lift) MAY live in `apps/mall` components but MUST consume shared tokens, not a private global stylesheet.
|
||||
|
||||
#### Scenario: storefront uses shared tokens
|
||||
- **WHEN** a shopper views the home page
|
||||
- **THEN** brand red, the 1200px grid, and the 450px hero height come from the shared theme keys rather than `mall.css`
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared design system in the merchant console
|
||||
The shop-admin console SHALL use `@vmall/ui` primitives and `@vmall/shared` theme tokens for chrome (buttons, fields, cards, tables, page headers, nav). It MUST NOT depend on Element Plus or the removed `ui.css` stylesheet.
|
||||
|
||||
#### Scenario: console chrome from the kit
|
||||
- **WHEN** an authenticated shop user opens shop-admin
|
||||
- **THEN** primary actions and page chrome render through the shared kit with the current accent preset
|
||||
|
||||
### Requirement: Accent preset switcher
|
||||
Shop-admin SHALL expose the shared accent preset control (`red`, `blue`, `teal`, `violet`) in the authenticated shell header.
|
||||
|
||||
#### Scenario: switcher visible
|
||||
- **WHEN** an authenticated shop user opens shop-admin
|
||||
- **THEN** they can select an accent preset without leaving the current page
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared Tailwind design system
|
||||
The three Nuxt apps SHALL load one token stylesheet from `@vmall/shared` (`theme.css`) and SHALL compose repeating chrome from `@vmall/ui` primitives (`VBtn`, `VBadge`, `VField`/`VInput`, `VCard`/`VPanel`, `VTable`, `VPage`). They MUST NOT depend on Element Plus, MUST NOT keep `packages/shared/src/ui.css` or `apps/mall/assets/mall.css`, and MUST NOT use Vue `<style scoped>` for app chrome after the migration. Default brand/primary color SHALL be `#ca151e`.
|
||||
|
||||
#### Scenario: apps share tokens
|
||||
- **WHEN** a developer opens mall, shop-admin, and platform admin with the default accent
|
||||
- **THEN** primary buttons, links, and active nav use `#ca151e` from the shared theme rather than per-app palettes
|
||||
|
||||
#### Scenario: no legacy stylesheets
|
||||
- **WHEN** the migration is complete
|
||||
- **THEN** `@vmall/shared` exports `theme.css` (not `ui.css`) and the three apps have no remaining `<style scoped>` blocks for layout or chrome
|
||||
|
||||
### Requirement: Console accent presets
|
||||
Shop-admin and platform admin SHALL let the signed-in operator pick an accent preset of `red`, `blue`, `teal`, or `violet`. The choice SHALL apply immediately to primary actions, links, and active navigation, SHALL persist in `localStorage` for that app origin, and SHALL restore on reload. An invalid stored value MUST fall back to `red`. The public mall MUST NOT expose this control and MUST remain on the red accent.
|
||||
|
||||
#### Scenario: operator switches accent
|
||||
- **WHEN** a platform admin or shop user selects the blue preset in the console header
|
||||
- **THEN** primary buttons and active nav in that app render with the blue preset without a full page reload
|
||||
|
||||
#### Scenario: accent survives reload
|
||||
- **WHEN** the operator reloads shop-admin or platform admin after choosing teal
|
||||
- **THEN** the console still uses the teal preset
|
||||
|
||||
#### Scenario: mall stays red
|
||||
- **WHEN** a shopper uses the mall after an operator has changed a console accent
|
||||
- **THEN** the mall still uses `#ca151e` and shows no accent switcher
|
||||
@@ -0,0 +1,38 @@
|
||||
## 1. Tooling and tokens
|
||||
|
||||
- [x] 1.1 Add Tailwind v4 and `@tailwindcss/vite` to mall, shop-admin, and platform admin; wire the Vite plugin in each `nuxt.config.ts`
|
||||
- [x] 1.2 Add `packages/shared/src/theme.css` with `@theme` keys (primary `#ca151e`, hover, soft, radius, type, `max-w-mall` 1200px, `h-hero` 450px) and `html[data-accent]` maps for `red|blue|teal|violet`
|
||||
- [x] 1.3 Export `./theme.css` from `@vmall/shared`; load it in all three apps while still loading `ui.css` / `mall.css`
|
||||
|
||||
## 2. `@vmall/ui` kit
|
||||
|
||||
- [x] 2.1 Scaffold `packages/ui` (`@vmall/ui`) in the pnpm workspace with Vue peer deps and exports
|
||||
- [x] 2.2 Implement `VBtn`, `VBadge`, `VField`/`VInput`, `VCard`/`VPanel`, `VTable`, `VPage` on shared tokens
|
||||
- [x] 2.3 Implement `useAccent()` (read/write `localStorage`, set `document.documentElement.dataset.accent`, invalid → `red`) and an accent swatch control
|
||||
- [x] 2.4 Add the kit as a dependency of the three Nuxt apps and transpile it if needed
|
||||
|
||||
## 3. Platform admin
|
||||
|
||||
- [x] 3.1 Convert `apps/admin` shell and pages to kit + Tailwind utilities; add the accent swatch to the authenticated header
|
||||
- [x] 3.2 Remove admin `<style scoped>` blocks
|
||||
- [x] 3.3 `pnpm --filter @vmall/admin build` green; browser-check accent switch + reload persistence
|
||||
|
||||
## 4. Shop-admin
|
||||
|
||||
- [x] 4.1 Convert `apps/shop-admin` shell and pages the same way; add the accent swatch
|
||||
- [x] 4.2 Remove shop-admin `<style scoped>` blocks
|
||||
- [x] 4.3 `pnpm --filter @vmall/shop-admin build` green; browser-check accent switch + reload persistence
|
||||
|
||||
## 5. Mall storefront
|
||||
|
||||
- [x] 5.1 Lock mall to `data-accent="red"` at boot; do not render the swatch
|
||||
- [x] 5.2 Convert mall shell (utility bar, header, dark nav, mega-menu, 1200px grid, footer) to tokens/utilities/kit
|
||||
- [x] 5.3 Convert mall pages (home hero/sidebar/carousel last-but-one; `goods/[id]`, integral, store last)
|
||||
- [x] 5.4 Remove all mall `<style scoped>` and stop loading `mall.css`
|
||||
|
||||
## 6. Cut over and docs
|
||||
|
||||
- [x] 6.1 Delete `packages/shared/src/ui.css` and the `./ui.css` export; point `AGENTS.md` / OpenSpec config at `theme.css` and `@vmall/ui`
|
||||
- [x] 6.2 Confirm no `<style scoped>` remains under `apps/mall`, `apps/shop-admin`, `apps/admin`
|
||||
- [x] 6.3 `pnpm --filter @vmall/mall build` plus admin and shop-admin builds all green
|
||||
- [x] 6.4 Browser pass: mall shell, home hero/sidebar, search grid, product detail, cart/checkout chrome; both consoles on default red and after a preset change
|
||||
Reference in New Issue
Block a user