Files
ASOwnerYTandClaude Opus 5 3970cf9a00
Docker / build (push) Failing after 1m43s
Add design-sync inputs for claude.ai/design
Import this repo's components into a Claude Design project so the design
agent builds with the real Cloudrite library instead of generic components.

The repo is a Next.js app, not a component library: no dist/, no exports
map, no .d.ts tree, and Tailwind exists only as build directives. So
.design-sync/ carries a small build that manufactures a consumable package
from source (staged to the gitignored .ds-pkg/):

- tsconfig.dts.json  declaration emit, for real <Name>Props contracts
- build-css.mjs      Tailwind v4 -> a static stylesheet via @tailwindcss/postcss
- make-pkg.mjs       stages the package; index.js exports all 292 symbols
                     while index.d.ts exports only the 61 roots, so shadcn's
                     flat compound parts don't each become a preview card.
                     Also generates the per-root parts tables in docs/
- shims/next-link    router-free anchor; the real next/link needs
                     AppRouterContext and drags in the App Router runtime
- fonts/             Space Grotesk + Inter woff2 (SIL OFL). next/font supplies
                     these at runtime in the app, so the bundle has none

previews/ holds 40 authored preview stories, conventions.md is prepended to
the generated README as the design agent's usage guide, and NOTES.md records
the gotchas a future sync would otherwise rediscover.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-05 17:04:10 +12:00

9.7 KiB

design-sync notes — Cloudrite

Repo-specific gotchas for future syncs. Read this before re-running.

What this repo is

Not a component library — a Next.js 16 marketing site (private: true, no dist/, no exports, no .d.ts tree). The DS surface is components/ui/* (54 shadcn/ui primitives, new-york style) plus 7 hand-built Cloudrite page sections.

The staging package (.ds-pkg/)

The converter needs a package with a built entry and a .d.ts tree. Three scripts under .design-sync/ build one from source; run them in this order (this is cfg.buildCmd):

  1. node_modules/.bin/tsc -p .design-sync/tsconfig.dts.json — declaration emit, 69 .d.ts files. Exits 0; all three steps are chained with &&, so a real failure stops the run. ../next-env.d.ts must stay in that config's include. It pulls in Next's JSX augmentation for styled-jsx; without it the <style jsx> blocks in hero.tsx and footer.tsx raise TS2322 and the emit exits non-zero. Those are not repo errors — pnpm build and tsc --noEmit -p tsconfig.json are both clean.
  2. node .design-sync/build-css.mjs — compiles Tailwind v4 → .design-sync/compiled.css.
  3. node .design-sync/make-pkg.mjs — writes .ds-pkg/{package.json,index.js,index.d.ts}, copies the stylesheet + fonts in, and regenerates .design-sync/docs/*.md.

index.js re-exports every symbol from source (292 exports → window.Cloudrite), while index.d.ts re-exports only the 61 roots. That split is deliberate: shadcn exports compound parts flat (CardHeader, not Card.Header), and the converter's subcomponent grouping only recognises TS-namespace compounds — so without the split all 292 exports would each get their own preview card. The parts are instead documented per-root in the generated .design-sync/docs/<Root>.md parts tables, which become the .prompt.md the design agent reads.

Known render warns

  • [DTS_STYLE_SYSTEM] filtering @types/react props — expected. Most shadcn primitives type their props as React.ComponentProps<'div'>, so the CSS-shorthand filter trips on React's own DOM prop bag. The emitted <Name>Props still carry the real API (variant unions, asChild, etc.). Not a defect.
  • inlined npm packages: 1 in the build log is a pnpm artifact, not a real count — the metafile regex matches .pnpm first in pnpm's node_modules/.pnpm/<pkg>@ver/... layout. The bundle really does inline radix, lucide, cva, etc.

Gotchas that cost a debugging cycle

  • cfg.tsconfig must use /* */ comments only. The converter strips // comments with a regex that also mangles a "//" JSON key, leaving unparseable JSON. The failure is silent: tsconfigPathsPlugin returns null, esbuild falls back to autodiscovering the repo's own tsconfig.json, and next/link resolves to the real Next runtime — which bundles the App Router and makes every preview die with ReferenceError: process is not defined.
  • next/link is shimmed to a plain anchor (.design-sync/shims/next-link.tsx) via compilerOptions.paths. Header, Hero and Footer import it; the real one needs AppRouterContext, which doesn't exist outside a Next app.
  • CSS edits need a full package-build.mjs. lib/preview-rebuild.mjs only recompiles _preview/*.js; it does not re-copy the stylesheet, so a CSS fix appears to do nothing until the full build runs.
  • Never set html { background } in the DS stylesheet. The card harness paints body{background:#fff} in a later inline <style>, so a dark html under a white body box paints a black band across the bottom of every card. body alone is correct: designs get the brand surface, cards keep the harness's white chrome.
  • Fonts are self-hosted. next/font/google supplies Space Grotesk + Inter at runtime in the app, so the repo has no font files. Latin + latin-ext woff2 subsets were downloaded from Google Fonts into .design-sync/fonts/ (SIL OFL) and are committed. app/globals.css declares --font-sans: var(--font-sans), …, which is a self-reference that is invalid outside Next — .design-sync/tailwind-entry.css re-declares both families with real stacks.

Preview conventions (apply to every new preview)

  • Wrap every story in a Surface<div className="bg-background text-foreground rounded-lg p-6">. Cloudrite is dark-only; ghost, link, outline and border-border are all invisible against the harness's white card body.
  • Overlay / menu components need an open state. Give them a defaultOpen story and set cfg.overrides.<Name> = {"cardMode":"single","primaryStory":"Open","viewport":"WxH"} — see Select. A closed trigger shows none of the compound parts.
  • Animated sections need transitions zeroed. Hero fades in on a 300 ms timer plus a 1 s transition; the capture harness only waits on fonts and images, so an unmodified render screenshots at ~10 % opacity. See the still style in previews/Hero.tsx. Features and Process use IntersectionObserver instead and settle on their own.
  • Use real Cloudrite copy (Auckland IT services, 021 107 7483, the four service lines), never foo/bar.
  • Recompile the CSS after authoring previews: Tailwind only emits utilities for classes it can see, and @source './previews' covers the preview files.

Excluded from the DS, on purpose

  • components/chatwidget.tsx — Chatwoot script injector. Renders no markup and fetches /api/chatwoot on mount.
  • components/ui/toaster.tsx — legacy Toaster, colliding with ui/sonner.tsx's. Sonner wins; the site uses neither.
  • ThemeProvider — in the bundle (wrap-able) but given no card.

Re-sync risks

  • .ds-pkg/ and .design-sync/compiled.css are gitignored and regenerated. A fresh clone must run all three buildCmd steps before the converter, or the entry and stylesheet won't exist.
  • The scaffolding is inert to the app build, but only because of dot-prefixing. tsconfig.json includes **/*.ts(x) from the repo root with only node_modules excluded; TypeScript's glob matcher skips directories beginning with ., which is the sole reason .design-sync/previews/*.tsx (they import 'cloudrite', which does not resolve in the app) and .ds-pkg/types/** don't enter the app's program. Verified: pnpm build and a cold tsc --noEmit both exit 0. If any of this is ever moved to a non-dotted path, add it to the app tsconfig's exclude first.
  • next build is not a substitute for the staging package, but it does compile the Tailwind stylesheet (~136 KB, hash-named under .next/static/chunks/*.css). That is smaller than .design-sync/compiled.css (~170 KB) because Tailwind only emits classes it can see and the app never references the utilities used by .design-sync/previews/. The app build supplies no library entry and no declarations, which is what .ds-pkg/ exists to provide.
  • The fonts are a point-in-time copy. If Google Fonts revs the woff2, nothing here notices; the committed files keep working. Re-download only deliberately.
  • The parts tables in .design-sync/docs/ are generated by make-pkg.mjs from the declaration emit — never hand-edit them; they are overwritten every run. The prop signature column falls back to for declarations that aren't plain functions.
  • ROOT_OVERRIDE in make-pkg.mjs is a hand-maintained exception list (currently just toast.tsx → Toast, whose first export is the provider). A new shadcn component whose first export isn't the root needs an entry there.
  • Only 40 of 61 components have authored previews; the other 21 ship the floor card and can be authored on any later sync. .design-sync/previews/ and the grades carry forward.
  • Playwright drives the machine's installed Google Chrome via DS_CHROMIUM_PATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" (no 200 MB chromium download). Export it before package-validate.mjs / package-capture.mjs, or the render check is skipped.

Findings for the Cloudrite team (not sync bugs)

  • --accent is set to the brand green, byte-identical to --primary (oklch(0.75 0.18 145)), in both the :root and .dark blocks of app/globals.css. shadcn's default is a low-contrast neutral used for hover and placeholder surfaces, so every bg-accent consumer is now vivid green — most visibly Skeleton (loading blocks render as solid green bars), plus the hover states in dropdown-menu, command, menubar, navigation-menu, calendar, item, toggle, button and dialog. The previews render this faithfully rather than papering over it. If it wasn't intentional, giving --accent its own muted value is a one-line change.
  • styles/globals.css is an unused light-theme duplicate of app/globals.css (v0 scaffolding). Nothing imports it.

Gotchas found after the first NOTES pass

  • package-build.mjs wipes the whole --out dir, _screenshots/ included. Any review sheet captured before a full rebuild is gone; re-run package-capture.mjs for anything still awaiting a grade.
  • Overlay content must not be given className="relative". Radix positions DialogContent with fixed plus translate(-50%,-50%); switching it to relative keeps the translate but drops the centring anchor, so the card clips the title. Size the card via cfg.overrides.<Name>.viewport instead.
  • Radix autofocuses the first focusable in an open overlay, which screenshots as a green text selection. onOpenAutoFocus={(e) => e.preventDefault()} gives a clean still.
  • DS_CHROMIUM_PATH must be exported for resync.mjs too — without it the driver's validate stage fails [RENDER_SKIPPED] and skips capture on prior_failure.