From a49e62292c29c193636f4c661ee54ae433c6a7e9 Mon Sep 17 00:00:00 2001 From: Matt Calthrop Date: Fri, 3 Apr 2026 23:08:46 +0100 Subject: [PATCH 1/3] =?UTF-8?q?feat(web):=20PLAN=20=C2=A74.7=20full=20shad?= =?UTF-8?q?cn-solid=20adoption=20and=20Tailwind=20theme=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate app shell, home, recipe cards, and recipe detail to registry-style UI components (card, alert, skeleton, separator) plus shared layout, typography, and loading primitives. Replace bespoke Page.css and AppShell.css with Tailwind and @theme-mapped semantic tokens. Document adoption in COMPONENT_LIBRARY and tick §4.7 in PLAN. Add VS Code Tailwind IntelliSense settings and CSS custom data for v4 at-rules. Made-with: Cursor --- .vscode/extensions.json | 1 + .vscode/settings.json | 6 + .vscode/tailwind-css-v4-custom-data.json | 29 +++ PLAN.md | 2 +- apps/web/COMPONENT_LIBRARY.md | 28 +- apps/web/README.md | 2 +- apps/web/src/components/RecipeCard.tsx | 56 ++-- apps/web/src/components/RecipeList.tsx | 5 + .../src/components/layout/PageBackLink.tsx | 18 ++ apps/web/src/components/layout/shell.tsx | 55 ++++ .../recipe-detail/RecipeDetailBody.tsx | 2 + .../recipe-detail/RecipeDetailHeader.tsx | 24 +- .../recipe-detail/RecipeDetailHero.tsx | 10 +- .../recipe-detail/RecipeDetailIngredients.tsx | 16 +- .../recipe-detail/RecipeDetailPanel.tsx | 36 +++ .../recipe-detail/RecipeDetailSteps.tsx | 18 +- apps/web/src/components/ui/alert.tsx | 58 +++++ apps/web/src/components/ui/button.tsx | 13 +- apps/web/src/components/ui/card.tsx | 53 ++++ apps/web/src/components/ui/cover-image.tsx | 21 ++ apps/web/src/components/ui/loading-region.tsx | 20 ++ apps/web/src/components/ui/separator.tsx | 28 ++ apps/web/src/components/ui/skeleton.tsx | 16 ++ apps/web/src/components/ui/typography.tsx | 106 ++++++++ apps/web/src/index.css | 22 ++ apps/web/src/layout/AppShell.css | 82 ------ apps/web/src/layout/AppShell.tsx | 35 ++- apps/web/src/pages/Home.tsx | 40 +-- apps/web/src/pages/Page.css | 241 ------------------ apps/web/src/pages/RecipePage.tsx | 32 +-- 30 files changed, 635 insertions(+), 440 deletions(-) create mode 100644 .vscode/tailwind-css-v4-custom-data.json create mode 100644 apps/web/src/components/RecipeList.tsx create mode 100644 apps/web/src/components/layout/PageBackLink.tsx create mode 100644 apps/web/src/components/layout/shell.tsx create mode 100644 apps/web/src/components/recipe-detail/RecipeDetailPanel.tsx create mode 100644 apps/web/src/components/ui/alert.tsx create mode 100644 apps/web/src/components/ui/card.tsx create mode 100644 apps/web/src/components/ui/cover-image.tsx create mode 100644 apps/web/src/components/ui/loading-region.tsx create mode 100644 apps/web/src/components/ui/separator.tsx create mode 100644 apps/web/src/components/ui/skeleton.tsx create mode 100644 apps/web/src/components/ui/typography.tsx delete mode 100644 apps/web/src/layout/AppShell.css delete mode 100644 apps/web/src/pages/Page.css diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 731f6a0..db2760e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "biomejs.biome", + "bradlc.vscode-tailwindcss", "charliermarsh.ruff", "ms-python.python", "ms-python.vscode-pylance", diff --git a/.vscode/settings.json b/.vscode/settings.json index 04668ab..a420741 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,10 @@ { + "css.customData": [".vscode/tailwind-css-v4-custom-data.json"], + "css.lint.unknownAtRules": "ignore", + "files.associations": { + "**/apps/web/**/*.css": "tailwindcss" + }, + "tailwindCSS.experimental.configFile": "apps/web/src/index.css", "javascript.preferences.quoteStyle": "single", "typescript.preferences.quoteStyle": "single", "[javascript]": { diff --git a/.vscode/tailwind-css-v4-custom-data.json b/.vscode/tailwind-css-v4-custom-data.json new file mode 100644 index 0000000..9653681 --- /dev/null +++ b/.vscode/tailwind-css-v4-custom-data.json @@ -0,0 +1,29 @@ +{ + "version": 1.1, + "atDirectives": [ + { + "name": "@theme", + "description": "Tailwind CSS v4: map design tokens to the framework theme (utilities, variants)." + }, + { + "name": "@plugin", + "description": "Tailwind CSS v4: load a first-party or third-party Tailwind plugin." + }, + { + "name": "@source", + "description": "Tailwind CSS v4: register source paths for class detection." + }, + { + "name": "@utility", + "description": "Tailwind CSS v4: define a custom utility." + }, + { + "name": "@variant", + "description": "Tailwind CSS v4: define a custom variant." + }, + { + "name": "@custom-variant", + "description": "Tailwind CSS v4: define a custom variant." + } + ] +} diff --git a/PLAN.md b/PLAN.md index 9740e6b..47b08a9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -31,7 +31,7 @@ Tasks and subtasks for building the bread-recipes app (SolidJS + Python REST + O - [x] **4.4** Home page: fetch and list bread recipes with overview + thumbnail; navigate to detail on click. - [x] **4.5** Component library: evaluate options for SolidJS (e.g. **shadcn-solid** with Tailwind vs smaller stacks); record the decision; add the chosen tooling and migrate or adopt components on at least one real screen so the pattern is established. - [x] **4.6** Recipe page: full recipe content and larger image; deep-linkable route (e.g. by id). -- [ ] **4.7** **shadcn-solid adoption (full):** migrate remaining UI (app shell, home, recipe cards and detail sections, and any shared layout) to registry components and Tailwind utilities where it replaces bespoke CSS; align tokens with **`COMPONENT_LIBRARY.md`**; no orphaned hand-rolled controls that duplicate registry patterns. Update **`COMPONENT_LIBRARY.md`** when scope is complete. +- [x] **4.7** **shadcn-solid adoption (full):** migrate remaining UI (app shell, home, recipe cards and detail sections, and any shared layout) to registry components and Tailwind utilities where it replaces bespoke CSS; align tokens with **`COMPONENT_LIBRARY.md`**; no orphaned hand-rolled controls that duplicate registry patterns. Update **`COMPONENT_LIBRARY.md`** when scope is complete. - [ ] **4.8** MSW for tests; knip configured; Vitest coverage at 100% with a CI gate. ## 5. Contract testing (Pact) diff --git a/apps/web/COMPONENT_LIBRARY.md b/apps/web/COMPONENT_LIBRARY.md index 636cbb5..a298915 100644 --- a/apps/web/COMPONENT_LIBRARY.md +++ b/apps/web/COMPONENT_LIBRARY.md @@ -1,4 +1,4 @@ -# Component library (PLAN §4.5) +# Component library (PLAN §4.5 / §4.7) ## Decision: [shadcn-solid](https://shadcn-solid.com/) @@ -27,7 +27,27 @@ Implementation details (Kobalte, CVA, etc.) are whatever the **published registr 2. Add components: **`pnpm dlx shadcn-solid@latest add `** (e.g. **`button`**, **`card`**). Use **`--overwrite`** when refreshing an existing file. 3. Prefer **`@/`** imports; use **`./`** for same-folder modules where Biome allows (see project rules). -## Adoption so far +If the registry is unreachable, add components by porting the [shadcn/ui](https://ui.shadcn.com/) (React) registry output to Solid primitives, matching existing files in **`src/components/ui/`** (same **`cn()`**, CVA, and bread theme tokens **`var(--*)`** from **`src/index.css`**). -- **`src/components/ui/button.tsx`** — **shadcn-solid-style** `Button` + **`buttonVariants`** (see [Button](https://shadcn-solid.com/docs/components/button)). For router links, the docs recommend **`buttonVariants` on ``** — used on **recipe detail** for “← All recipes” (`RecipePage`). -- Existing bread **CSS variables** in **`src/index.css`** remain the source of theme tokens; utility classes reference them where the default shadcn HSL tokens are not wired yet. +## Adoption (§4.7 complete) + +| Component | Role | +| --------- | ---- | +| **`button.tsx`** | **`Button`**, **`buttonVariants`** — links use **`buttonVariants` on ``** (e.g. recipe detail “← All recipes”). | +| **`card.tsx`** | **`Card`**, **`CardHeader`**, **`CardTitle`**, **`CardDescription`**, **`CardContent`**, **`CardFooter`** — home recipe **cards**, recipe **hero** image, **ingredients** / **steps** panels. | +| **`alert.tsx`** | **`Alert`**, **`AlertDescription`** — API **error** states on home and recipe pages. | +| **`skeleton.tsx`** | **`Skeleton`** — **loading** placeholders (lists and detail). | +| **`separator.tsx`** | **`Separator`** — between **ingredients** and **steps** on the recipe detail view. | +| **`typography.tsx`** | **`SiteTitle`**, **`Heading2`**, **`Heading3Panel`**, **`RecipeCardTitle`**, **`TextLede`**, **`TextMuted`**, **`RecipeTimes`**, etc. — shared **type scales** and **meta** text so screens do not repeat long class strings. | +| **`cover-image.tsx`** | **`CoverImage`** — framed **thumbnail / hero** images (aspect ratio and **`object-cover`** on the frame). | +| **`loading-region.tsx`** | **`LoadingRegion`** — **`role="status"`**, **`aria-busy`**, and **skeleton** spacing for list/detail loading. | +| **`layout/shell.tsx`** | **`Shell`**, **`ShellHeader`**, **`ShellMain`**, **`ShellFooter`**, **`SiteBrand`**, **`ShellFooterNote`** — **app chrome** structure. | +| **`layout/PageBackLink.tsx`** | **`PageBackLink`** — back navigation row using **`buttonVariants`**. | +| **`recipe-detail/RecipeDetailPanel.tsx`** | **`RecipeDetailPanel`** — shared **card + panel heading** for **ingredients** and **steps**. | +| **`RecipeList.tsx`** | **`RecipeList`** — home **recipe grid** (`