| name | bryllen-new |
| description | Create a new design project and start designing |
/bryllen-new
Create a new design project inside Bryllen and launch the dev environment.
Steps
-
Parse the command. The format is /bryllen-new <project-name> [description].
- If both name AND description are provided: proceed to step 2
- If only name is provided (no description/prompt): Use AskUserQuestion to ask "What would you like to design?" with example options like "Dashboard", "Landing page", "Mobile app", "Component library". Use their response as the description and proceed.
- If no name provided: Use AskUserQuestion to ask for a project name first, then ask what they want to design.
-
Initialize git — check for a .git directory in the current folder.
- If missing: run
git init
- If already a repo: continue
-
Create .gitignore — check if .gitignore exists.
-
Check if bryllen is installed. Look for package.json in the current directory.
- If no
package.json: run npm init -y && npm install github:madebynoam/bryllen
- If
package.json exists but no bryllen dependency: run npm install github:madebynoam/bryllen
- If already installed: continue
If npm install fails, stop immediately and tell the user what went wrong. Do not proceed to step 5.
-
Scaffold the project. Run:
npx bryllen new
This creates index.html, vite.config.ts, src/App.tsx, src/main.tsx, CLAUDE.md (project rules), .claude/settings.json (frozen guard hook), tsconfigs, and installs peer dependencies. Files that already exist are skipped.
-
Create the project folder structure:
src/projects/<project-name>/
v1/
tokens.css ← OKLCH custom properties (.iter-v1 + :root scope)
components/
index.ts ← barrel export — add every new component here when you create it
pages/ ← empty initially
context/ ← inspiration images (designer pastes via Cmd+V)
manifest.ts
CHANGELOG.md
-
Initial commit: Create a git commit with the scaffolded project:
git add . && git commit -m 'feat: init <project-name> project'
This captures the scaffold. A second commit will follow after the design is generated in the 'What happens next' sequence.
-
Launch the dev server:
npx bryllen design
This starts both Vite and the annotation HTTP server in one command.
-
Confirm: Tell the designer the canvas is ready:
"Project <project-name> is live at http://localhost:5173"
-
Continue immediately with the What happens next sequence below to generate the initial design. After generating, enter watch mode automatically so the designer can annotate without running any commands.
What happens next
After init, the designer describes what they want. The agent follows this exact sequence — order matters:
-
Check for context images — Run npx bryllen context --project <name> --iteration v1 to see if the designer pasted any inspiration images. If present, read them via the Read tool and analyze via Vision. Incorporate their style into the design directions.
-
GENERATE 3-5 DESIGN DIRECTIONS (MANDATORY) — This is the whole point of Bryllen. Before writing any code:
- Brainstorm 3-5 genuinely different design approaches
- "Genuinely different" means different in layout, hierarchy, interaction pattern, or information density — NOT just color or font variations
- Examples of genuinely different:
- Dashboard: card grid vs. data table vs. sidebar+detail vs. timeline view
- Landing page: hero-first vs. feature grid vs. testimonial-led vs. interactive demo
- Component: horizontal pills vs. vertical list vs. icon grid vs. dropdown
- NOT genuinely different: same layout with different colors, same hierarchy with different fonts
- The designer will pick one — only then do you build out variations/states
-
Populate tokens — Write the complete OKLCH token set in v1/tokens.css, scoped under :root, .iter-v1. Derive colors from the OKLCH palette defined in CLAUDE.md. If context images are present, extract colors from them. Every visual value (color, background, border, shadow) must be a CSS custom property. No hex values. Tokens come FIRST — the direction subagents in the next step read this file and may only use values from it.
-
FAN OUT DIRECTION SUBAGENTS (PARALLEL) — build the directions concurrently, one subagent each. Spawn one direction-generator agent (bundled in this plugin) per direction using the Agent tool, all in a single message so they run in parallel. Each agent is blind to its siblings — that is the point: directions stay genuinely different because they don't anchor on each other. Each prompt must include:
- The absolute project path (
src/projects/<name>/) and that tokens live in v1/tokens.css
- The direction's name, slug (e.g.
DirA), and brief — the layout/hierarchy/interaction bet from step 1 that makes it different
- Context image paths from step 0, if any
- The conflict rule (also enforced by the agent definition): new component files only, prefixed with the slug; one page
v1/pages/<Slug>.tsx; never touch tokens.css, manifest.ts, components/index.ts, or CHANGELOG.md
Each subagent returns its slug, page path, and component list.
Fallback — if the direction-generator agent type is unavailable, build the directions yourself sequentially, following the same file rules.
-
Create the utility pages yourself (main agent) — while or after the subagents run:
- Tokens page — renders color swatches using
TokenSwatch from bryllen/runtime, typography scale, and spacing grid.
- Components page — shows all building blocks individually (write it after the subagents return, so it covers their components).
- DO NOT create an "AllDirectionsPage" component. Each direction is its own separate canvas frame — the canvas itself IS the "all directions" view.
-
Wire it up (main agent, after ALL subagents return) — export every new component from v1/components/index.ts, then update the manifest: import each direction page and add it to the components map along with the utility pages. Frames are auto-registered — the runtime detects new components and creates frame records automatically on reload. No POST /frames needed.
import './v1/tokens.css'
import { DirA } from './v1/pages/DirA'
import { DirB } from './v1/pages/DirB'
import { TokensPage } from './v1/pages/tokens'
import { ComponentsPage } from './v1/pages/components'
const manifest: ProjectManifest = {
id: '...', project: '<name>',
components: { DirA, DirB, TokensPage, ComponentsPage },
}
That's it. The canvas will auto-discover and display all components after Vite reloads.
-
Log to CHANGELOG.md — record the design directions and why each is different
-
Arm the annotation stream — use the Monitor tool with command: npx bryllen watch --stream, persistent: true (see /bryllen-design step 6; fall back to looping npx bryllen watch if Monitor is unavailable). This is critical — the designer should be able to click and annotate immediately without running any commands.
The full token system, design language, component hierarchy, and guard protocol are defined in CLAUDE.md — those rules apply to every file the agent creates.
Component hierarchy
The agent MUST follow the three-layer hierarchy:
Tokens (v1/tokens.css) → CSS custom properties, all visual values
↓
Components (v1/components/) → building blocks, use ONLY tokens
↓
Pages (v1/pages/) → compositions, import ONLY from ../components/
If a page needs a button, create Button.tsx in components/ FIRST, then import it in the page. Never inline styled HTML in page files.
Components must be interactive
This is the whole point of Bryllen — the code IS the design. Every component must work:
- Text inputs are typeable
- Buttons have hover and active states
- Menus open on click and dismiss on outside click
- Tabs and navigation switch content via React state
No static mockups. If it looks like it should do something, it does.
Interactive navigation
If a component has internal navigation (tabs, sidebar nav, segmented sections), handle it with React state inside one component. Do not split navigable sections into separate frames.
The matrix model (variations × states as separate frames) is for isolated component states. For a dashboard with sidebar nav, build one interactive component that actually swaps content on click — don't create separate frames for each nav section. That produces static snapshots where nothing is clickable, which defeats the purpose.
Rule: If a page has internal navigation, the navigation must work. Use useState to track the active section and render the correct content. One frame, one component, full interactivity.
Manifest format (DB mode)
The manifest only maps component keys to React components. Frames are auto-registered — the runtime detects new components and creates DB records automatically on reload. No manual POST needed.
import type { ProjectManifest } from 'bryllen/runtime'
import './v1/tokens.css'
import { TokensPage } from './v1/pages/tokens'
import { ComponentsPage } from './v1/pages/components'
import { DirA } from './v1/pages/DirA'
import { DirB } from './v1/pages/DirB'
const manifest: ProjectManifest = {
id: '<uuid>',
project: '<project-name>',
components: {
TokensPage,
ComponentsPage,
DirA,
DirB,
},
}
export default manifest
That's it. Add a component to manifest.components → Vite reloads → runtime auto-creates the DB frame record → frame appears on canvas in a grid layout.
For fine-tuned control (custom title, specific width, sort order), you can optionally use POST /frames:
curl -X POST http://localhost:4748/frames -H 'Content-Type: application/json' \
-d '{"project":"<name>","id":"dir-a","title":"Direction A","componentKey":"DirA","width":1440,"height":900}'
Spring module
If the designer wants motion, create v1/spring.ts with golden ratio spring physics:
import { useRef, useEffect, useCallback } from 'react'
export const PHI = (1 + Math.sqrt(5)) / 2
export const SPRING = {
snappy: { tension: 233, friction: 19 },
gentle: { tension: 144, friction: 15 },
soft: { tension: 89, friction: 12 },
}
Components import spring as import { SPRING, useSpring } from '../spring'.
Layout rules
- Columns = states, flowing left to right
- Rows = variations, flowing top to bottom
- Frame IDs:
<component>-<variation>-<state>
- Frame titles:
Component / Variation / State
- The grid config in the manifest controls column width, row height, and gap
- The layout engine computes x/y positions — never set them manually