create-rivet 0.0.0-stage → 0.1.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +45 -2
- package/cli.d.ts +2 -0
- package/cli.js +10 -0
- package/deploy.d.ts +32 -0
- package/deploy.js +99 -0
- package/index.d.ts +118 -0
- package/index.js +184 -0
- package/main.d.ts +9 -0
- package/main.js +169 -0
- package/package.json +57 -4
- package/prompt.d.ts +24 -0
- package/prompt.js +127 -0
- package/templates/starter-minimal/.agents/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-minimal/.agents/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-minimal/.agents/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-minimal/.agents/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-minimal/.agents/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-minimal/.claude/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-minimal/.claude/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-minimal/.claude/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-minimal/.claude/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-minimal/.claude/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-minimal/.cursor/rules/rivet-add-route.mdc +37 -0
- package/templates/starter-minimal/.cursor/rules/rivet-add-ui-page.mdc +43 -0
- package/templates/starter-minimal/.cursor/rules/rivet-fix-resumability-error.mdc +27 -0
- package/templates/starter-minimal/.cursor/rules/rivet-form-in-drawer.mdc +63 -0
- package/templates/starter-minimal/.cursor/rules/rivet-port-react-component.mdc +37 -0
- package/templates/starter-minimal/.cursor/rules/rivet-ui-design.mdc +42 -0
- package/templates/starter-minimal/.cursor/rules/rivet.mdc +27 -0
- package/templates/starter-minimal/.github/copilot-instructions.md +21 -0
- package/templates/starter-minimal/AGENTS.md +457 -0
- package/templates/starter-minimal/CLAUDE.md +457 -0
- package/templates/starter-minimal/README.md +26 -0
- package/templates/starter-minimal/_gitignore +3 -0
- package/templates/starter-minimal/package.json +28 -0
- package/templates/starter-minimal/prerender.js +5 -0
- package/templates/starter-minimal/src/document.ts +14 -0
- package/templates/starter-minimal/src/routes/index.tsx +16 -0
- package/templates/starter-minimal/tsconfig.json +21 -0
- package/templates/starter-minimal/vite.config.ts +4 -0
- package/templates/starter-server/.agents/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-server/.agents/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-server/.agents/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-server/.agents/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-server/.agents/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-server/.claude/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-server/.claude/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-server/.claude/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-server/.claude/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-server/.claude/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-server/.cursor/rules/rivet-add-route.mdc +37 -0
- package/templates/starter-server/.cursor/rules/rivet-add-ui-page.mdc +43 -0
- package/templates/starter-server/.cursor/rules/rivet-fix-resumability-error.mdc +27 -0
- package/templates/starter-server/.cursor/rules/rivet-form-in-drawer.mdc +63 -0
- package/templates/starter-server/.cursor/rules/rivet-port-react-component.mdc +37 -0
- package/templates/starter-server/.cursor/rules/rivet-ui-design.mdc +42 -0
- package/templates/starter-server/.cursor/rules/rivet.mdc +27 -0
- package/templates/starter-server/.dockerignore +7 -0
- package/templates/starter-server/.github/copilot-instructions.md +21 -0
- package/templates/starter-server/AGENTS.md +457 -0
- package/templates/starter-server/CLAUDE.md +457 -0
- package/templates/starter-server/Dockerfile +18 -0
- package/templates/starter-server/README.md +30 -0
- package/templates/starter-server/_gitignore +3 -0
- package/templates/starter-server/package.json +31 -0
- package/templates/starter-server/serve.js +6 -0
- package/templates/starter-server/src/document.ts +14 -0
- package/templates/starter-server/src/routes/index.tsx +41 -0
- package/templates/starter-server/src/styles.css +9 -0
- package/templates/starter-server/tsconfig.json +21 -0
- package/templates/starter-server/vite.config.ts +8 -0
- package/templates/starter-static/.agents/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-static/.agents/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-static/.agents/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-static/.agents/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-static/.agents/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-static/.claude/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-static/.claude/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-static/.claude/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-static/.claude/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-static/.claude/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-static/.cursor/rules/rivet-add-route.mdc +37 -0
- package/templates/starter-static/.cursor/rules/rivet-add-ui-page.mdc +43 -0
- package/templates/starter-static/.cursor/rules/rivet-fix-resumability-error.mdc +27 -0
- package/templates/starter-static/.cursor/rules/rivet-form-in-drawer.mdc +63 -0
- package/templates/starter-static/.cursor/rules/rivet-port-react-component.mdc +37 -0
- package/templates/starter-static/.cursor/rules/rivet-ui-design.mdc +42 -0
- package/templates/starter-static/.cursor/rules/rivet.mdc +27 -0
- package/templates/starter-static/.github/copilot-instructions.md +21 -0
- package/templates/starter-static/AGENTS.md +457 -0
- package/templates/starter-static/CLAUDE.md +457 -0
- package/templates/starter-static/README.md +30 -0
- package/templates/starter-static/_gitignore +3 -0
- package/templates/starter-static/package.json +31 -0
- package/templates/starter-static/prerender.js +5 -0
- package/templates/starter-static/serve.js +21 -0
- package/templates/starter-static/src/components/Counter.tsx +15 -0
- package/templates/starter-static/src/document.ts +14 -0
- package/templates/starter-static/src/routes/_layout.tsx +18 -0
- package/templates/starter-static/src/routes/about.tsx +11 -0
- package/templates/starter-static/src/routes/index.tsx +14 -0
- package/templates/starter-static/src/styles.css +9 -0
- package/templates/starter-static/tsconfig.json +21 -0
- package/templates/starter-static/vite.config.ts +9 -0
- package/templates/starter-ui/.agents/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-ui/.agents/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-ui/.agents/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-ui/.agents/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-ui/.agents/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-ui/.claude/skills/rivet-add-route/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-add-route/SKILL.md +36 -0
- package/templates/starter-ui/.claude/skills/rivet-add-ui-page/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-add-ui-page/SKILL.md +42 -0
- package/templates/starter-ui/.claude/skills/rivet-fix-resumability-error/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-fix-resumability-error/SKILL.md +26 -0
- package/templates/starter-ui/.claude/skills/rivet-form-in-drawer/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-form-in-drawer/SKILL.md +62 -0
- package/templates/starter-ui/.claude/skills/rivet-port-react-component/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-port-react-component/SKILL.md +36 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/LICENSE.txt +21 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/SKILL.md +41 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/component-composition.md +55 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/interactions.md +49 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/rivet-implementation.md +66 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/structure-and-layout.md +51 -0
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/visual-foundations.md +64 -0
- package/templates/starter-ui/.cursor/rules/rivet-add-route.mdc +37 -0
- package/templates/starter-ui/.cursor/rules/rivet-add-ui-page.mdc +43 -0
- package/templates/starter-ui/.cursor/rules/rivet-fix-resumability-error.mdc +27 -0
- package/templates/starter-ui/.cursor/rules/rivet-form-in-drawer.mdc +63 -0
- package/templates/starter-ui/.cursor/rules/rivet-port-react-component.mdc +37 -0
- package/templates/starter-ui/.cursor/rules/rivet-ui-design.mdc +42 -0
- package/templates/starter-ui/.cursor/rules/rivet.mdc +27 -0
- package/templates/starter-ui/.github/copilot-instructions.md +21 -0
- package/templates/starter-ui/AGENTS.md +457 -0
- package/templates/starter-ui/CLAUDE.md +457 -0
- package/templates/starter-ui/README.md +32 -0
- package/templates/starter-ui/_gitignore +3 -0
- package/templates/starter-ui/package.json +34 -0
- package/templates/starter-ui/prerender.js +5 -0
- package/templates/starter-ui/serve.js +21 -0
- package/templates/starter-ui/src/components/LikeButton.tsx +12 -0
- package/templates/starter-ui/src/document.ts +14 -0
- package/templates/starter-ui/src/routes/index.tsx +39 -0
- package/templates/starter-ui/src/styles.css +11 -0
- package/templates/starter-ui/tsconfig.json +21 -0
- package/templates/starter-ui/vite.config.ts +9 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rivet-fix-resumability-error
|
|
3
|
+
description: 'Diagnose and fix Rivet resumability errors - RV2xxx compiler diagnostics, RV1016 handler errors, "is not serializable" / "Cannot resume" build or prerender errors, browser globals during server rendering, state that resets after the first click. Use when a Rivet build, check or page fails with such an error.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/fix-resumability-error.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
7
|
+
|
|
8
|
+
# Fix a resumability error
|
|
9
|
+
|
|
10
|
+
A Rivet page is rendered on the server and resumed in the browser: handlers, bindings and client effects run there with the values they capture, which the server serialized into the HTML. Errors come from code that cannot cross that line.
|
|
11
|
+
|
|
12
|
+
1. Get the exact diagnostic: `npm run check` (`file:line:col - error RVnnnn`), the Vite overlay (`[RVnnnn] file:line:col`), or `check_code(source, filename)`. For the fix: `explain_diagnostic("RVnnnn")`; `check_code` also returns the quick-fixed source when one applies.
|
|
13
|
+
2. Common codes:
|
|
14
|
+
- RV2001 browser global (`window`, `document`, `localStorage`) read during server rendering → move it into a handler, `clientEffect(() => …)`, `clientEvent(window, "resize", …)` or `clientOnly(() => import("…"))`.
|
|
15
|
+
- RV2003 captured class instance (`new Intl.NumberFormat`, an SDK client) → capture plain data; create the object inside the handler or `clientEffect`, or build it with `reconstruct(() => new X(…))` from `@rivetjs/runtime/dom` (the browser runs the factory on first read). A data class declared in the component module (plain fields) resumes as is.
|
|
16
|
+
- RV2102 captured instance of a module class that cannot resume as data (`#private` field, a decorator, a field arrow using `super`, a base that is not a data class of the module, `window` / `document` / `WebSocket` read while constructing) → create it inside the handler, build it with `reconstruct(() => new X(…))`, keep only plain fields, or add `[serialize]()` + `static [revive](data)` (from `@rivetjs/runtime/resume-format`; package classes: `registerClass(Cls, { id, serialize, revive })` in a module imported by the server code and the resume entry). At render, `RV2102: X has [serialize] but no static [revive]` → add the `static [revive]`.
|
|
17
|
+
- RV1016 handler expression reads state when it binds (`onClick={a() ? f : g}`) → wrap: `onClick={(e) => (a() ? f(e) : g(e))}`.
|
|
18
|
+
- RV2002 ref callback must assign a local → `ref={(el) => (local = el)}` or `const el = ref()` + `ref={el}`.
|
|
19
|
+
- RV2006 JSX inside a handler / effect / computed → render JSX in the component, switch it with a signal.
|
|
20
|
+
- RV2007 `this` in resumable code → `event.currentTarget`.
|
|
21
|
+
- RV2009 two module references share a browser name (two imports of different modules under one name) → rename one import (`import { fmt as fmtDate } from …`).
|
|
22
|
+
- RV2101 (warning, type-aware) a captured value's type does not serialize → as RV2003.
|
|
23
|
+
3. Build / prerender error `Cannot resume <file>#<Component>$<hash>: function is not serializable` (dev server: ``capture `<name>` function is not serializable``; dev build: `capture #<i> …`): a captured function Rivet did not compile (created at runtime by a library such as `memoize(fn)`, a `.bind(…)` written inside an object literal (keep `const inc = add.bind(null, 2)` in a local constant and store `inc`; `.bind(ctx)` of a function using `this` stays RV2007), a method, or held by an object built outside a component: at module level such as `const table = { format: formatEuro }`, or by your own hook / helper such as `function useFormat() { return { format: formatEuro }; }`). Wrap it in an inline arrow, or build the object inside the component (module-level and imported functions used by name there resume by reference). A library object holding functions (a chart, editor, socket client from a package): build it with `reconstruct(() => createChart(…))` from `@rivetjs/runtime/dom` — no value in the page, the browser runs the factory on first read (module level: one server instance per process, never read request state in it). A class instance at render (`X instance is not serializable`): your class → `reconstruct(() => new X(…))` or `[serialize]()` + `static [revive](data)`; a package class → `registerClass(X, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`. `NODE_ENV=development vite build` names the value path. Rules: `get_doc("docs:/best-practices/serializable-captures/")`.
|
|
24
|
+
4. State resets on the first change after resume: a child destructures its props (`function C({ value })`) and is re-created → read `props.value`. Copies of `props.children` in a local are not tracked → render `{props.children}` directly.
|
|
25
|
+
5. `.ts` module importing `@rivetjs/runtime/dom` (bundles the DOM runtime into SSR) → move that code into a `.tsx` file.
|
|
26
|
+
6. Re-run `npm run check` and `npm run build` (prerender exercises serialization); both must be clean.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denys Severyn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rivet-form-in-drawer
|
|
3
|
+
description: 'Build a validated form inside a drawer or dialog on a Rivet live page, so a submit updates the list behind it in place, keeps the overlay open on errors and works without JavaScript. Use for "add/edit X in a drawer/modal/dialog" tasks in a Rivet project.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/form-in-drawer.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
7
|
+
|
|
8
|
+
# Form in a drawer with a live page
|
|
9
|
+
|
|
10
|
+
Reference implementation: `get_doc("docs:/guides/forms/forms-in-overlays/")` (example `docs:/examples/drawer-form/`). Read it first; the steps below are its shape.
|
|
11
|
+
|
|
12
|
+
1. Define the form once, shared by page and action (any Standard Schema library):
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// src/forms/customer.ts
|
|
16
|
+
import { defineForm } from "@rivetjs/forms";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
export const customerForm = defineForm({ name: "customer", schema: z.object({ name: z.string().min(1), email: z.string().email() }) });
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
2. Make the page live and add the action:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import type { LivePageProps } from "@rivetjs/router";
|
|
25
|
+
import { actionResult } from "@rivetjs/router/client";
|
|
26
|
+
import { action, invalid } from "@rivetjs/forms/server";
|
|
27
|
+
|
|
28
|
+
export const live = true; // data is an accessor, refreshed in place after a submit: signals (open drawer, input, focus) survive
|
|
29
|
+
export const serverLoad = ({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") });
|
|
30
|
+
export const serverActions = {
|
|
31
|
+
customer: action(customerForm, (value) => {
|
|
32
|
+
if (customers.some((c) => c.email === value.email)) return invalid({ email: "Already a customer" });
|
|
33
|
+
customers.push({ id: customers.length + 1, ...value });
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
|
|
37
|
+
/* … */
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. In the component: `const result = actionResult("customer")`, `const open = signal(data().creating || (result?.status ?? 0) >= 400)` (no-JS entry: `<a href="?new" onClick={(e) => { e.preventDefault(); open.set(true); }}>`), `const form = createForm(customerForm, { result })`. Render the list from `data()` with `<For each={data().customers} by={(c) => c.id}>`; never copy `data()` into a signal.
|
|
42
|
+
4. The overlay: `@rivetjs/ui` (`Drawer` / `Modal`, `isOpen={open}`) or cdk `ModalOverlay isDismissable isOpen={open}` + `Modal` + `Dialog` (div overlay layer, two-way `isOpen`). The form inside:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<form id={formId(form)} method="post" action="?/customer" onSubmit={async (event) => (await onSubmit(customerForm, form, event)) && open.set(false)}>
|
|
46
|
+
<label for={fieldId(form, "email")}>Email</label>
|
|
47
|
+
<input
|
|
48
|
+
id={fieldId(form, "email")}
|
|
49
|
+
name="email"
|
|
50
|
+
value={defaultValue(form, "email")}
|
|
51
|
+
aria-invalid={isInvalid(form, "email")}
|
|
52
|
+
aria-describedby={describedBy(form, "email")}
|
|
53
|
+
/>
|
|
54
|
+
<FieldError of={form} name="email" />
|
|
55
|
+
<button aria-disabled={submitting(form) ? "true" : undefined}>Save</button>
|
|
56
|
+
</form>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`onSubmit` resolves true once the server accepted the form and the page data is fresh: close the drawer then. Errors stay in the open drawer. Imports: `createForm, defaultValue, describedBy, fieldId, formId, isInvalid, onSubmit, submitting` from `@rivetjs/forms`, `FieldError` from `@rivetjs/forms/components`.
|
|
60
|
+
|
|
61
|
+
5. A layout `serverLoad` re-runs too (counts in headers update).
|
|
62
|
+
6. Check: `check_code` on the page, `npm run check`, then try it with JavaScript disabled (plain POST, 400 re-render with the drawer open).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denys Severyn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rivet-port-react-component
|
|
3
|
+
description: "Port a React (or Solid / Next.js) component or page to Rivet idioms - signals instead of useState, clientEffect instead of useEffect, For with a key, live props, class, serializable handlers. Use when the user pastes React code or asks to convert / migrate a component to Rivet."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/port-react-component.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
7
|
+
|
|
8
|
+
# Port a React component to Rivet
|
|
9
|
+
|
|
10
|
+
Map each construct (full table: `get_doc("docs:/start/coming-from-other-frameworks/")`):
|
|
11
|
+
|
|
12
|
+
| React | Rivet |
|
|
13
|
+
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
14
|
+
| `const [n, setN] = useState(0)` | `const n = signal(0)`; read `n()`, write `n.set(1)` / `n.update((v) => v + 1)` (`@rivetjs/reactivity`) |
|
|
15
|
+
| `useMemo(() => a * 2, [a])` | `computed(() => a() * 2)` (no dependency list) |
|
|
16
|
+
| `useEffect(() => …, [deps])` | `clientEffect(() => …)` from `@rivetjs/runtime/dom` (browser only, re-runs when read signals change; return a cleanup) |
|
|
17
|
+
| `useRef()` for an element | `const el = ref<HTMLInputElement>()` + `ref={el}`; read `el()` |
|
|
18
|
+
| `useCallback`, `memo` | nothing: components run once |
|
|
19
|
+
| `useContext` | `createContext` / `useContext` (`@rivetjs/reactivity`) + `<Provider token value>` (`@rivetjs/runtime/dom`) |
|
|
20
|
+
| `items.map((i) => <li key={i.id}>…)` | `<For each={items} by={(i) => i.id}>{(i) => <li>{i().name}</li>}</For>` (item / index are accessors); `.map` is fine for fixed data |
|
|
21
|
+
| `cond && <p />` | `cond() && <p />` or `<Show when={cond()}>` |
|
|
22
|
+
| `className`, `htmlFor`, `onChange` on text inputs | `class`, `for`, `onInput` or `model:value={signal}` |
|
|
23
|
+
| `style="…"` strings | `style={{ color: c() }}` or `style:color={c()}` |
|
|
24
|
+
| `function Badge({ count })` | `function Badge(props: { count: number })` + `props.count` (props are live; destructuring reads once) |
|
|
25
|
+
| `React.lazy`, `<Suspense>` | `lazyComponent`, `Async`, `Suspense` (`@rivetjs/runtime/dom`) |
|
|
26
|
+
| Next.js `getServerSideProps`, Remix `loader` / `action` | `export const serverLoad` / `serverActions` in the route file |
|
|
27
|
+
| `useRouter().push`, `useSearchParams` | `navigate()`, `useSearchParams()` from `@rivetjs/router/client` |
|
|
28
|
+
| icon components / inline SVG | `<Icon icon={Check} />` from `@rivetjs/kit/icon` + `import { Check } from "lucide"` |
|
|
29
|
+
|
|
30
|
+
Then fix what resuming requires:
|
|
31
|
+
|
|
32
|
+
1. Move browser reads (`window`, `document`, `localStorage`, `matchMedia`) out of the component body into handlers or `clientEffect` (RV2001).
|
|
33
|
+
2. Handlers: inline arrows or local consts. Values they capture must serialize: create class instances (`Intl.*`, SDK clients) inside the handler or build them with `reconstruct(() => new X(…))` (`import { reconstruct } from "@rivetjs/runtime/dom"`; RV2003); wrap library callbacks: `onPress={() => track("save")}`.
|
|
34
|
+
3. Render props and item renderers stay functions (`{(item) => …}`).
|
|
35
|
+
4. Third-party React UI → `@rivetjs/ui` / `@rivetjs/cdk` equivalents: `list_components`, `get_component(name)`; React Aria-style names carry over (`onPress`, `isOpen={signal}`).
|
|
36
|
+
5. `check_code` the result, fix every RV code (`explain_diagnostic`), then `npm run check`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denys Severyn
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rivet-ui-design
|
|
3
|
+
description: "Use when designing, building, or polishing Rivet interfaces with @rivetjs/ui or @rivetjs/cdk, including colors, spacing, typography, page structure, app layouts, component composition, responsive behavior, and interactions."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
7
|
+
|
|
8
|
+
# Design with Rivet UI and CDK
|
|
9
|
+
|
|
10
|
+
Design a coherent experience around the user's task, then express it with Rivet components. Preserve the app's brand, established patterns, and requested scope. This skill works in consumer projects; MCP is optional.
|
|
11
|
+
|
|
12
|
+
## Start with the task and existing system
|
|
13
|
+
|
|
14
|
+
Read project instructions, the relevant screens, installed package versions, global styles, and theme overrides. Identify the main task, content priorities, primary action, data density, and target devices. Ask only about missing information that materially changes the design; otherwise state a reasonable assumption and proceed.
|
|
15
|
+
|
|
16
|
+
For a new or substantially redesigned screen, establish a compact design contract:
|
|
17
|
+
|
|
18
|
+
- **Structure:** navigation, page regions, reading order, and what changes on mobile.
|
|
19
|
+
- **Visual system:** semantic color roles, type hierarchy, spacing rhythm, density, and surface treatment.
|
|
20
|
+
- **Composition:** chosen UI/CDK components and why they fit the task.
|
|
21
|
+
- **Behavior:** primary action, focus/keyboard behavior, feedback, and loading/empty/error/success states.
|
|
22
|
+
|
|
23
|
+
Use that contract to guide the implementation, not as a mandatory separate deliverable or approval gate. For a small edit, preserve the existing contract and change only what is needed.
|
|
24
|
+
|
|
25
|
+
## Read the relevant design references
|
|
26
|
+
|
|
27
|
+
| Decision | Reference |
|
|
28
|
+
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
|
29
|
+
| Palette, contrast, spacing, typography, radius, visual hierarchy | [Visual foundations](references/visual-foundations.md) |
|
|
30
|
+
| Information architecture, app shell, page regions, responsive and scrolling behavior | [Structure and app layouts](references/structure-and-layout.md) |
|
|
31
|
+
| Choosing and combining components, forms, lists, overlays, reusable patterns | [Component composition](references/component-composition.md) |
|
|
32
|
+
| Action hierarchy, states, feedback, keyboard, focus, motion | [Interactions](references/interactions.md) |
|
|
33
|
+
| Actual imports, APIs, signals, resumability, styles, MCP and offline lookup | [Rivet implementation](references/rivet-implementation.md) |
|
|
34
|
+
|
|
35
|
+
Load the references needed for the task. Before writing code, consult the implementation reference and verify the selected components against the installed version.
|
|
36
|
+
|
|
37
|
+
## Implement and assess the whole experience
|
|
38
|
+
|
|
39
|
+
Use styled `@rivetjs/ui` for its supported patterns. Use headless `@rivetjs/cdk` when the task requires a different visual treatment or a behavior primitive; custom styling still needs every interactive state. Compose semantic HTML for regions that need no component behavior.
|
|
40
|
+
|
|
41
|
+
Evaluate the screen with realistic content: long titles, many rows, empty data, validation errors, pending actions, and narrow widths. Check hierarchy, alignment, contrast, readable text, keyboard access, focus restoration, and overflow. Inspect light/dark modes if supported and reduced motion when adding movement. Run the project's available checks and build; use MCP `check_code` when connected. Report the design decisions and what was actually verified. If browser inspection is unavailable, say which visual checks remain.
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/component-composition.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui/references/component-composition.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
2
|
+
|
|
3
|
+
# Choose and combine components
|
|
4
|
+
|
|
5
|
+
## Select by purpose
|
|
6
|
+
|
|
7
|
+
Use styled `@rivetjs/ui` when the existing look/variants fit. Use headless `@rivetjs/cdk` for a bespoke visual treatment or a lower-level behavior. The CDK supplies behavior and state hooks; the application supplies layout and complete visual states. Semantic HTML is appropriate for ordinary content regions. Neither a component count nor visual complexity makes a screen better.
|
|
8
|
+
|
|
9
|
+
| Need | Consider | Selection criterion |
|
|
10
|
+
| ---------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
11
|
+
| Navigate to a location | Link or semantic anchor | Navigation should retain href, new-tab and no-JS behavior |
|
|
12
|
+
| Perform an action | Button; Toolbar for related controls | Keep one dominant action per action group |
|
|
13
|
+
| Show one object's summary | Card/Surface with title, content, optional footer | A meaningful object/section boundary, not a decorative wrapper for every item |
|
|
14
|
+
| Compare repeated structured data | Table or DataGrid | Use DataGrid when its editing/selection/interaction features are needed; verify feature support |
|
|
15
|
+
| Switch peer views within a context | Tabs | Related views; use route navigation for independent locations |
|
|
16
|
+
| Reveal optional content in place | Disclosure/Accordion | Content remains in reading order; not for required content hidden by default |
|
|
17
|
+
| Choose from a small known set | RadioGroup, Segment, Select | Visibility, number of choices, and space available |
|
|
18
|
+
| Choose from a searchable set | ComboBox/Autocomplete composition | Verify the particular input, collection, filtering and focus wiring |
|
|
19
|
+
| Change a binary preference | Switch or Checkbox | Switch for immediate on/off; checkbox for selecting/agreeing in a form |
|
|
20
|
+
| Edit with surrounding context | Drawer or contextual aside | Keep the current task visible and preserve draft state |
|
|
21
|
+
| Complete a focused interruption | Modal/AlertDialog | Modal for a focused task; AlertDialog for consequential confirmation |
|
|
22
|
+
| Display supplementary content | Popover/Tooltip | Tooltip for short help; popover for richer contextual interaction |
|
|
23
|
+
| Inform about an outcome | Inline message/Alert, optionally Toast | Persistent information belongs inline; transient confirmation may use toast |
|
|
24
|
+
|
|
25
|
+
The table routes decisions, not prop spellings. UI and CDK subpaths differ: e.g. UI `combo-box`/`list-box` versus CDK `combobox`/`listbox`. Use the installed package's exports and examples.
|
|
26
|
+
|
|
27
|
+
## Preserve component anatomy
|
|
28
|
+
|
|
29
|
+
Compound roots establish state/context; triggers, labels, content and items consume it. Keep required relationships and place parts under their documented provider/root. Start from a real Rivet example, then adapt content and layout.
|
|
30
|
+
|
|
31
|
+
- Cards: `Card.Header` groups `Card.Title` and `Card.Description`, `Card.Content` holds content, `Card.Footer` holds related actions. Omit unnecessary parts.
|
|
32
|
+
- Fields: combine an explicit `Label`, the appropriate field/control, optional `Description`, and `FieldError` according to that field's anatomy. Required information should not exist only in placeholder text or tooltips.
|
|
33
|
+
- Selection: keep trigger/value/indicator/popover/collection parts in their supported structure. Give each item a stable identity and searchable text where required.
|
|
34
|
+
- Overlays: preserve backdrop/container/dialog/title/close relationships. Content must remain inside its interaction context; a visually correct panel with missing dialog semantics is incomplete.
|
|
35
|
+
- Custom-styled anchors can use `buttonVariants`/`linkVariants`, but CSS classes do not provide the component's interactive behavior. Use the correct native semantics for the task.
|
|
36
|
+
|
|
37
|
+
Avoid nested interactive targets: an entire clickable card with nested buttons, a button inside an anchor, or a row whose selection and action both fire. Separate the primary target and secondary controls. Event propagation hacks do not fix ambiguous semantics.
|
|
38
|
+
|
|
39
|
+
## Compose common regions
|
|
40
|
+
|
|
41
|
+
**Collection page:** heading + main create action → labeled search/filter controls → result count and collection → pagination. Show bulk actions only when selection exists; `ActionBar` or `Toolbar` may fit. Align filters and table columns to the same content edges. Provide initial-empty and filtered-empty states separately.
|
|
42
|
+
|
|
43
|
+
**Settings page:** heading/description → grouped `Fieldset` sections → controls/help/errors → save feedback/actions. Group related rows within a section with consistent spacing or separators. Do not wrap every preference in a card. Choose either explicit save or immediate updates for a region and make that behavior visible.
|
|
44
|
+
|
|
45
|
+
**Detail/edit page:** summary and status → related content sections → contextual actions. Use a drawer for short contextual edits, a page for long/deep edits, and inline editing for small well-bounded values. Destructive actions belong near the affected object with clear labels and proportionate confirmation.
|
|
46
|
+
|
|
47
|
+
**Dashboard:** lead with metrics that support a decision, then the trend or collection needed to act. `Kpi`, `TrendChip`, and the chart exports may fit. Every chart needs a readable explanation or data alternative; meaningful units, time range and empty states matter more than decorative chart variety.
|
|
48
|
+
|
|
49
|
+
**Command picker:** compose a labeled search field and a supported action collection, often using `Autocomplete` + `Menu` for commands. Confirm documented filtering, virtual focus and activation behavior for the installed version. A command menu and a value-selecting list box are different tasks. A dialog may provide a focused enclosing surface. Add pending, no-match, error and clear-query behavior; do not invent a `Command` export or manual arrow-key loop when suitable primitives exist.
|
|
50
|
+
|
|
51
|
+
## Reuse at the pattern boundary
|
|
52
|
+
|
|
53
|
+
Extract a repeated region when content, behavior and styling form a stable pattern (page header, labeled field row, filter toolbar). Prefer wrappers that expose domain-relevant choices and forward native/component props. Preserve live props with `splitProps`/`mergeProps`; use `Content` for renderable values. Avoid a universal wrapper with dozens of layout flags or a separate wrapper for every instance.
|
|
54
|
+
|
|
55
|
+
When composing CDK, style its actual `rv-*` classes and documented `data-*`/ARIA states. Supply idle, hover, pressed, focus-visible, selected, disabled, invalid and pending treatments as applicable. Do not remove its behavior-critical inline styles, roles or keyboard handling.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui/references/interactions.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
2
|
+
|
|
3
|
+
# Interactions and complete states
|
|
4
|
+
|
|
5
|
+
## Action hierarchy
|
|
6
|
+
|
|
7
|
+
Give the user a clear next action. Within an action group, use one primary treatment, secondary alternatives, and tertiary dismissal. Independent regions may each have a primary action; do not make every toolbar control primary. Use verb/object labels such as "Save changes" or "Archive ticket" rather than ambiguous "OK".
|
|
8
|
+
|
|
9
|
+
Use links for navigation, buttons for actions, toggles for persistent state. Place actions near the content they affect and keep ordering consistent. Put low-frequency actions in a menu only if discoverability remains adequate. Give icon-only buttons an accessible label; a tooltip is supplemental. Use Lucide through `@rivetjs/kit/icon` rather than invented SVG or Unicode icons.
|
|
10
|
+
|
|
11
|
+
## Define states with the behavior
|
|
12
|
+
|
|
13
|
+
| State | User-visible behavior |
|
|
14
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| Idle | Clear affordance and current value/selection |
|
|
16
|
+
| Hover/pressed/focus-visible | Consistent feedback; keyboard focus is distinct from hover |
|
|
17
|
+
| Pending | Describe what is happening, keep useful context, prevent accidental duplicate mutation |
|
|
18
|
+
| Success | Show the resulting state; close/clear only after acceptance; use transient confirmation if needed |
|
|
19
|
+
| Validation error | Explain the affected field and recovery near it; preserve entered values |
|
|
20
|
+
| Request error | Explain the failed operation and offer a meaningful retry; preserve draft/selection |
|
|
21
|
+
| Initial empty | Explain what belongs here and the next action to populate it |
|
|
22
|
+
| Filtered empty | Show that no results match; preserve filters and offer reset/clear |
|
|
23
|
+
| Disabled/unavailable | Communicate why when useful and provide a reachable alternative |
|
|
24
|
+
|
|
25
|
+
For loading, use a skeleton when the eventual structure is known, a spinner for a bounded wait, and progress for measurable work. Avoid replacing the entire workspace when only one region is refreshing. Maintain dimensions to prevent layout shifts and keep result announcements concise. For async search, ensure an older response cannot replace newer results.
|
|
26
|
+
|
|
27
|
+
For forms, decide when validation occurs and what happens after submit. Keep labels stable, required status visible and help/error associations connected. On errors, keep the form or overlay open; focus the first invalid control or a useful summary. An explicit-save form preserves drafts until accepted. Immediate-update preferences need pending/success/error feedback and rollback if rejected.
|
|
28
|
+
|
|
29
|
+
## Choose the right feedback surface
|
|
30
|
+
|
|
31
|
+
Use inline errors for fields and local operations, an Alert for persistent page/section information, and a Toast for short-lived outcomes. Do not put the only error explanation in a disappearing toast. Use an AlertDialog for high-consequence confirmation when undo is unavailable; offer undo for easily reversible operations when it fits. Make a destructive confirmation name the affected item and action.
|
|
32
|
+
|
|
33
|
+
## Keyboard, focus and dismissal
|
|
34
|
+
|
|
35
|
+
Retain the UI/CDK primitive's interaction model. Verify Tab order, arrow-key navigation where expected, Enter/Space activation, Escape dismissal, and visible focus. Do not add global shortcuts that conflict with typing, native browser actions or existing app shortcuts; shortcuts supplement visible controls.
|
|
36
|
+
|
|
37
|
+
For menus, dialogs, drawers and popovers, check initial focus, focus containment where modal, and restoration to the opening control. Define outside-press and Escape behavior, especially with unsaved work. Make sure a sticky header/footer does not cover the focused element. A clickable div with an `onClick` handler does not supply button semantics or keyboard behavior.
|
|
38
|
+
|
|
39
|
+
Accessible names should remain meaningful out of context. Status updates that are not focused may need a live announcement; do not announce every render or keystroke. Custom CDK styling must retain selected/focus/invalid indicators, not just idle visuals.
|
|
40
|
+
|
|
41
|
+
## Motion explains change
|
|
42
|
+
|
|
43
|
+
Use movement to show an opening surface, selection change, or structural transition. Prefer the kit's existing CSS transitions and reduced-motion support. Match easing/duration across related controls. Small feedback usually needs a short transition (roughly 100–200ms); larger panel transitions may need more time (roughly 200–300ms). These are starting design ranges, not overrides for every component.
|
|
44
|
+
|
|
45
|
+
Prefer opacity/transform for custom animation where suitable, avoid animating unrelated layout, and keep actions usable while feedback runs. Respect `prefers-reduced-motion`; use a component's `reduceMotion` option when available. Do not add motion merely to fill space or delay navigation for an animation. For custom element animation (entrances, hover / press states, exits, layout moves) use `@rivetjs/motion` (`<Motion.*>`, `Presence`, `animateLayout`): entrances stay CSS from the server and reduced motion is handled; keep drag / flick physics on the cdk's own motion.
|
|
46
|
+
|
|
47
|
+
## Verify realistic sequences
|
|
48
|
+
|
|
49
|
+
Try open → edit → submit → pending → error → retry → success; select → filter → no matches → clear; resize → collapse → restore; and queue → detail → back on mobile. Include keyboard-only use and touch-sized controls. Verify behavior on an actual SSR/resumed page, not only a client-rendered story: deferred overlay content, preserved drafts and first interaction are part of the experience.
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/rivet-implementation.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui/references/rivet-implementation.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
2
|
+
|
|
3
|
+
# Rivet implementation and documentation lookup
|
|
4
|
+
|
|
5
|
+
## Use the installed version as the authority
|
|
6
|
+
|
|
7
|
+
Read project `AGENTS.md`/`CLAUDE.md`, dependencies, JSX/compiler configuration and styling setup. Rivet resembles React JSX but uses fine-grained signals and compiled/resumable output. Do not install React UI libraries or assume React examples run unchanged. Do not upgrade dependencies or replace a working app setup just to apply this skill.
|
|
8
|
+
|
|
9
|
+
When Rivet MCP is connected, use `get_component(name, package)` with an explicit `ui` or `cdk` package to retrieve imports, signatures, compound parts and examples. `search_docs(query, site)` and `get_doc(path)` provide guides. Useful doc ids include:
|
|
10
|
+
|
|
11
|
+
- `ui:/start/design-principles/`, `ui:/start/theming/`, `ui:/start/styling/`, `ui:/start/composition/`.
|
|
12
|
+
- `ui:/components/app-layout/`, `ui:/components/typography/`, and the chosen component's page.
|
|
13
|
+
- `docs:/learn/resuming/` for SSR/resume constraints.
|
|
14
|
+
|
|
15
|
+
Without MCP, inspect the installed package's `package.json` exports and corresponding `.d.ts`/source files in `node_modules/@rivetjs/{ui,cdk}`. Those determine available names, props, parts and event types. If an export points into `dist` or a conditional types entry, follow that target. Use the project's docs links or a supplied docs origin: `/llms.txt` indexes Markdown pages, and `<page>.md` provides a page. A `ui:/…` id is an MCP identifier, not a browser URL; do not invent a production host or fetch localhost unless a docs server is actually running.
|
|
16
|
+
|
|
17
|
+
In a Rivet source checkout, sources are `libs/ui/src/components/<name>/index.ts`, `libs/ui/package.json`, `libs/cdk/package.json`; guides are `apps/ui-docs/content/start/*.md`, component pages are `apps/ui-docs/content/components/<name>.md` and `apps/cdk-docs/content/components/<name>.md`. Examples live under the corresponding app's `examples/`. These paths are a checkout fallback, not dependencies of this installed skill. If the API remains unavailable, request the relevant docs or state the limitation; do not fabricate exports or behavior.
|
|
18
|
+
|
|
19
|
+
## Styles and tokens
|
|
20
|
+
|
|
21
|
+
For an app already using Tailwind v4, keep this import order in its global CSS:
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
@import "tailwindcss";
|
|
25
|
+
@import "@rivetjs/ui/styles";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The `ui` starter provides the build setup. The UI style import uses the Rivet theme; other looks (the Classic theme included) are `@rivetjs/ui/theme` presets whose variables follow the import. Keep existing custom theme overrides. Tailwind must scan application source containing the classes; if a class is missing, check source detection and CSS import/build configuration. Raw `--surface` is a CSS token; `bg-surface` is a Tailwind utility generated from the kit's theme mapping. With plain CSS use `var(--surface)`.
|
|
29
|
+
|
|
30
|
+
Themes respond to `.light`/`.dark` or `[data-theme="light" | "dark"]`. UI components can render in portals: scope theme changes to a root that includes their portal output, or use the documented overlay/theme scoping mechanism. Test a menu/dialog as well as the page when changing a scoped theme. A themed inner wrapper alone may leave its overlay with different tokens.
|
|
31
|
+
|
|
32
|
+
Use `@rivetjs/ui/theme` for framework-free theme derivation. Verify these exports on the installed version. This example generates a candidate restrained teal theme and checks both modes:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { auditContrast, themeCss, type ThemeParams } from "@rivetjs/ui/theme";
|
|
36
|
+
|
|
37
|
+
const theme: ThemeParams = {
|
|
38
|
+
lightness: 0.5,
|
|
39
|
+
chroma: 0.1,
|
|
40
|
+
hue: 185,
|
|
41
|
+
base: 0.004,
|
|
42
|
+
radius: "medium",
|
|
43
|
+
formRadius: "large",
|
|
44
|
+
};
|
|
45
|
+
const css = themeCss(theme);
|
|
46
|
+
const failures = ["light", "dark"].flatMap((mode) => auditContrast(theme, mode as "light" | "dark").filter((pair) => !pair.pass));
|
|
47
|
+
// Review failures, adjust paired tokens/overrides, and re-audit before applying css.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`themeVariables(theme, mode)` returns CSS variables for a style object. `RIVET_PRESET`, `CLASSIC_PRESET` and `THEME_PRESETS` (also the look presets `BRUTALIST_PRESET`, `GLASS_PRESET`, `MAUVE_PRESET`, which change radii, border widths, shadows and card backdrop too) are available starting points. To switch a subtree's whole look by one attribute, import `@rivetjs/ui/styles/themes/presets.css` and set `data-ui-preset="<id>"`. `auditContrast` evaluates the kit's listed pairs including hover/soft variants; custom text, controls, transparency and scoped themes still require checks on the actual screen.
|
|
51
|
+
|
|
52
|
+
## Rivet conventions that affect composition
|
|
53
|
+
|
|
54
|
+
- Prefer subpaths: `import { Button } from "@rivetjs/ui/button"`, `import { AppLayout } from "@rivetjs/ui/app-layout"`. CDK names can differ from UI names (`@rivetjs/cdk/combobox`, `@rivetjs/cdk/listbox`); verify exports.
|
|
55
|
+
- Prefer `class` and `for`. Component events follow their typed APIs, commonly `onPress`, `onSelectionChange`, `onOpenChange`; native text input uses `onInput` or `model:value`.
|
|
56
|
+
- Create `const open = signal(false)` from `@rivetjs/reactivity`; read `open()`, write `open.set(true)`. Supported two-way props take the signal itself (`isOpen={open}`, `value={value}`, `selectedKeys={keys}`). Reading it passes a one-way value; check which props support two-way state.
|
|
57
|
+
- Preserve live props: read `props.name`, and use `splitProps`/`mergeProps` from `@rivetjs/runtime/props` for forwarding. Destructuring live props can read them once.
|
|
58
|
+
- Dynamic collections use `For` from `@rivetjs/runtime/dom` with a stable `by` key; its item and index arguments are accessors. Component render-prop state values can also be accessors; inspect the example/type before reading them.
|
|
59
|
+
- For renderable props use `Content` from `@rivetjs/runtime/jsx-runtime`. For deferred closed overlays/providers on resumable pages use render-function children `{() => …}` instead of already-created JSX. `AppLayout` named slots also take render functions, not JSX values.
|
|
60
|
+
- Keep handlers as inline arrows or supported local functions with serializable captures. Browser reads (`window`, `document`, storage) belong in handlers or `clientEffect`, not server render. Import browser runtime helpers from `.tsx` modules for resume rewriting.
|
|
61
|
+
- Icons: `import { Icon } from "@rivetjs/kit/icon"` and icon data from `lucide`, then `<Icon icon={Search} />`. Keep decorative icons hidden and icon-only controls named. Do not invent SVG paths or use text glyphs as icons.
|
|
62
|
+
- CDK is headless: documented `rv-*` classes and `data-*` state support custom CSS, while behavior-critical styles remain inline. Use its overlay primitives, not a hand-built/native-dialog replacement with a different focus/dismissal model.
|
|
63
|
+
|
|
64
|
+
## Validation
|
|
65
|
+
|
|
66
|
+
Run `check_code` on changed files when MCP is available. Run the consumer project's existing check/typecheck and build commands (starters use `npm run check` and `npm run build`). A compiler check does not prove visual quality or keyboard behavior; inspect the screen and key sequences in a browser. Use targeted interaction tests when behavior is added or changed. Report checks separately from assumptions, especially when API lookup, browser access or SSR verification is unavailable.
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/structure-and-layout.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui/references/structure-and-layout.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
2
|
+
|
|
3
|
+
# Structure and app layouts
|
|
4
|
+
|
|
5
|
+
## Organize around the task
|
|
6
|
+
|
|
7
|
+
Identify what users repeatedly do, what they need to compare, and what they must know before acting. Order content by that sequence. Separate global navigation, page navigation, local controls, and the main content; a row of tabs should not silently replace route navigation.
|
|
8
|
+
|
|
9
|
+
Before selecting components, name the regions: app navigation, page heading/actions, filters, main content, contextual detail, feedback. Decide which are essential and which are optional. A simple form does not need an app shell; a multi-area workspace usually benefits from stable navigation.
|
|
10
|
+
|
|
11
|
+
| User task | Layout direction | Rivet building blocks |
|
|
12
|
+
| --------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------- |
|
|
13
|
+
| Move between persistent app areas | Sidebar + page heading/content | `AppLayout`, `Sidebar`, optional `Navbar` |
|
|
14
|
+
| A few top-level areas, public site, lightweight app | Top navigation + normal page flow | `Navbar`, semantic `main`/sections |
|
|
15
|
+
| Read/edit one object | Bounded content column; related sections and local actions | `Typography`, `Form`, `Fieldset`, optional `Card`/`Surface` |
|
|
16
|
+
| Scan, compare, and act on many objects | Filters/toolbar + collection + pagination; contextual detail | `SearchField`, `Toolbar`, `Table`/`DataGrid`, `Pagination` |
|
|
17
|
+
| Work through a queue | Master list + selected item + optional context panel | `AppLayout` aside, `Resizable`, collections, conversation primitives |
|
|
18
|
+
| Complete ordered stages | Visible progress + focused current step | `Stepper` when its navigation/validation model fits |
|
|
19
|
+
|
|
20
|
+
These are selection directions, not interchangeable APIs. Verify component exports/parts using [implementation lookup](rivet-implementation.md).
|
|
21
|
+
|
|
22
|
+
## Use the app shell's capabilities deliberately
|
|
23
|
+
|
|
24
|
+
`@rivetjs/ui/app-layout` already composes a sidebar provider, sticky header, optional toolbar, main region, contextual aside and footer. Its `sidebar`, `navbar`, `aside`, `toolbar`, and `footer` slots take render functions `() => …` so their contents run inside the layout's providers. Do not recreate a shell's focus and mobile behavior from styled divs when this component fits.
|
|
25
|
+
|
|
26
|
+
- Choose `sidebarCollapsible="icon"` only if icons/tooltips remain comprehensible; choose `"offcanvas"` when full labels are essential. Preserve useful navigation labels.
|
|
27
|
+
- Use the aside for information about the current selection, not another global navigation tree. `asideMobile="sheet"` makes that information available on narrow screens; the default `"hidden"` requires another accessible path if the information is necessary.
|
|
28
|
+
- `AppLayout.MenuToggle` is the mobile sidebar trigger; `Sidebar.Trigger` supports sidebar toggling. `AppLayout.AsideTrigger` controls the aside. Include the triggers that correspond to the panels you expose.
|
|
29
|
+
- Use `sidebarResizable`/`asideResizable` when users benefit from varying the region widths. Set usable minimum sizes and verify keyboard resizing. Persist sizes with `resizableAutoSaveId` only when useful.
|
|
30
|
+
- `scrollMode="page"` is ordinary document scrolling with sticky chrome. `"content"` fixes the shell to the viewport and scrolls the main region. Select an intentional scroll owner; test how long lists, composers, toolbars and detail panels behave together.
|
|
31
|
+
|
|
32
|
+
`AppLayout` renders the main landmark. Avoid nesting another `main` inside it. Its slots and providers also affect resumability; preserve their render-function form.
|
|
33
|
+
|
|
34
|
+
## Responsive behavior is a task decision
|
|
35
|
+
|
|
36
|
+
Define what stacks, collapses, hides, or becomes a separate view. Do not merely scale down desktop columns.
|
|
37
|
+
|
|
38
|
+
- Keep the main task reachable first. Move secondary details behind a visible trigger; keep essential data available.
|
|
39
|
+
- A master/detail workspace can become list → detail navigation on mobile, with a clear back path and preserved list selection/filter/scroll position.
|
|
40
|
+
- Wide tables may keep horizontal scrolling for comparisons. If switching to cards, preserve the labels, key values, sorting/filtering and row actions; do not silently remove functionality.
|
|
41
|
+
- Preserve source order that makes sense for reading and keyboard navigation. CSS reordering should not create a different focus sequence.
|
|
42
|
+
- Define how page actions wrap, whether a composer/save action stays reachable, and whether a sticky element obscures content, focus, or the mobile keyboard.
|
|
43
|
+
- Use content-driven breakpoints. Test at a narrow width around 320–375 CSS px, a middle width where columns begin to collide, and a wide width. Check zoom, long translated labels and touch interaction.
|
|
44
|
+
|
|
45
|
+
Use `min-w-0`/`min-h-0` on flex/grid children that need to shrink, bounded widths for reading, and overflow only on the region that owns it. Avoid hard-coded screen heights and three nested scrolling containers by default.
|
|
46
|
+
|
|
47
|
+
## Example decision: support workspace
|
|
48
|
+
|
|
49
|
+
For triaging tickets while replying, use a compact ticket queue, a dominant conversation, and an optional customer aside. Global navigation remains stable. A page toolbar holds assignment/priority controls; the reply composer owns the main send action. Use neutral surfaces and the customer's accent for selection/action, with text labels for priority.
|
|
50
|
+
|
|
51
|
+
On narrow screens, show queue and conversation as separate reachable views; customer context can use an aside sheet. Preserve the selected ticket and draft when changing views. Test a long thread, a long customer name, empty queue, failed send, and the keyboard opening over the composer. This is a decision example, not a fixed layout for every app.
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/visual-foundations.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/design-ui/references/visual-foundations.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
2
|
+
|
|
3
|
+
# Visual foundations
|
|
4
|
+
|
|
5
|
+
## Color roles before palette
|
|
6
|
+
|
|
7
|
+
Inspect the app's theme before selecting new colors. Preserve a customer's brand; Rivet's blue/indigo default is a starting theme, not a requirement for every application. Assign a role to each color rather than adding arbitrary shades per component.
|
|
8
|
+
|
|
9
|
+
| Role | Rivet CSS tokens / Tailwind utilities | Decision |
|
|
10
|
+
| -------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
11
|
+
| Page | `--background`, `--foreground`; `bg-background text-foreground` | Quiet canvas and primary reading text |
|
|
12
|
+
| Grouped content | `--surface`, `--surface-foreground`; `bg-surface text-surface-foreground` | Separate a meaningful section from its surroundings |
|
|
13
|
+
| Secondary surfaces | `--surface-secondary`, `--surface-tertiary` | Subtle hierarchy; not a different tint for every region |
|
|
14
|
+
| Floating content | `--overlay`, `--overlay-foreground` | Menus, popovers, dialogs use the overlay pair |
|
|
15
|
+
| Supporting text | `--muted`; `text-muted` | De-emphasize metadata; keep required labels readable |
|
|
16
|
+
| Primary action / selection | `--accent`, `--accent-foreground` | Draw attention to the current action or selection |
|
|
17
|
+
| Status | `--success`, `--warning`, `--danger` and their `-foreground` pairs | Communicate meaning with text/icon as well as color |
|
|
18
|
+
| Separation / focus | `--border`, `--separator`, `--focus` | Delineate groups and keep keyboard focus visible |
|
|
19
|
+
| Fields | `--field-background`, `--field-foreground`, `--field-placeholder`, `--field-border` | Preserve legibility in idle, focus, invalid and disabled states |
|
|
20
|
+
|
|
21
|
+
Use component variants for semantic intent: `Button variant="primary"` for the main action, `secondary` for alternatives, `tertiary`/`ghost` for lower emphasis, `danger`/`danger-soft` for destructive actions. Variant vocabularies differ per component; verify them. Reserve accent for meaningful emphasis; several accent-filled panels compete with the primary action. A white surface hard-coded for light mode will fail in dark mode; use paired semantic tokens.
|
|
22
|
+
|
|
23
|
+
When customizing a theme, update paired foregrounds and both modes. Changes to `--accent` can also require `--focus` and the derived hover/soft colors. Prefer `themeCss`, `themeVariables`, and the contrast helpers from `@rivetjs/ui/theme` for generated themes; see [implementation](rivet-implementation.md). A computed theme is a candidate, not proof of contrast.
|
|
24
|
+
|
|
25
|
+
Check actual foreground/background combinations, including hover, selected, focus, invalid and overlay states. Use 4.5:1 for ordinary text, 3:1 for large text (at least 24 CSS px regular or about 18.7 CSS px bold), and 3:1 for essential control boundaries/state indicators. Inspect muted labels on their real surface and transparent colors on the composited background. Color-only status and invisible focus indicators fail even when body text passes. Theme audits cover their listed token pairs; custom elements still need inspection.
|
|
26
|
+
|
|
27
|
+
## Spacing expresses relationships
|
|
28
|
+
|
|
29
|
+
Rivet's default style kit uses `--spacing: 0.25rem`. Tailwind spacing utilities multiply that base. Reuse the project's existing scale; if none is established, begin with these **design recommendations**, not new library defaults:
|
|
30
|
+
|
|
31
|
+
| Relationship | Starting range at a 16px root | Typical utilities |
|
|
32
|
+
| -------------------------------------------- | ----------------------------- | ---------------------- |
|
|
33
|
+
| Icon and label, tightly related metadata | 4–8px | `gap-1`, `gap-2` |
|
|
34
|
+
| Label/control/help, controls in a toolbar | 8–12px | `gap-2`, `gap-3` |
|
|
35
|
+
| Repeated rows and ordinary component content | 12–16px | `gap-3`, `gap-4` |
|
|
36
|
+
| Section padding and related groups | 16–24px | `p-4`, `p-6`, `gap-6` |
|
|
37
|
+
| Distinct page sections | 24–32px | `gap-6`, `gap-8` |
|
|
38
|
+
| Page gutters | 16px narrow, 24–32px wide | `px-4 sm:px-6 lg:px-8` |
|
|
39
|
+
|
|
40
|
+
Keep space within a group smaller than space between groups. Align headings, fields, rows, and footer actions to shared edges. Pick compact density for repetitive scanning and comfortable density for reading or touch; shrink gaps before shrinking essential text or hit targets. Change density across a whole region, not individual rows.
|
|
41
|
+
|
|
42
|
+
Use grid/flex gaps for repeated layout and padding at container boundaries. Avoid stacking wrapper padding, card padding, and child margins into accidental whitespace. Do not change global `--spacing` to fix one page: it changes the kit's component dimensions as well. Use a local layout adjustment.
|
|
43
|
+
|
|
44
|
+
## Typography establishes hierarchy
|
|
45
|
+
|
|
46
|
+
Use the app's existing font family and weights. When choosing new typography, select a small set of roles rather than assigning a different size to every element. `Typography.Heading` provides semantic `level`, `Typography.Paragraph` supports `size="base" | "sm" | "xs"`, and `Typography.Code` is for code. Verify the installed props before separating heading semantics from visual size.
|
|
47
|
+
|
|
48
|
+
| Role | Practical starting treatment |
|
|
49
|
+
| ---------------------------- | --------------------------------------------- |
|
|
50
|
+
| Page title | 24–32px, semibold, tight heading leading |
|
|
51
|
+
| Section title | 18–20px, medium/semibold |
|
|
52
|
+
| Reading text | 16px, about 1.5–1.75 line height |
|
|
53
|
+
| Dense rows, controls, labels | 14–16px with readable leading |
|
|
54
|
+
| Secondary metadata | 12–14px; retain contrast and zoom readability |
|
|
55
|
+
|
|
56
|
+
These are starting recommendations. The shipped `Typography` h1–h6 styles use Tailwind `text-4xl` through `text-base`; body styles use `text-base leading-7`, `text-sm leading-6`, and `text-xs leading-5`. Inspect project overrides rather than assuming pixel sizes from utility names.
|
|
57
|
+
|
|
58
|
+
Use one clear page heading and a logical outline. Increase size/weight for a heading before decorating its container. Keep reading columns around 60–75 characters when practical; dense tables and workspaces need different widths. Use tabular numerals for comparable metrics and amounts. Allow important labels to wrap; truncate only scan-oriented content with a way to inspect the full value. Avoid tiny uppercase gray labels as the main way of identifying fields.
|
|
59
|
+
|
|
60
|
+
## Surface and density discipline
|
|
61
|
+
|
|
62
|
+
Use a surface/card to establish a distinct object or section. Related settings rows often need one section with separators, not a card per row. Avoid card-inside-card stacks. Use the theme's `--radius` and `--field-radius` and component defaults; adjust them consistently when the brand calls for it. Prefer boundaries, spacing and typography for hierarchy; reserve elevation for floating content and genuinely elevated controls.
|
|
63
|
+
|
|
64
|
+
For polish, review the screen in grayscale or with accent usage mentally removed: the task and action hierarchy should remain understandable.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Add a page or endpoint to a Rivet app with file routes, server data (serverLoad), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/add-route.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
8
|
+
|
|
9
|
+
# Add a route to a Rivet app
|
|
10
|
+
|
|
11
|
+
1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const onGet = () => Response.json(…)`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
|
|
12
|
+
2. Write the page. Server code lives in `server…` exports (stripped from the browser build):
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { notFound, type PageProps, type RequestEvent } from "@rivetjs/router";
|
|
16
|
+
|
|
17
|
+
export const serverLoad = async ({ params }: RequestEvent<"blog/[slug]">) => {
|
|
18
|
+
const post = await findPost(params.slug); // server-only: database, files, secrets
|
|
19
|
+
if (!post) throw notFound();
|
|
20
|
+
return { post };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const head = { title: "Post" };
|
|
24
|
+
|
|
25
|
+
export default function Post({ data }: PageProps<"blog/[slug]">) {
|
|
26
|
+
return <h1 class="text-2xl">{data.post.title}</h1>;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- Page props `data`, `params`, `url` are plain values; in `_layout.tsx` (`LayoutProps`) they are accessors: `data()`, `params()`, `url()`, plus `children`.
|
|
31
|
+
- Mutations: `export const serverActions = { name: async ({ request }) => … }` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
|
|
32
|
+
- Access checks: `serverGuard` (throw / return `redirect()`, `deny()`, `notFound()`).
|
|
33
|
+
- Static mode (`rivetRouter({ mode: "static" })`): `serverLoad` runs at build time; dynamic `[param]` pages list their params in `serverEntries`.
|
|
34
|
+
- Server-only helpers go in `*.server.ts` files (browser code cannot import them: RV3008).
|
|
35
|
+
|
|
36
|
+
3. Link to it: plain `<a href="/blog/hello">` (client navigation), or typed `<Link to="/blog/hello">` from `@rivetjs/router/link`; programmatic `navigate()` from `@rivetjs/router/client`.
|
|
37
|
+
4. Run `npm run check` (`rivet sync` regenerates the typed routes in `.rivet/`, `rivet-check` reports `RV` codes; RV3101 = a link matches no route). Fix every diagnostic (`explain_diagnostic(code)`), then `npm run build`.
|