create-nexst-monorepo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/dist/index.js +1727 -0
  2. package/package.json +37 -0
  3. package/template/.agents/plugins/marketplace.json +21 -0
  4. package/template/.claude/settings.json +6 -0
  5. package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
  6. package/template/.claude/skills/frontend-design/SKILL.md +154 -0
  7. package/template/.claude/skills/theme-factory/SKILL.md +95 -0
  8. package/template/.claude/skills/theme-factory/references/schema.md +104 -0
  9. package/template/.claude/skills/theme-factory/scripts/theme.mjs +261 -0
  10. package/template/.dockerignore +43 -0
  11. package/template/.editorconfig +10 -0
  12. package/template/.github/workflows/ci.yaml +289 -0
  13. package/template/.github/workflows/publish-app.yaml +66 -0
  14. package/template/.github/workflows/publish.yaml +148 -0
  15. package/template/.github/workflows/snyk.yaml +34 -0
  16. package/template/.vscode/settings.json +8 -0
  17. package/template/AGENTS.md +42 -0
  18. package/template/Dockerfile.base +9 -0
  19. package/template/README.md +54 -0
  20. package/template/_gitignore +10 -0
  21. package/template/apps/api/.env.example +32 -0
  22. package/template/apps/api/AGENTS.md +44 -0
  23. package/template/apps/api/Dockerfile +69 -0
  24. package/template/apps/api/_gitignore +8 -0
  25. package/template/apps/api/knip.config.ts +22 -0
  26. package/template/apps/api/kustomize/base/application.properties +16 -0
  27. package/template/apps/api/kustomize/base/deployment.yaml +94 -0
  28. package/template/apps/api/kustomize/base/kustomization.yaml +17 -0
  29. package/template/apps/api/kustomize/base/service.yaml +10 -0
  30. package/template/apps/api/kustomize/overlays/prod/kustomization.yaml +17 -0
  31. package/template/apps/api/kustomize/overlays/staging/application.properties +1 -0
  32. package/template/apps/api/kustomize/overlays/staging/kustomization.yaml +21 -0
  33. package/template/apps/api/kustomize/secret.keys +14 -0
  34. package/template/apps/api/oxfmt.config.ts +8 -0
  35. package/template/apps/api/oxlint.config.ts +107 -0
  36. package/template/apps/api/package.json +79 -0
  37. package/template/apps/api/prisma/migrations/20260927000000_init/migration.sql +113 -0
  38. package/template/apps/api/prisma/migrations/migration_lock.toml +3 -0
  39. package/template/apps/api/prisma/schema.prisma +113 -0
  40. package/template/apps/api/prisma.config.ts +7 -0
  41. package/template/apps/api/scripts/auth.cli.ts +19 -0
  42. package/template/apps/api/scripts/seal-secret.sh +171 -0
  43. package/template/apps/api/sonar-project.properties +9 -0
  44. package/template/apps/api/src/app-info/app-info.dto.ts +10 -0
  45. package/template/apps/api/src/app-info/app-info.module.ts +7 -0
  46. package/template/apps/api/src/app-info/app-info.resolver.spec.ts +14 -0
  47. package/template/apps/api/src/app-info/app-info.resolver.ts +21 -0
  48. package/template/apps/api/src/app.module.spec.ts +93 -0
  49. package/template/apps/api/src/app.module.ts +77 -0
  50. package/template/apps/api/src/auth/auth.guard.spec.ts +90 -0
  51. package/template/apps/api/src/auth/auth.guard.ts +97 -0
  52. package/template/apps/api/src/auth/auth.ts +129 -0
  53. package/template/apps/api/src/auth/decorator/allow-anonymous.decorator.ts +17 -0
  54. package/template/apps/api/src/auth/decorator/is-email-verified.decorator.spec.ts +41 -0
  55. package/template/apps/api/src/auth/decorator/is-email-verified.decorator.ts +20 -0
  56. package/template/apps/api/src/auth/decorator/roles.decorator.ts +27 -0
  57. package/template/apps/api/src/auth/testing/make-session.ts +31 -0
  58. package/template/apps/api/src/auth/types/auth.types.ts +27 -0
  59. package/template/apps/api/src/bootstrap/register-graphql-enums.ts +12 -0
  60. package/template/apps/api/src/bootstrap/security.ts +24 -0
  61. package/template/apps/api/src/bootstrap/swagger.ts +28 -0
  62. package/template/apps/api/src/config/app.config.spec.ts +22 -0
  63. package/template/apps/api/src/config/app.config.ts +122 -0
  64. package/template/apps/api/src/config/graphql-depth-limit.ts +103 -0
  65. package/template/apps/api/src/config/graphql.config.ts +77 -0
  66. package/template/apps/api/src/config/resources/app.yaml +29 -0
  67. package/template/apps/api/src/errors/app-parse-uuid.pipe.ts +27 -0
  68. package/template/apps/api/src/errors/app.exception.ts +47 -0
  69. package/template/apps/api/src/errors/error-code.enum.ts +54 -0
  70. package/template/apps/api/src/errors/http-status-fallback-code.util.ts +27 -0
  71. package/template/apps/api/src/errors/validation-exception.factory.ts +43 -0
  72. package/template/apps/api/src/health/health.controller.spec.ts +82 -0
  73. package/template/apps/api/src/health/health.controller.ts +57 -0
  74. package/template/apps/api/src/health/health.module.ts +12 -0
  75. package/template/apps/api/src/logging/context-logger.ts +18 -0
  76. package/template/apps/api/src/logging/logger.config.ts +23 -0
  77. package/template/apps/api/src/logging/logging.module.ts +22 -0
  78. package/template/apps/api/src/mail/mail.module.ts +12 -0
  79. package/template/apps/api/src/mail/mail.service.spec.ts +47 -0
  80. package/template/apps/api/src/mail/mail.service.ts +123 -0
  81. package/template/apps/api/src/main.ts +41 -0
  82. package/template/apps/api/src/prisma/prisma.module.ts +12 -0
  83. package/template/apps/api/src/prisma/prisma.service.spec.ts +136 -0
  84. package/template/apps/api/src/prisma/prisma.service.ts +138 -0
  85. package/template/apps/api/src/queue/dto/enqueue-example.input.ts +11 -0
  86. package/template/apps/api/src/queue/example-queue.resolver.ts +14 -0
  87. package/template/apps/api/src/queue/example-queue.service.spec.ts +21 -0
  88. package/template/apps/api/src/queue/example-queue.service.ts +32 -0
  89. package/template/apps/api/src/queue/queue.module.ts +27 -0
  90. package/template/apps/api/src/r2/r2.module.ts +12 -0
  91. package/template/apps/api/src/r2/r2.service.spec.ts +117 -0
  92. package/template/apps/api/src/r2/r2.service.ts +131 -0
  93. package/template/apps/api/src/user/dto/user.dto.ts +26 -0
  94. package/template/apps/api/src/user/user.module.ts +10 -0
  95. package/template/apps/api/src/user/user.resolver.spec.ts +18 -0
  96. package/template/apps/api/src/user/user.resolver.ts +24 -0
  97. package/template/apps/api/src/utils/catch-everything-exception.filter.spec.ts +292 -0
  98. package/template/apps/api/src/utils/catch-everything-exception.filter.ts +70 -0
  99. package/template/apps/api/src/utils/error-handler.util.spec.ts +20 -0
  100. package/template/apps/api/src/utils/error-handler.util.ts +12 -0
  101. package/template/apps/api/src/utils/escape-like-pattern.util.spec.ts +29 -0
  102. package/template/apps/api/src/utils/escape-like-pattern.util.ts +11 -0
  103. package/template/apps/api/src/utils/pagination.input.ts +20 -0
  104. package/template/apps/api/src/utils/prisma-exception.filter.spec.ts +186 -0
  105. package/template/apps/api/src/utils/prisma-exception.filter.ts +45 -0
  106. package/template/apps/api/src/utils/prisma-graphql-exception.filter.spec.ts +216 -0
  107. package/template/apps/api/src/utils/prisma-graphql-exception.filter.ts +43 -0
  108. package/template/apps/api/tests/app.e2e-spec.ts +35 -0
  109. package/template/apps/api/tests/auth.e2e-spec.ts +38 -0
  110. package/template/apps/api/tests/helpers/api.ts +27 -0
  111. package/template/apps/api/tsconfig.json +11 -0
  112. package/template/apps/api/vitest.config.e2e.ts +16 -0
  113. package/template/apps/api/vitest.config.ts +40 -0
  114. package/template/apps/web/.env.example +7 -0
  115. package/template/apps/web/.storybook/decorators.tsx +38 -0
  116. package/template/apps/web/.storybook/main.ts +17 -0
  117. package/template/apps/web/.storybook/preview.ts +38 -0
  118. package/template/apps/web/.storybook/vitest.setup.ts +5 -0
  119. package/template/apps/web/AGENTS.md +29 -0
  120. package/template/apps/web/Dockerfile +49 -0
  121. package/template/apps/web/_gitignore +16 -0
  122. package/template/apps/web/knip.config.ts +15 -0
  123. package/template/apps/web/kustomize/base/application.properties +5 -0
  124. package/template/apps/web/kustomize/base/deployment.yaml +60 -0
  125. package/template/apps/web/kustomize/base/kustomization.yaml +17 -0
  126. package/template/apps/web/kustomize/base/service.yaml +10 -0
  127. package/template/apps/web/kustomize/overlays/prod/kustomization.yaml +12 -0
  128. package/template/apps/web/kustomize/overlays/staging/application.properties +2 -0
  129. package/template/apps/web/kustomize/overlays/staging/kustomization.yaml +18 -0
  130. package/template/apps/web/messages/de.json +124 -0
  131. package/template/apps/web/messages/en.json +124 -0
  132. package/template/apps/web/next-env.d.ts +7 -0
  133. package/template/apps/web/next.config.ts +39 -0
  134. package/template/apps/web/oxfmt.config.ts +9 -0
  135. package/template/apps/web/oxlint.config.ts +36 -0
  136. package/template/apps/web/package.json +80 -0
  137. package/template/apps/web/playwright.config.ts +34 -0
  138. package/template/apps/web/postcss.config.mjs +7 -0
  139. package/template/apps/web/sonar-project.properties +9 -0
  140. package/template/apps/web/src/app/[locale]/(auth)/recovery/page.tsx +22 -0
  141. package/template/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +29 -0
  142. package/template/apps/web/src/app/[locale]/(auth)/sign-in/page.tsx +24 -0
  143. package/template/apps/web/src/app/[locale]/(auth)/sign-up/page.tsx +24 -0
  144. package/template/apps/web/src/app/[locale]/(auth)/verify/page.tsx +24 -0
  145. package/template/apps/web/src/app/[locale]/dashboard/page.tsx +34 -0
  146. package/template/apps/web/src/app/[locale]/layout.tsx +60 -0
  147. package/template/apps/web/src/app/[locale]/page.tsx +23 -0
  148. package/template/apps/web/src/app/api/health/route.ts +3 -0
  149. package/template/apps/web/src/app/global-error.tsx +19 -0
  150. package/template/apps/web/src/app/globals.css +98 -0
  151. package/template/apps/web/src/app/icon.svg +1 -0
  152. package/template/apps/web/src/app/robots.ts +15 -0
  153. package/template/apps/web/src/app/sitemap.ts +24 -0
  154. package/template/apps/web/src/components/alert/alert.stories.tsx +25 -0
  155. package/template/apps/web/src/components/alert/alert.tsx +50 -0
  156. package/template/apps/web/src/components/avatar/avatar.stories.tsx +116 -0
  157. package/template/apps/web/src/components/avatar/avatar.tsx +88 -0
  158. package/template/apps/web/src/components/button/button-variants.ts +28 -0
  159. package/template/apps/web/src/components/button/button.stories.tsx +33 -0
  160. package/template/apps/web/src/components/button/button.tsx +32 -0
  161. package/template/apps/web/src/components/checkbox/checkbox.stories.tsx +312 -0
  162. package/template/apps/web/src/components/checkbox/checkbox.tsx +38 -0
  163. package/template/apps/web/src/components/dialog/danger-panel.stories.tsx +41 -0
  164. package/template/apps/web/src/components/dialog/danger-panel.tsx +32 -0
  165. package/template/apps/web/src/components/dialog/dialog.stories.tsx +239 -0
  166. package/template/apps/web/src/components/dialog/dialog.tsx +136 -0
  167. package/template/apps/web/src/components/dialog-buttons.stories.tsx +55 -0
  168. package/template/apps/web/src/components/dialog-buttons.tsx +48 -0
  169. package/template/apps/web/src/components/drawer/drawer.stories.tsx +251 -0
  170. package/template/apps/web/src/components/drawer/drawer.tsx +120 -0
  171. package/template/apps/web/src/components/dropdown-menu/dropdown-menu.stories.tsx +329 -0
  172. package/template/apps/web/src/components/dropdown-menu/dropdown-menu.tsx +263 -0
  173. package/template/apps/web/src/components/empty-state.stories.tsx +49 -0
  174. package/template/apps/web/src/components/empty-state.tsx +33 -0
  175. package/template/apps/web/src/components/field-group.stories.tsx +47 -0
  176. package/template/apps/web/src/components/field-group.tsx +20 -0
  177. package/template/apps/web/src/components/field-label.stories.tsx +34 -0
  178. package/template/apps/web/src/components/field-label.tsx +28 -0
  179. package/template/apps/web/src/components/icons.tsx +25 -0
  180. package/template/apps/web/src/components/input/input-otp.stories.tsx +86 -0
  181. package/template/apps/web/src/components/input/input-otp.tsx +184 -0
  182. package/template/apps/web/src/components/input/input.stories.tsx +35 -0
  183. package/template/apps/web/src/components/input/input.tsx +180 -0
  184. package/template/apps/web/src/components/popover/popover.stories.tsx +479 -0
  185. package/template/apps/web/src/components/popover/popover.tsx +45 -0
  186. package/template/apps/web/src/components/responsive-dialog/responsive-dialog.stories.tsx +307 -0
  187. package/template/apps/web/src/components/responsive-dialog/responsive-dialog.tsx +96 -0
  188. package/template/apps/web/src/components/searchbar/searchbar.stories.tsx +59 -0
  189. package/template/apps/web/src/components/searchbar/searchbar.tsx +64 -0
  190. package/template/apps/web/src/components/select/select.stories.tsx +293 -0
  191. package/template/apps/web/src/components/select/select.tsx +445 -0
  192. package/template/apps/web/src/components/skeleton/skeleton.stories.tsx +76 -0
  193. package/template/apps/web/src/components/skeleton/skeleton.tsx +13 -0
  194. package/template/apps/web/src/components/spinner/spinner.tsx +18 -0
  195. package/template/apps/web/src/components/switch/switch.stories.tsx +71 -0
  196. package/template/apps/web/src/components/switch/switch.tsx +35 -0
  197. package/template/apps/web/src/components/tabs/tabs.stories.tsx +209 -0
  198. package/template/apps/web/src/components/tabs/tabs.tsx +69 -0
  199. package/template/apps/web/src/components/tabs/url-tabs.stories.tsx +68 -0
  200. package/template/apps/web/src/components/tabs/url-tabs.tsx +42 -0
  201. package/template/apps/web/src/components/textarea/textarea.stories.tsx +144 -0
  202. package/template/apps/web/src/components/textarea/textarea.tsx +115 -0
  203. package/template/apps/web/src/components/toast/toaster.tsx +38 -0
  204. package/template/apps/web/src/components/tooltip/tooltip.stories.tsx +237 -0
  205. package/template/apps/web/src/components/tooltip/tooltip.tsx +49 -0
  206. package/template/apps/web/src/config/csp.ts +59 -0
  207. package/template/apps/web/src/config/routes.ts +14 -0
  208. package/template/apps/web/src/config/site.ts +5 -0
  209. package/template/apps/web/src/env.ts +15 -0
  210. package/template/apps/web/src/features/auth/auth-card.tsx +20 -0
  211. package/template/apps/web/src/features/auth/auth-divider.tsx +14 -0
  212. package/template/apps/web/src/features/auth/google-auth-button.tsx +49 -0
  213. package/template/apps/web/src/features/auth/google-one-tap.tsx +23 -0
  214. package/template/apps/web/src/features/auth/lib/auth-error.test.ts +22 -0
  215. package/template/apps/web/src/features/auth/lib/auth-error.ts +16 -0
  216. package/template/apps/web/src/features/auth/lib/return-to.test.ts +41 -0
  217. package/template/apps/web/src/features/auth/lib/return-to.ts +19 -0
  218. package/template/apps/web/src/features/auth/lib/validator.test.ts +45 -0
  219. package/template/apps/web/src/features/auth/lib/validator.ts +49 -0
  220. package/template/apps/web/src/features/auth/passkey-settings.tsx +106 -0
  221. package/template/apps/web/src/features/auth/recovery-form.tsx +61 -0
  222. package/template/apps/web/src/features/auth/resend-verification-button.tsx +36 -0
  223. package/template/apps/web/src/features/auth/reset-password-form.tsx +62 -0
  224. package/template/apps/web/src/features/auth/sign-in-form.stories.tsx +22 -0
  225. package/template/apps/web/src/features/auth/sign-in-form.tsx +116 -0
  226. package/template/apps/web/src/features/auth/sign-out-button.tsx +26 -0
  227. package/template/apps/web/src/features/auth/sign-up-form.tsx +107 -0
  228. package/template/apps/web/src/features/shared/provider.tsx +16 -0
  229. package/template/apps/web/src/features/shared/site-header.tsx +48 -0
  230. package/template/apps/web/src/features/shared/theme-toggle.tsx +24 -0
  231. package/template/apps/web/src/i18n/navigation.ts +5 -0
  232. package/template/apps/web/src/i18n/request.ts +21 -0
  233. package/template/apps/web/src/i18n/routing.ts +6 -0
  234. package/template/apps/web/src/lib/auth-client.ts +19 -0
  235. package/template/apps/web/src/lib/auth-server.ts +46 -0
  236. package/template/apps/web/src/lib/auth.ts +8 -0
  237. package/template/apps/web/src/lib/codegen.ts +30 -0
  238. package/template/apps/web/src/lib/graphql-client.ts +29 -0
  239. package/template/apps/web/src/lib/graphql-server.ts +28 -0
  240. package/template/apps/web/src/lib/graphql-utils.test.ts +32 -0
  241. package/template/apps/web/src/lib/graphql-utils.ts +76 -0
  242. package/template/apps/web/src/lib/hooks/use-media-query.ts +21 -0
  243. package/template/apps/web/src/lib/metadata.ts +17 -0
  244. package/template/apps/web/src/lib/network-error.test.ts +30 -0
  245. package/template/apps/web/src/lib/network-error.ts +17 -0
  246. package/template/apps/web/src/lib/proxy-utils.ts +42 -0
  247. package/template/apps/web/src/lib/query-client.ts +29 -0
  248. package/template/apps/web/src/lib/utils.ts +5 -0
  249. package/template/apps/web/src/proxy.ts +57 -0
  250. package/template/apps/web/test-results/.last-run.json +4 -0
  251. package/template/apps/web/tests/auth.spec.ts +17 -0
  252. package/template/apps/web/tests/csp-theme-script.spec.ts +37 -0
  253. package/template/apps/web/tests/home.spec.ts +25 -0
  254. package/template/apps/web/tsconfig.build.json +4 -0
  255. package/template/apps/web/tsconfig.json +28 -0
  256. package/template/apps/web/vitest.config.ts +40 -0
  257. package/template/apps/web/vitest.shims.d.ts +1 -0
  258. package/template/apps/worker/.env.example +4 -0
  259. package/template/apps/worker/AGENTS.md +11 -0
  260. package/template/apps/worker/Dockerfile +35 -0
  261. package/template/apps/worker/knip.config.ts +3 -0
  262. package/template/apps/worker/kustomize/base/application.properties +4 -0
  263. package/template/apps/worker/kustomize/base/deployment.yaml +62 -0
  264. package/template/apps/worker/kustomize/base/kustomization.yaml +16 -0
  265. package/template/apps/worker/kustomize/overlays/prod/kustomization.yaml +12 -0
  266. package/template/apps/worker/kustomize/overlays/staging/application.properties +1 -0
  267. package/template/apps/worker/kustomize/overlays/staging/kustomization.yaml +18 -0
  268. package/template/apps/worker/oxfmt.config.ts +8 -0
  269. package/template/apps/worker/oxlint.config.ts +15 -0
  270. package/template/apps/worker/package.json +33 -0
  271. package/template/apps/worker/sonar-project.properties +8 -0
  272. package/template/apps/worker/src/env.ts +13 -0
  273. package/template/apps/worker/src/lib/graceful-shutdown.ts +27 -0
  274. package/template/apps/worker/src/lib/logger.ts +20 -0
  275. package/template/apps/worker/src/main.ts +17 -0
  276. package/template/apps/worker/src/processors/example-processor.ts +15 -0
  277. package/template/apps/worker/src/worker/redis.ts +19 -0
  278. package/template/apps/worker/src/worker/worker.ts +37 -0
  279. package/template/apps/worker/tests/example-processor.test.ts +13 -0
  280. package/template/apps/worker/tests/graceful-shutdown.test.ts +34 -0
  281. package/template/apps/worker/tsconfig.json +4 -0
  282. package/template/apps/worker/vitest.config.ts +13 -0
  283. package/template/bun.lock +3053 -0
  284. package/template/bunfig.toml +2 -0
  285. package/template/compose.yaml +53 -0
  286. package/template/docker-bake.hcl +86 -0
  287. package/template/package.json +47 -0
  288. package/template/packages/lint/knip.ts +40 -0
  289. package/template/packages/lint/oxfmt.ts +8 -0
  290. package/template/packages/lint/oxlint.ts +136 -0
  291. package/template/packages/lint/package.json +13 -0
  292. package/template/packages/queues/oxfmt.config.ts +8 -0
  293. package/template/packages/queues/oxlint.config.ts +7 -0
  294. package/template/packages/queues/package.json +15 -0
  295. package/template/packages/queues/src/example-job.ts +11 -0
  296. package/template/packages/queues/src/index.ts +3 -0
  297. package/template/packages/queues/src/queue-names.ts +2 -0
  298. package/template/packages/queues/src/redis-connection.ts +27 -0
  299. package/template/packages/queues/tsconfig.json +4 -0
  300. package/template/packages/tsconfig/base.json +23 -0
  301. package/template/packages/tsconfig/node.json +11 -0
  302. package/template/packages/tsconfig/package.json +4 -0
  303. package/template/turbo.json +50 -0
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "create-nexst-monorepo",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a Next.js + NestJS monorepo with bun, turbo, oxlint, vitest, Prisma, Docker, kustomize and CI",
5
+ "keywords": ["create", "monorepo", "nextjs", "nestjs", "bun", "template"],
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "create-nexst-monorepo": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "template"
14
+ ],
15
+ "engines": {
16
+ "node": ">=22.4"
17
+ },
18
+ "scripts": {
19
+ "build": "bun build src/index.ts --target=node --outfile dist/index.js",
20
+ "prepack": "bun run build && bun scripts/pack-template.ts",
21
+ "postpack": "rm -rf template",
22
+ "lint": "oxlint",
23
+ "lint:ts": "tsc --noEmit",
24
+ "fmt": "oxfmt",
25
+ "fmt:check": "oxfmt --check",
26
+ "test": "vitest run",
27
+ "test:matrix": "bun scripts/verify-matrix.ts"
28
+ },
29
+ "devDependencies": {
30
+ "@clack/prompts": "^1.8.1",
31
+ "@types/node": "^26.6.3",
32
+ "oxfmt": "^0.70.0",
33
+ "oxlint": "^1.85.0",
34
+ "typescript": "^7.0.2",
35
+ "vitest": "^5.0.2"
36
+ }
37
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "nexst-plugins",
3
+ "interface": {
4
+ "displayName": "Nexst Plugins"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "superpowers",
9
+ "source": {
10
+ "source": "url",
11
+ "url": "https://github.com/obra/superpowers.git",
12
+ "ref": "v6.2.0"
13
+ },
14
+ "policy": {
15
+ "installation": "INSTALLED_BY_DEFAULT",
16
+ "authentication": "ON_INSTALL"
17
+ },
18
+ "category": "Developer Tools"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "enabledPlugins": {
3
+ "context7@claude-plugins-official": true,
4
+ "frontend-design@claude-plugins-official": true
5
+ }
6
+ }
@@ -0,0 +1,177 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: frontend-design
3
+ description: Use when building, restyling, or reviewing any user-facing UI — a page, component, layout, landing page, artifact, or prototype — and whenever a decision about colour, typography, spacing, motion, or interface copy has to be made.
4
+ license: Adapted from Anthropic's frontend-design skill; complete terms in LICENSE.txt
5
+ ---
6
+
7
+ # Frontend Design
8
+
9
+ Approach this as the design lead at a small studio: every decision about
10
+ palette, typography, layout, and copy is deliberate and defensible, and nothing
11
+ ships because it was the default.
12
+
13
+ **Two situations, opposite disciplines. Decide which one you are in first.**
14
+
15
+ ## Which mode
16
+
17
+ **Does this render inside the product?** (anything in `apps/web`, or any UI a
18
+ user of this app will see)
19
+
20
+ | | **Product mode** — yes | **Studio mode** — no |
21
+ |---|---|---|
22
+ | Palette, type, radius | Fixed. Use the tokens. | Yours to design. |
23
+ | Where distinctiveness comes from | Layout, information design, copy | Everything |
24
+ | Success looks like | Indistinguishable from the rest of the app | Could not be mistaken for anyone else's |
25
+
26
+ Studio mode covers standalone artifacts, marketing one-offs, prototypes,
27
+ internal tools, and other projects. When in doubt — if a real user of the app
28
+ could reach it — it is product mode.
29
+
30
+ ---
31
+
32
+ ## Product mode
33
+
34
+ **Read the semantic tokens in `apps/web/src/app/globals.css` before writing
35
+ markup.** They are the brand: what each colour means in light and dark. If the
36
+ project adds a brand guideline, keep it in `references/brand.md` and read it
37
+ first.
38
+
39
+ The short version, in the order it goes wrong:
40
+
41
+ 1. **Semantic tokens only.** No raw hex, no ramp step in a component, no
42
+ `neutral-*`. Every colour exists in both light and dark.
43
+ 2. **Reuse the components in `src/components/*`.** They carry variants, focus
44
+ rings, ARIA wiring, and loading states you would otherwise reimplement worse.
45
+ 3. **One primary CTA per page.** Everything else neutral.
46
+ 4. **`text-sm` is the default.** Small type and dense spacing are the house style.
47
+ 5. **Every string through `next-intl`, in every locale under `apps/web/messages`.**
48
+ 6. **Check it in both modes and at `sm:` before calling it done.**
49
+
50
+ Creative work in product mode goes into *structure*: what the user sees first,
51
+ what collapses, what a row has to say for itself, what the empty state offers.
52
+ Not into new colours. A new hex value here is not a bold choice, it is a bug.
53
+
54
+ If a design genuinely needs something the system lacks, that is a theme change:
55
+ use **theme-factory** to add it to the theme, validate, emit, and say so — do not smuggle it in as a one-off utility class.
56
+
57
+ ---
58
+
59
+ ## Studio mode
60
+
61
+ Here the brief is paying for a point of view. Make opinionated choices specific
62
+ to *this* subject and take one real aesthetic risk you can justify.
63
+
64
+ **Ground it in the subject.** If the brief doesn't pin down what the product is,
65
+ pin it yourself: name the subject, its audience, and the page's single job, and
66
+ state your choice. The subject's own world — its materials, instruments,
67
+ artifacts, vernacular — is where distinctive choices come from. Use memory about
68
+ this human's preferences and past work as a hint.
69
+
70
+ **Plan before building.** Draft a compact token system first:
71
+
72
+ - **Colour** — 4–6 named hex values. Build it with **theme-factory** so it comes out as a validated ramp + semantic layer, not five loose swatches.
73
+ - **Type** — faces for 2+ roles: a characterful display face used with restraint, a complementary body face, a utility face for captions or data if needed. Not the families you reach for on every project.
74
+ - **Layout** — a one-sentence concept plus ASCII wireframes to compare options.
75
+ - **Signature** — the one element this page will be remembered by.
76
+
77
+ **Then critique the plan against the brief before writing code.** Work through a
78
+ similar prompt in your head: if you would have arrived at the same plan for a
79
+ different subject, it is a default, not a choice. Revise that part and say what
80
+ you changed and why.
81
+
82
+ **Calibration — AI-generated design currently clusters around three looks:**
83
+ (1) warm cream background near `#F4F1EA` + high-contrast serif display +
84
+ terracotta accent; (2) near-black background + one acid-green or vermilion
85
+ accent; (3) broadsheet layout with hairline rules, zero radius, dense columns.
86
+ All are legitimate for some briefs, but they appear regardless of subject.
87
+ Where the brief pins a direction, follow it exactly — the brief's own words
88
+ always win, including when it asks for one of these. Where it leaves an axis
89
+ free, don't spend that freedom on a default.
90
+
91
+ **The hero is a thesis.** Open with the most characteristic thing in the
92
+ subject's world, in whatever form fits: a headline, an image, an animation, a
93
+ live demo. A big number with a small label, supporting stats, and a gradient
94
+ accent is the template answer — use it only if it is genuinely the best one.
95
+
96
+ **Structure is information.** Numbering, eyebrows, dividers, and labels should
97
+ encode something true about the content. `01 / 02 / 03` is only right when the
98
+ content actually is a sequence.
99
+
100
+ **Match complexity to the vision.** Maximalist directions need elaborate
101
+ execution; minimal directions need precision in spacing, type, and detail.
102
+ Elegance is executing the chosen vision well.
103
+
104
+ **Spend your boldness in one place.** Let the signature element be the one
105
+ memorable thing and keep everything around it quiet. Chanel's rule: before
106
+ leaving the house, look in the mirror and remove one accessory.
107
+
108
+ ---
109
+
110
+ ## Both modes
111
+
112
+ **Copy is design material.** Words appear to make the interface easier to
113
+ understand. Write from the user's side of the screen — name things by what
114
+ people control, never by how the system is built. Active voice; a control says
115
+ what happens when it is used ("Save changes", not "Submit"), and keeps the same
116
+ name through the whole flow, so a "Publish" button produces a "Published"
117
+ toast. Errors explain what went wrong and how to fix it, in the interface's
118
+ voice; they don't apologise and are never vague. An empty screen is an
119
+ invitation to act. Sentence case, plain verbs, no filler. Each element does
120
+ exactly one job.
121
+
122
+ **Motion is deliberate.** One orchestrated moment lands harder than scattered
123
+ effects. Extra animation is one of the strongest tells that a design was
124
+ generated rather than designed.
125
+
126
+ **Quality floor, built in without announcing it:** responsive down to mobile,
127
+ visible keyboard focus, contrast that passes measurement rather than the eye,
128
+ `prefers-reduced-motion` respected, no layout shift.
129
+
130
+ **Critique your own work as you build.** Take screenshots if the environment
131
+ supports it — a picture is worth 1000 tokens. Do the iteration in your thinking
132
+ and show the human ideas only once you have confidence they will land.
133
+
134
+ **Watch CSS specificity.** Type-based selectors (`.section`) and element-based
135
+ ones (`.cta`) cancel each other out constantly, especially on section padding
136
+ and margins.
137
+
138
+ ## Common mistakes
139
+
140
+ | Mistake | Mode | Fix |
141
+ |---|---|---|
142
+ | New hex value for a one-off badge | Product | Use a semantic token, or change the theme properly |
143
+ | Rebuilding an input's frame and error text | Product | Pass the props; the component wires ARIA |
144
+ | Three blue buttons on one screen | Product | One primary CTA, rest neutral |
145
+ | Hardcoded English string | Product | `next-intl` key in every locale |
146
+ | Gradient or frosted-glass panel | Product | Flat surfaces, one accent, hairline borders |
147
+ | Reaching for cream + serif + terracotta | Studio | That is a default, not a choice — revise |
148
+ | Building before the token plan exists | Studio | Plan, critique the plan, then build |
149
+ | Animating everything | Both | One deliberate moment |
150
+
151
+ ## Reference
152
+
153
+ - `apps/web/src/app/globals.css` — the semantic token system (product mode)
154
+ - `../theme-factory/SKILL.md` — creating, extending, and auditing themes
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: theme-factory
3
+ description: Use when a colour palette, dark mode, semantic token layer, or whole design system has to be created, extended, ported, or audited — including adding brand colours, checking contrast, or turning an existing stylesheet into a maintainable token system.
4
+ ---
5
+
6
+ # Theme Factory
7
+
8
+ A theme is **data**, not a vibe. It lives in one JSON file, gets validated by a
9
+ script, and the stylesheet is emitted from it. Anything you cannot express in
10
+ that file is not part of the theme — it is a one-off, and one-offs are how
11
+ design systems rot.
12
+
13
+ ## When to use
14
+
15
+ - Creating a palette or theme from scratch, or a second mode (dark, high-contrast, print)
16
+ - Adding a colour, radius, or ramp to an existing system
17
+ - Auditing a stylesheet: is the contrast real, do the ramps behave, is anything drifting
18
+ - Porting a system between Tailwind / CSS variables / a design tool
19
+
20
+ Not for: picking one accent colour for a throwaway snippet, or restyling a
21
+ single component inside a system that already has tokens (use those tokens).
22
+
23
+ ## The three layers
24
+
25
+ Every theme has exactly these, and they only ever reference downward:
26
+
27
+ | Layer | What it is | Example | Who may use it |
28
+ |---|---|---|---|
29
+ | **Ramps** | Raw brand scales, 50→900 | `brand-blue-500` | The semantic layer only |
30
+ | **Semantic** | Named roles, per mode | `primary`, `muted-foreground`, `border` | Components |
31
+ | **Components** | Utilities and variants | `bg-primary text-primary-foreground` | Pages |
32
+
33
+ **A component that names a ramp step or a raw hex has skipped a layer.** That is
34
+ the single most common failure — it is invisible in light mode and breaks the
35
+ moment a second mode exists. Every colour a component touches must be a semantic
36
+ token, and every semantic token must exist in every mode.
37
+
38
+ ## Pipeline
39
+
40
+ ```bash
41
+ # 1. Write or edit <name>.theme.json (schema: references/schema.md)
42
+ # 2. Validate — contrast, ramp monotonicity, mode parity, categorical separation
43
+ node scripts/theme.mjs validate <name>.theme.json
44
+
45
+ # 3. Read the actual numbers when you changed a colour
46
+ node scripts/theme.mjs contrast <name>.theme.json
47
+
48
+ # 4. Emit the stylesheet
49
+ node scripts/theme.mjs emit <name>.theme.json --out globals-theme.css
50
+ ```
51
+
52
+ The emitted CSS is a build product. **Never hand-edit it** — change the JSON and
53
+ re-emit, otherwise the JSON stops being true and every later audit lies.
54
+
55
+ Validation is not advisory. `validate` exits non-zero on failures; a theme that
56
+ fails is not finished, and "it looks fine on my screen" is not a counter-argument
57
+ to a 3.1:1 measurement.
58
+
59
+ ## Deriving a theme from an existing app
60
+
61
+ When a codebase already has a stylesheet, extract rather than invent:
62
+
63
+ 1. Read the real token declarations (`@theme`, `:root`, `.dark`).
64
+ 2. Transcribe them into the JSON verbatim — same hex values, same references.
65
+ 3. Emit and diff against the original. **The token set must come out identical.**
66
+ A diff means you guessed somewhere; fix the JSON, not the diff.
67
+ 4. Only then propose changes, as edits to the JSON.
68
+
69
+ Then measure what the code actually does, and record it in the JSON's `type`,
70
+ `space`, and `elevation` sections: count utility usage across the source, and
71
+ write down the winners as the conventions. A scale nobody uses is fiction; the
72
+ frequencies are the design system whether or not anyone documented it.
73
+
74
+ ## Choosing colours for a new theme
75
+
76
+ - **Build ramps, not colours.** One accent hex gives you nothing to hover, press, or disable with. Nine steps do.
77
+ - **Ramps must darken monotonically.** The validator checks this; a lightness reversal mid-ramp makes hover states move the wrong direction.
78
+ - **The dark mode is not an inversion.** Saturated brand colours read heavier on dark surfaces — a token that carries text usually needs a lighter step (e.g. `500` → `300`), while surfaces need more separation than pure inversion gives. Set them per mode; only tokens verified in both may be inherited.
79
+ - **Categorical hues encode meaning.** If a colour identifies a *kind* of thing, it means the same thing everywhere and must clear 3:1 against every surface it lands on, in every mode, and stay separable from its neighbours. The validator checks all three.
80
+ - **Pick a radius and mean it.** One `--radius` that the named steps all resolve to beats six steps drifting apart. Reserve larger radii for genuinely large surfaces and `full` for things that are actually round.
81
+
82
+ ## Common mistakes
83
+
84
+ | Mistake | Why it hurts |
85
+ |---|---|
86
+ | Hand-editing the emitted CSS | JSON and CSS diverge silently; every later audit is wrong |
87
+ | A token defined in light but not dark | Inherits a hex tuned for a white surface; usually unreadable |
88
+ | Component uses `brand-blue-500` directly | Cannot be re-themed, ignores dark mode |
89
+ | Adding a colour "just for this one badge" | This is how a 3-colour system becomes a 40-colour system |
90
+ | Trusting the eye over the validator | 4.4:1 and 4.6:1 look identical and only one is legal |
91
+
92
+ ## Reference
93
+
94
+ - `references/schema.md` — the theme JSON shape, field by field
95
+ - `scripts/theme.mjs` — validator and emitter (Node, no dependencies)
@@ -0,0 +1,104 @@
1
+ # Theme JSON schema
2
+
3
+ One file per theme, named `<name>.theme.json`. Every field below is read by
4
+ `scripts/theme.mjs`; anything else you add is documentation and is ignored by
5
+ the tooling.
6
+
7
+ ```jsonc
8
+ {
9
+ "name": "nexst", // required — used in output and messages
10
+ "source": "path/to/globals.css", // where this was extracted from, if it was
11
+ "extractedOn": "2026-08-07",
12
+
13
+ "font": { // roles → faces. One face for every role is
14
+ "sans": { // a legitimate choice; state it explicitly.
15
+ "family": "Lexend",
16
+ "loader": "next/font/google",
17
+ "variable": "--font-lexend",
18
+ "role": "everything — display, body, UI, data",
19
+ "weightsLoaded": ["400", "500", "600", "700"],
20
+ "weightsUsed": ["400", "500", "600", "700"] // divergence = wasted bytes
21
+ }
22
+ },
23
+
24
+ "ramps": { // required — raw scales, hex only
25
+ "brand-blue": { "50": "#eef3ff", "…": "…", "900": "#1c2f79" }
26
+ },
27
+ "rampNotes": { // optional — emitted as a CSS comment
28
+ "brand-blue": "Cool, slightly cyan-leaning vivid blue"
29
+ },
30
+
31
+ "semantic": { // required — the first mode is the base,
32
+ "light": { // every other mode is a `.mode { }` override
33
+ "background": "#fbfcfe",
34
+ "primary": "brand-blue-500", // ramp reference → emitted as var(--color-…)
35
+ "primary-foreground": "#ffffff"
36
+ },
37
+ "dark": { "background": "#0d1017", "…": "…" }
38
+ },
39
+ "notes": { // optional — emitted above that token
40
+ "destructive-solid": "Always paired with white text."
41
+ },
42
+ "verifiedInherits": ["kind-carddeck"], // literals a later mode may inherit
43
+ // because they were checked there
44
+
45
+ "radius": { // required — emitted verbatim as --<key>
46
+ "radius": "0.5rem",
47
+ "radius-sm": "calc(var(--radius) - 4px)",
48
+ "radius-lg": "var(--radius)"
49
+ },
50
+
51
+ "type": { // required — documentation, not emitted
52
+ "scale": [{ "token": "text-sm", "size": "0.875rem", "role": "…", "share": "252 uses" }],
53
+ "weights": [{ "token": "font-medium", "value": 500, "role": "…", "share": "188 uses" }]
54
+ },
55
+ "space": { // required — documentation, not emitted
56
+ "unit": "0.25rem",
57
+ "gaps": [{ "token": "gap-2", "role": "default — items inside one control" }],
58
+ "padding": [{ "token": "p-4", "role": "card padding" }],
59
+ "controlHeights": { "sm": "h-9", "default": "h-11" }
60
+ },
61
+ "elevation": [{ "token": "shadow-sm", "role": "buttons and cards — the default" }],
62
+ "breakpoints": { "strategy": "mobile-first", "usage": { "sm": 183, "md": 62 } }
63
+ }
64
+ ```
65
+
66
+ ## Value resolution
67
+
68
+ A semantic value is either a literal `#rrggbb`, a ramp reference
69
+ (`<ramp-name>-<step>`), or another semantic token in the same mode. References
70
+ resolve recursively and are emitted as `var(--color-…)`, so the CSS keeps the
71
+ indirection the JSON describes. Circular references are a validation failure.
72
+
73
+ ## What `validate` enforces
74
+
75
+ | Check | Level | Rule |
76
+ |---|---|---|
77
+ | Required top-level fields | fail | `name`, `ramps`, `semantic`, `radius`, `type`, `space`, `font` |
78
+ | Ramp values are hex | fail | No references inside a ramp |
79
+ | Ramp monotonicity | warn | Each step must be darker than the last |
80
+ | Mode parity | warn | A later mode omitting a token inherits it; flagged when the inherited value is a literal hex, unless listed in `verifiedInherits` |
81
+ | Text contrast | fail | 4.5:1 for the foreground/background pairs below |
82
+ | Focus ring | fail | 3:1 against `background` |
83
+ | Categorical hues (`kind-*`) | fail | 3:1 against `card` in every mode |
84
+ | Categorical separation | warn | Adjacent hues must differ in lightness or channel |
85
+ | Radius count | warn | More than 4 distinct radii |
86
+
87
+ Checked text pairs: `foreground` on `background` and `card`;
88
+ `muted-foreground` on `background` and `card`; `desc` on `card`;
89
+ `primary-foreground` on `primary`; `secondary-foreground` on `secondary`;
90
+ `accent-foreground` on `accent`; `destructive-foreground` on
91
+ `destructive-solid`; `destructive` on `card`.
92
+
93
+ Pairs whose tokens are absent are skipped, so a theme using different names
94
+ still validates — but it also gets less checking. Prefer these names; if a
95
+ theme genuinely needs others, add the pair to `PAIRS` in `scripts/theme.mjs`
96
+ rather than leaving the contrast unmeasured.
97
+
98
+ ## Adding a mode
99
+
100
+ Add a key under `semantic`. It becomes a `.<key> { }` block inside
101
+ `@layer theme`, in declaration order, so `dark` stays `.dark`. List **every**
102
+ token that needs a different value there; rely on inheritance only for ramp
103
+ references and for literals you have actually measured on that mode's surfaces
104
+ (then record them in `verifiedInherits` so the warning stays meaningful).