A web platform for building, managing, and publishing design systems end-to-end.
Design System Builder lets product teams compose their own design system from a shared component catalog, customize tokens (colors, typography, spacing, etc.), preview the result in real time, and then generate, publish, and document the resulting npm package β all from a single UI.
It is the tooling layer behind several Salute design systems built on top of @salutejs/plasma.
- Compose a design system from a shared catalog of components and variations
- Tweak tokens (colors, typography, spacing, shape, etc.) with live preview
- Generate a ready-to-use npm package from your configuration
- Publish that package to a private npm registry in one click
- Generate documentation (Docusaurus site) and deploy it to S3
- Inspect and query the underlying registry through an admin panel (tables, schema, OpenAPI, NL queries)
The platform is a small set of independent services around a central registry. All services are deployed and orchestrated through Docker Compose.
ββββββββββββββββββββββ
β Admin β React SPA β registry inspector,
β apps/admin :3004 β schema browser, OpenAPI, NL queries
βββββββββββ¬βββββββββββ
β REST (OpenAPI-typed)
βΌ
ββββββββββββββββββββββ REST ββββββββββββββββββββββ ββββββββββββββββββββ
β Client β βββββββΆ β DS Registry βββββΆβ PostgreSQL β
β apps/client :3002 β β ds-registry :3008β β (postgres- β
β (DS builder UI) β β Express + Drizzle β β registry :5433)β
βββββββ¬ββββββββ¬βββββββ βββββββββββ¬βββββββββββ ββββββββββββββββββββ
β β β²
β β β fetch DS data
β β β
β β βββββββββββ΄βββββββββββ ββββββββββββββββββββ
β βββββββRESTββββββΆβ DS Generator βββββΆβ Publisher β
β β ds-generator :3005β β publisher :3007 β
β β Fastify + CLI β β npm publish β
β ββββββββββββββββββββββ ββββββββββββββββββββ
β
β ββββββββββββββββββββββ ββββββββββββββββββββ
ββββββββββRESTβββββββββββΆβ Docs Generator βββββΆβ AWS S3 β
β docs-generator:3006β β (docs hosting) β
β NestJS+Docusaurus β β β
βββββββββββ¬βββββββββββ ββββββββββββββββββββ
β fetch DS data
βΌ
(DS Registry)
| Service | Path | Port | Stack | Purpose |
|---|---|---|---|---|
| DS Registry | services/ds-registry | 3008 | Express, Drizzle ORM, Postgres, Zod, OpenAPI | Source of truth. Stores design systems, components, variations, tokens. Exposes a typed REST API and an auto-generated OpenAPI spec. |
| DS Generator | services/ds-generator | 3005 | Fastify | Builds an npm package from a design system stored in the registry. Available both as a web service and as a CLI (plasma-ds-generate). |
| Publisher | services/publisher | 3007 | Express | Receives a packaged design system from the generator and publishes it to a configured npm registry. |
| Docs Generator | services/ds-documentation-generator | 3006 | NestJS, Handlebars, Docusaurus | Pulls a design system from the registry, renders a Docusaurus project from templates, builds the static site, and uploads it to S3. |
| Client | apps/client | 3002 | React 18, Vite, @salutejs/plasma-* |
The end-user UI for composing a design system, editing tokens, and triggering generation/publication/documentation. |
| Admin | apps/admin | 3004 | React 18, Vite, OpenAPI types | Inspector for the registry: schema view, table browser, OpenAPI reference, saved/NL queries. |
- Authoring. A user opens the Client (apps/client) and composes a design system β picks components, edits tokens, previews the result. The client persists everything to the DS Registry over REST.
- Generation. From the same UI the user triggers
generate-downloadorgenerate-publishon the DS Generator. The generator fetches the design system from the registry, builds an npm package, and either streams it back to the client or hands it off to the Publisher. - Publication. The Publisher receives the tarball and pushes it to a configured npm registry, making the design system installable as
@salutejs-ds/<name>. - Documentation. The user (still from the client UI) triggers
documentation/generateon the Docs Generator. It fetches the design system from the registry, renders a Docusaurus project from Handlebars templates, builds the static site, and uploads the result to S3. - Inspection. The Admin app talks to the registry directly through its OpenAPI-typed client, used for debugging schema, browsing tables, and running ad-hoc queries.
design-system-builder/
βββ apps/
β βββ admin/ # React admin (registry inspector)
β β βββ src/
β β β βββ api/
β β β β βββ client.ts # typed API client (openapi-fetch)
β β β β βββ openapi.json # generated OpenAPI spec (artifact)
β β β β βββ types.gen.ts # generated TS types (artifact)
β β β βββ pages/
β β β β βββ TablesPage.tsx # /tables β DB table browser
β β β β βββ QueriesPage.tsx # /queries β saved query catalog
β β β β βββ SchemaPage.tsx # /schema β ER diagram
β β β β βββ DocsPage.tsx # /docs β Scalar API reference
β β β β βββ SettingsPage.tsx # /settings
β β β βββ App.tsx
β β β βββ main.tsx
β β βββ vite.config.ts
β β βββ package.json
β βββ client/ # React DS builder UI (Plasma-based)
β βββ src/
β βββ vite.config.ts
β βββ package.json
βββ services/
β βββ ds-registry/ # Express + Drizzle, central registry API
β β βββ src/
β β β βββ db/
β β β β βββ index.ts # DB connection (postgres-js + Drizzle)
β β β β βββ schema.ts # Drizzle schema
β β β β βββ seed-dev.ts # dev seeding entry point
β β β β βββ seed-prod.ts # prod seeding entry point
β β β β βββ reset.ts # full schema reset
β β β β βββ clear.ts # truncate tables
β β β β βββ seeds/ # per-table seed files (data/dev/prod)
β β β βββ routes/
β β β β βββ index.ts # root router
β β β β βββ api/ # CRUD routes (one file per resource)
β β β β β βββ utils.ts # assertFound, tryCatch
β β β β β βββ design-systems.ts
β β β β β βββ components.ts
β β β β β βββ tokens.ts
β β β β β βββ legacy.ts # /api/legacy/design-systems/:name/*
β β β β β βββ β¦ # the rest of the resources
β β β β βββ misc/ # auxiliary routes
β β β β βββ tables.ts # /api/tables
β β β β βββ queries.ts # /api/queries
β β β β βββ nl-query.ts # /api/nl-query
β β β β βββ schema.ts # /api/schema
β β β βββ validation/
β β β β βββ schema.ts # Zod schemas for all tables
β β β β βββ middleware.ts # validateBody, validateParams
β β β βββ openapi/
β β β β βββ spec.ts # OpenAPI 3 spec (auto-built from Drizzle + Zod)
β β β β βββ generate.ts # writes openapi.json
β β β βββ queries/
β β β β βββ catalog.ts # named query catalog
β β β βββ nl-query/
β β β β βββ llm-service.ts # Z.AI API client
β β β β βββ schema-context.ts # schema context for the LLM
β β β β βββ sql-validator.ts # validation (SELECT only)
β β β βββ zod-extend.ts # extendZodWithOpenApi (called once)
β β β βββ index.ts # Express server entry
β β βββ drizzle/ # generated SQL migrations
β β βββ drizzle.config.ts
β β βββ .env.example
β β βββ package.json
β βββ ds-generator/ # Fastify, builds DS npm packages
β βββ ds-documentation-generator/ # NestJS, builds Docusaurus + S3 deploy
β βββ publisher/ # Express, npm publishing
βββ docs/ # Internal docs (schema, status, todo)
βββ .github/
β βββ workflows/ # CI: build & deploy apps to S3
β βββ actions/
βββ .claude/
β βββ skills/ # Claude Code skills
β βββ sync-all/ # /sync-all β full sync
β βββ sync-validation/ # /sync-validation β refresh Zod schemas
β βββ sync-routes/ # /sync-routes β create/remove routes
β βββ sync-spec/ # /sync-spec β refresh OpenAPI spec
β βββ sync-api-types/ # /sync-api-types β regenerate TS types
βββ docker-compose.yml # Production compose (subset)
βββ docker-compose.dev.yml # Development compose (full stack)
βββ setup-docker.sh # One-command dev setup
βββ package.json # Root scripts (dev / build / install:all)
Recommended path: Docker. It is the only setup that's actively used and tested. Local (non-Docker) development still works for individual services but you have to wire ports and env vars yourself.
- Docker and Docker Compose
- Node.js β₯ 18 (only if you want to run services outside of Docker)
# Clone
git clone <repo-url>
cd design-system-builder
# Start everything (postgres + registry + admin + client + generator + publisher + docs-generator)
./setup-docker.shThe script builds the dev images, starts the stack via docker-compose.dev.yml, runs the registry migrations, seeds dev data, and prints a health summary.
Once it's done:
| Service | URL |
|---|---|
| Client (DS builder UI) | http://localhost:3002 |
| Admin (registry inspector) | http://localhost:3004 |
| DS Registry API | http://localhost:3008/api |
| DS Generator | http://localhost:3005 |
| Docs Generator | http://localhost:3006 |
| Publisher | http://localhost:3007 |
| Postgres (ds-registry) | localhost:5433 |
Common commands:
# Logs
docker-compose -f docker-compose.dev.yml logs -f
# Stop (data persists)
docker-compose -f docker-compose.dev.yml down
# Stop and wipe data
docker-compose -f docker-compose.dev.yml down -v
# Re-run migrations / re-seed manually
docker-compose -f docker-compose.dev.yml exec ds-registry npx drizzle-kit migrate
docker-compose -f docker-compose.dev.yml exec ds-registry npx tsx src/db/seed-dev.tsYou'll need a Postgres instance reachable from ds-registry and the right env vars in each service.
# 1. Start Postgres (you can still use the dev compose for the DB only)
docker-compose -f docker-compose.dev.yml up -d postgres-registry
# 2. Configure ds-registry
cp services/ds-registry/.env.example services/ds-registry/.env
# default values already point at localhost:5433/ds_registry
# 3. Install dependencies for every workspace
npm run install:all
# 4. Apply migrations and seed
cd services/ds-registry
npm run db:migrate
npm run db:seed:dev
cd ../..
# 5. Start everything in parallel
npm run devThe root package.json exposes per-service scripts so you can also start them one by one:
| Script | What it runs |
|---|---|
npm run dev |
All services and apps in parallel |
npm run dev:services |
Backend services only (registry, generator, publisher, docs-generator) |
npm run dev:ds-registry |
DS Registry only (port 3008) |
npm run dev:ds-generator |
DS Generator only (port 3005) |
npm run dev:publisher |
Publisher only (port 3007) |
npm run dev:ds-documentation-generator |
Docs Generator only (port 3006) |
npm run dev:client |
Client app only (port 3002) |
npm run dev:admin |
Admin app only (port 3004) |
npm run build |
Production build of every service and app |
npm run install:all |
Install dependencies in every workspace |
Postgres lives in the dev compose as postgres-registry (host port 5433). The registry connects with:
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/ds_registry
PORT=3008
ZAI_API_KEY=your-zai-api-key-here # for /api/nl-queryInside Docker the host becomes postgres-registry:5432. See services/ds-registry/.env.example.
Run from services/ds-registry:
| Command | Description |
|---|---|
npm run db:generate |
Generate a SQL migration after changing schema.ts |
npm run db:migrate |
Apply migrations to the database |
npm run db:seed:dev |
Seed the DB with dev fixtures |
npm run db:seed:prod |
Seed the DB with prod fixtures |
npm run db:reset |
Drop the schema (run migrate + seed afterwards) |
npm run db:clear |
Truncate tables (keeps the schema) |
npm run db:studio |
Open Drizzle Studio |
First run (without Docker):
docker-compose -f docker-compose.dev.yml up -d postgres-registry
cd services/ds-registry
npm run db:migrate
npm run db:seed:devSchema (schema.ts) or routes (routes/api/) changed:
# Inside services/ds-registry
npm run db:generate # only if schema.ts changed
npm run db:migrate # only if schema.ts changed
# From the repo root, via Claude Code
/sync-all # updates validation, routes, openapi spec, and admin typesReset to a clean state:
cd services/ds-registry
npm run db:reset
npm run db:migrate
npm run db:seed:devThe admin app uses a typed OpenAPI client. Types are generated from the registry's OpenAPI spec β no running backend required:
# Inside apps/admin
npm run api:gen # uses the spec checked into apps/admin/src/api/openapi.json
npm run api:gen:live # fetches it from http://localhost:3008/api/openapi.jsonAfter regeneration the typed client in apps/admin/src/api/client.ts is good to use:
import { api } from "../api/client";
const { data } = await api.GET("/users");
const { data: user } = await api.GET("/users/{id}", {
params: { path: { id: "..." } },
});Run this after any schema/route changes β or just use /sync-all, which does it for you.
| Route | Description |
|---|---|
/tables |
Browse all DB tables, paginated |
/queries |
Saved query catalog with parameters |
/schema |
Interactive ER diagram |
/docs |
Scalar API reference (OpenAPI) |
/settings |
Admin settings |
Skills for keeping the registry's generated artifacts in sync with the schema and routes. Live in .claude/skills/.
| Skill | What it does |
|---|---|
/sync-all |
Runs the four skills below in order |
/sync-validation |
Updates services/ds-registry/src/validation/schema.ts |
/sync-routes |
Creates/removes files in services/ds-registry/src/routes/api/, updates routes/index.ts |
/sync-spec |
Updates services/ds-registry/src/openapi/spec.ts |
/sync-api-types |
Regenerates apps/admin/src/api/openapi.json and types.gen.ts |
GitHub Actions in .github/workflows/ build the two frontend apps and sync them to S3:
- design-system-builder.yml β runs on
master, buildsapps/clientandapps/adminand uploads the dists to the production S3 bucket. - design-system-builder-pr.yml β same flow for pull requests.
Backend services (ds-registry, ds-generator, publisher, ds-documentation-generator) are deployed separately from this repo's CI.
- docs/database-schema.md β registry database schema
- docs/component creation.md β how to create a component
- services/ds-documentation-generator/README.md β docs-generator deep-dive
See repository for license information.