From f64908d94e18b4abdf97419039c5a48ab66f44ba Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 8 Jun 2026 20:58:36 +0200 Subject: [PATCH 1/5] chore(mcp): add playwright server pinned to nix-profile chromium Mirrors the configuration we use across aiolabs/webapp. Sites forked from the boilerplate inherit a browser-driving MCP server for visual inspection, screenshots and devtools-protocol access against any URL without needing per-project setup. Co-Authored-By: Claude Opus 4.7 (1M context) --- .mcp.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.mcp.json b/.mcp.json index cffc7d9..b0777fd 100644 --- a/.mcp.json +++ b/.mcp.json @@ -3,6 +3,18 @@ "shadcn": { "command": "npx", "args": ["shadcn-vue@latest", "mcp"] + }, + "playwright": { + "type": "stdio", + "command": "npx", + "args": [ + "@playwright/mcp@latest", + "--executable-path", + "/etc/profiles/per-user/padreug/bin/chromium", + "--caps", + "devtools" + ], + "env": {} } } } From 063ece498ed720b2f27b2b2de5792b640130859b Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 8 Jun 2026 22:33:03 +0200 Subject: [PATCH 2/5] chore(shadcn): fix tsconfig + components.json for current CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shadcn-vue CLI (verified against https://www.shadcn-vue.com/docs/installation/vite) requires path aliases declared with both baseUrl and paths in tsconfig.json AND tsconfig.app.json. Without baseUrl the CLI's resolvedPaths validator fails with: Validation failed: - resolvedPaths: Required,Required,Required,Required,Required (one Required per declared alias under "aliases" in components.json). Also drop two stale keys from components.json that newer shadcn-vue schema versions reject: - "framework": "vite" → no longer part of the schema - "tailwind.config": "" → empty string fails validation; field is optional for Tailwind 4 (no JS config file) After this commit, "pnpm dlx shadcn-vue@latest add " succeeds out of the box. Co-Authored-By: Claude Opus 4.7 (1M context) --- components.json | 2 -- tsconfig.app.json | 1 + tsconfig.json | 8 +++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/components.json b/components.json index 906c7a1..9307fca 100644 --- a/components.json +++ b/components.json @@ -3,13 +3,11 @@ "style": "default", "typescript": true, "tailwind": { - "config": "", "css": "src/style.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" }, - "framework": "vite", "aliases": { "components": "@/components", "composables": "@/composables", diff --git a/tsconfig.app.json b/tsconfig.app.json index 9328499..6e90efc 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -4,6 +4,7 @@ "exclude": ["src/**/__tests__/*"], "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "baseUrl": ".", "paths": { "@/*": ["./src/*"] } diff --git a/tsconfig.json b/tsconfig.json index 1aaa791..d3bc091 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,10 @@ { "files": [], - "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.app.json" }] + "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.app.json" }], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } } From 2eaef7b677d69f503d2169bb8ef5950cd6727598 Mon Sep 17 00:00:00 2001 From: Padreug Date: Mon, 8 Jun 2026 23:01:39 +0200 Subject: [PATCH 3/5] chore(shadcn): silence TS 6 baseUrl deprecation in tsconfigs The previous commit added baseUrl so the shadcn-vue CLI's resolvedPaths validator passes. TypeScript 6 emits a deprecation error for baseUrl (slated for removal in TS 7); vue-tsc treats it as a hard build failure. Per the upstream guidance ('Specify compilerOption "ignoreDeprecations": "6.0" to silence this error'), suppress the deprecation in both tsconfig.json and tsconfig.app.json. Revisit when shadcn-vue's CLI migrates off baseUrl. Co-Authored-By: Claude Opus 4.7 (1M context) --- tsconfig.app.json | 3 ++- tsconfig.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.app.json b/tsconfig.app.json index 6e90efc..6a5ce4a 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -7,6 +7,7 @@ "baseUrl": ".", "paths": { "@/*": ["./src/*"] - } + }, + "ignoreDeprecations": "6.0" } } diff --git a/tsconfig.json b/tsconfig.json index d3bc091..5055806 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "baseUrl": ".", "paths": { "@/*": ["./src/*"] - } + }, + "ignoreDeprecations": "6.0" } } From 30c4794520823673bc91f1542fa3ffb83a4a53cf Mon Sep 17 00:00:00 2001 From: Padreug Date: Tue, 9 Jun 2026 12:44:37 +0200 Subject: [PATCH 4/5] chore(gitignore): ignore .playwright-mcp/ and inspect/ scratch dirs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both directories are generated by tooling that any aiolabs site forked off the boilerplate is likely to run: - .playwright-mcp/ — written by the Playwright MCP server (snapshots, console logs) every time it drives a browser. Lives in main now via .mcp.json so this gitignore entry belongs here too. - inspect/ — convention for a scratch folder holding screenshots, style dumps and other visual-QA artifacts produced during dev. Not source of truth for anything. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index ad5f2f7..73571b4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,9 @@ dist-ssr pnpm-debug.log* .claude/ + +# Tooling scratch dirs (Playwright MCP snapshots/console output; +# inspect/ is a working folder for screenshots and style dumps during +# visual QA). +.playwright-mcp/ +inspect/ From fc439eda0884a55912ad2f685b32bc112b742947 Mon Sep 17 00:00:00 2001 From: Padreug Date: Wed, 10 Jun 2026 00:44:47 +0200 Subject: [PATCH 5/5] docs(CLAUDE.md): boilerplate-wide guidance for Claude sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Captures hard-won lessons from forking the first site (chateau du faune) off this boilerplate so future sessions don't relitigate them: - Default to shadcn-vue primitives over hand-rolled UI. Lists the installed family (Button, Card, Badge, NavigationMenu, Sheet, Alert) and the as-child pattern for wrapping RouterLink / anchor. - Prefer semantic theme tokens (bg-background, text-foreground, border-border, …) over colour literals so site retheming is one CSS variable edit instead of a global class sweep. - Document the shadcn-vue CLI / nix-corepack conflict and the registry-JSON workaround. - Two consistent fixes when mirroring from the registry: lucide imports (lucide-vue-next → @lucide/vue) and Tailwind v4 CSS variable wrapping (h-[--name] → h-[var(--name)]), the latter filed upstream as unovue/shadcn-vue#1853. - tsconfig + components.json setup for when the CLI does work (baseUrl + paths in both tsconfigs, ignoreDeprecations 6.0, no framework key, no empty tailwind.config). - Asset / hero / frosted-glass nav patterns we use and reuse. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b0d1580 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,174 @@ +# boilerplate-website / CLAUDE.md + +Guidance for Claude sessions working in this repo (and any aiolabs site +forked off `main`). Stack: Vue 3.5 + Vite 8 + TypeScript 6 + Tailwind 4 ++ shadcn-vue + reka-ui + vue-router + vue-i18n + Pinia. + +## Reach for shadcn-vue primitives before hand-rolling + +**Default:** if a UI pattern has a shadcn-vue primitive, use it. Don't +roll your own. + +Installed (or known to work) primitives in this branch: + +| Primitive | Use for | +|---|---| +| `Button` (gold-pill variants tuned for this brand) | Every CTA. Use `as-child` to wrap a `` or `` so the link is the click target. | +| `Card` + `CardContent` + `CardHeader` + `CardTitle` | Any bordered content block. Pattern: `...` for prose cards; `......` for image-bearing cards. | +| `Badge` (default/secondary/destructive/outline) | Status pills (`Open` / `Coming soon` / labels). | +| `NavigationMenu` family | Desktop top-nav with dropdown groups. Opens on hover/focus, not click — that's by design. | +| `Sheet` family | Mobile/side panels. Use with `` wrapping a hamburger button. | +| `Alert` + `AlertTitle` + `AlertDescription` | Callout asides, inline notices. | + +When you need a new primitive, prefer it over a custom solution unless +the trade-off is well-justified in a comment. + +### The shadcn-vue CLI is broken on nix-managed pnpm + +`pnpm dlx shadcn-vue@latest add ` invokes `corepack pnpm add ...` +under the hood, which conflicts with our nix-store pnpm and exits with +a vague "Run 'pnpm approve-builds'…" error. The components themselves +install fine; only the dep-install step fails. + +**Workaround:** fetch the component JSON from the registry directly +and write the files yourself: + +```bash +# Find the URL +curl -s https://www.shadcn-vue.com/r/styles/default/.json +``` + +Each file lives at `files[].path` / `files[].content` inside that +JSON. Write to `src/components/ui//` matching the path. Then +`pnpm add ` manually for anything in the registry's +`dependencies` field (typically `reka-ui` and `@vueuse/core`, both +already installed). + +### Two fixes to apply when copying from the registry + +1. **lucide imports:** the registry ships `import { X } from + "lucide-vue-next"` but our project uses `@lucide/vue`. Swap the + path before saving the file. +2. **Tailwind v4 CSS-variable syntax:** the registry uses v3 arbitrary + values like `h-[--reka-navigation-menu-viewport-height]`. Under + Tailwind v4 the inner string is treated as a literal — the value + resolves to nothing and the element collapses. Wrap with `var()`: + `h-[var(--reka-navigation-menu-viewport-height)]`. Reported upstream + as [unovue/shadcn-vue#1853](https://github.com/unovue/shadcn-vue/issues/1853). + +## Semantic theme-aware Tailwind, never colour literals + +The shadcn-vue palette is exposed as CSS variables in `src/style.css` +and the `@theme inline` block maps each variable to a Tailwind utility. +Use the **semantic tokens** so the same component looks right when a +site forks off main and swaps the palette to its own theme. + +Use these, not the underlying colour: + +| Use | Don't | +|---|---| +| `bg-background` | `bg-zinc-950` | +| `bg-card` | `bg-stone-900` | +| `bg-secondary` | `bg-emerald-900` | +| `bg-muted` | `bg-zinc-800` | +| `bg-primary` | `bg-amber-500` | +| `bg-accent` | `bg-yellow-600` | +| `text-foreground` | `text-stone-50` | +| `text-muted-foreground` | `text-zinc-400` | +| `text-primary` | `text-amber-500` | +| `text-accent-foreground` | `text-zinc-950` | +| `border-border` | `border-zinc-800` | +| `ring-ring` | `ring-amber-500` | + +A site retheme is then a single edit to `src/style.css`: change the +HSL values behind `--background`, `--primary`, `--accent` etc. and +every component re-skins. + +Translucent variants (`bg-background/65`, `bg-card/40`, etc.) compose +the same way — keep using the semantic token, just append the opacity +shorthand. + +**Exception** — fully opaque overlays where the colour is the visual +intent, not the token (e.g. `bg-black/30` for a dialog overlay, plain +`bg-zinc-950/80` for a near-black callout panel). For everything else, +semantic. + +## Tailwind 4 / vite / tsconfig setup that the shadcn CLI needs + +If `pnpm dlx shadcn-vue@latest add ` later starts working, it +runs a `resolvedPaths` validator that fails unless these files match: + +`tsconfig.json` — must declare `baseUrl` + `paths`: + +```json +{ + "files": [], + "references": [...], + "compilerOptions": { + "baseUrl": ".", + "paths": { "@/*": ["./src/*"] }, + "ignoreDeprecations": "6.0" + } +} +``` + +`tsconfig.app.json` — same `baseUrl` + `paths` + `ignoreDeprecations` +inside its `compilerOptions`. + +`ignoreDeprecations` is required because TypeScript 6 emits a +deprecation error for `baseUrl` (slated for removal in TS 7) and +`vue-tsc` treats it as a hard failure. + +`components.json` — no `framework` key, no empty `"tailwind.config": +""` field. Both have been removed from current schema versions. + +## Asset patterns + +- `src/assets/*` — imported and Vite content-hashes the URL on build. + Use this for everything reused or that benefits from cache-busting + (logos, hero photography, repeating textures). +- `public/*` — served as-is, no hash. Avoid for things that change. +- Prefer modern formats: AVIF for stills with transparency, WebP for + animation, MP4/WebM for longer-duration motion. The browsers we + target (Chrome ≥85, Firefox ≥93, Safari ≥16, Edge ≥121) handle all + three. +- `` with `` + is the right hook for animation fallbacks. + +## Routing and i18n stay vanilla + +Vue Router and vue-i18n don't get the shadcn treatment — they stay as +the standard plugin wiring. Localised strings live in +`src/i18n/locales/{en,fr,…}.json`; don't inline copy. + +## Frosted-glass sticky header recipe (used here, reusable) + +``` +bg-background/65 backdrop-blur-xl backdrop-saturate-150 border-b border-white/10 +``` + +`bg-background/N` keeps it theme-aware. The `border-white/10` is +intentional — it reads as "glass edge" rather than a hard rule and +looks correct across both light and dark themes. + +## Pinned hero photo pattern + +For full-bleed hero photos that stay put as the page scrolls, put a +`position: fixed` `` at the top of the view's template with a +negative z-index, and let the hero section above be transparent: + +```vue + +
+ +
+``` + +The opaque sections below (welcome / pricing / footer / etc.) cover the +fixed photo as the user scrolls past, so it visually exits when the +hero leaves the viewport.