Building apps
Recipes
The tested UI and data patterns the builder drops into an app instead of writing them from scratch.
What recipes are
A recipe is a finished file the builder copies into a project when a screen needs that pattern: a component under src/components/, or a module under src/lib/. Each one is written on the design tokens (bg-bg, bg-surface, text-ink, text-mute, bg-accent, text-accent-fg, rounded-card) and lucide-react, so it matches the rest of the app without changes. Recipes are plain data in src/skills/recipes/skill.ts; the builder reads them through its use_recipe tool and adapts the record types and fields to the app. Several of them (shared, files, file-upload, payments, collections) are the skeleton's own files, so they are already in every app; the recipe is how the builder re-reads them. The kit already covers most UI patterns; a recipe is for a shape the kit does not ship.
The recipes
- empty-state — A list's empty state: icon, one sentence, one action. →
src/components/EmptyState.tsx - form — A form with inline validation, disabled-while-saving, and a success state. Adapt the fields. →
src/components/ItemForm.tsx - modal — A centered dialog (sheet on phones) with backdrop, Escape to close, focus trap-lite. →
src/components/Modal.tsx - confirm — Confirm a destructive action with a small dialog and a hook to use it. →
src/components/Confirm.tsx - toast — Lightweight toasts: useToast() returns a function toast(text, tone?: "ok" | "err"); ToastProvider wraps the app. →
src/components/Toast.tsx - tabs — Accessible tabs with keyboard arrows; content by key. →
src/components/Tabs.tsx - line-chart — A responsive SVG line chart with area fill, axis labels and a hover tooltip. No library. →
src/components/LineChart.tsx - shared — Records other people can see (posts, reviews, bookings, a shared board), enforced by Ripping: useShared
(name) with add, update, remove. Also use_recipe collections and declare each collection's rule there. → src/lib/shared.ts - files — File uploads stored on Ripping: uploadFile(file, { visibility }) and useFiles() (list, upload, remove). Public files get a permanent url to save in records. →
src/lib/files.ts - file-upload — An upload button with drag and drop that stores the file on Ripping and calls onUploaded(file). Needs use_recipe files. →
src/components/FileUpload.tsx - payments — Taking money: useProducts() lists the owner's products (set on Ripping's Settings → Payments), checkout(items) sends the buyer to pay with whichever processor the owner chose, confirmPayment() on the return route says whether it was paid; usePurchases() / owns(id) say what the signed-in person has bought, for unlocking paid content (a Purchases page at /purchases is already in every app). Already in every app as src/lib/pay.ts. →
src/lib/pay.ts - collections — Declares shared collections and their rules (public | members | inbox) for useShared; Ripping reads this file. →
src/lib/collections.ts - store — A typed localStorage collection hook: list, add, update, remove, undo, export/import JSON. Adapt the record type. →
src/lib/store.ts - supabase-store — The same collection API over a Supabase table (needs @supabase/supabase-js and the connected project's url + anon key). →
src/lib/supabaseStore.ts - supabase-auth — Only when the person asks for Supabase auth specifically: magic-link sign-in over their connected Supabase project: a SignIn screen, a useSession hook, and a sign-out button. →
src/lib/auth.tsx - list — A typed list view: search box, sort menu, filter chips, empty state and simple pagination. Adapt the record type and the row. →
src/components/ListView.tsx - settings — A settings screen: profile, preferences (theme, density), data export/import as JSON, and delete everything with a confirm. Wire exportJson/importJson from the store. →
src/components/SettingsScreen.tsx - stat-tiles — A row of KPI tiles: label, value, delta with colour. →
src/components/StatTiles.tsx
The store recipe is the browser-only version of the collection hook. The skeleton ships a different src/lib/store.ts with the same useStore API that also syncs per account; see Saving data.