Studio

Where a design-system team brings its tokens, decides what generated screens may look like, authors screens of its own, delivers them to products, and sees how they do. Hosted at studio.polyxd.com, or run on your own Cloudflare account.

Coming soon. The hosted Studio at studio.polyxd.com isn't open yet. This page describes what it will do.

Studio is the team's side of Polyxd: source-available (the Functional Source License, in apps/studio: free to run for your own team or company, not as a competing hosted service, and each version becomes Apache-2.0 after two years), running on Cloudflare Workers with D1 and R2, and the same code whether you use the hosted one or your own. The hosted Studio has a Free plan and paid ones (Plans); a Studio you run yourself has no plans and no limits.

Your design system#

Direction#

A Design Direction is the team's taste as one versioned file. Studio edits a whole one, section by section, and products fetch the published one by key.

Every change is checked against the Direction schema (direction.schema.json, and the pattern schema for your own patterns), and a problem shows beside the control it's about; a Direction that doesn't fit isn't saved. Save keeps a version with a note and your own version number (Studio suggests the next one); Changes compares the editor with the published version, or any saved one, field by field ("Density: Comfortable → Compact", "Words to avoid: added “kindly”"); Publish makes a version the one products get. Export downloads the Direction as <key>.direction.json, valid against the schema; Import loads a Direction file into the editor as unsaved changes, keeps your own patterns it still lists, and offers any rules in it that the workspace lacks.

Screens#

Where designers author a product's surfaces, in the same format a generator writes:

Shells are authored the same way. New screen → Shell starts from the spec's shell example, named after your product: a Frame with an AppBar, a main Navigation, the Outlet, an aside and a Footer. The tree shows the Frame's regions as labelled slots; the preview draws the shell with PolyxdFrame and a stand-in in the Outlet (a placeholder, or any published screen of the workspace), at phone, tablet and desktop, so the navigation's bar, rail and side forms show. The surface's kind and origin are edited from the Surface row; the checker applies the spec's shell rules (shell components only in a shell, which is authored, with a Frame at the root and exactly one Outlet under its main), and the picker refuses a shell component in a surface with the same message.

Delivering a screen to a product#

A published screen is fetched by key, with an API key from Team → API keys (keys can import tokens and read design systems, screens and Directions, nothing else):

curl -H "Authorization: Bearer $POLYXD_STUDIO_KEY" \
  https://studio.polyxd.com/api/w/<workspace>/screens/<key>

It returns the document with surface.origin: "authored" and an X-Polyxd-Screen-Version header. A key reads published screens and design systems and can't change anything; fetch on the server or at build time, since a screen changes when someone publishes, not on every request. Render it with PolyxdSurface (or PolyxdFrame for a shell) exactly like a generated one.

Delivering a Direction#

A published Direction is fetched the same way, by its key, which is also its name in the file:

curl -H "Authorization: Bearer $POLYXD_STUDIO_KEY" \
  https://studio.polyxd.com/api/w/<workspace>/directions/<key>

It returns the Direction, valid against direction.schema.json, with an X-Polyxd-Direction-Version header (the Studio version number); an unpublished one answers 404. Paths inside it are relative to that address: your own patterns are listed in patterns.custom as <key>/patterns/<id>.json, fetched from …/directions/<key>/patterns/<id>.json, and an exemplar screen as ../screens/<screen>, which is the screen's own delivery address. Hold a document to it with directionRules(direction) from @polyxd/spec, as in Design Direction. A key reads Directions and can't change them.

Insights#

Insights shows how your screens do in your product, from the semantic events your product's renderer already emits. Studio counts them each day. It never keeps the events.

To start, make an ingest key (on the Insights page, or in Team) and point toFetch from @polyxd/analytics at your workspace:

const studio = toFetch("https://studio.polyxd.com/api/w/<workspace>/events", {
  headers: { "x-polyxd-key": "<ingest key>" },
});
<PolyxdSurface document={doc} onEvent={studio} events={{ generator: "my-model@3" }} />

Name the generator on generated screens; a screen whose events name none counts as authored.

An ingest key is publishable, like the key a web analytics tool puts in a page. It can send events to its own workspace and nothing else: it can't read a screen, a Direction, a design system or Insights, and it isn't an API key. Studio shows it again whenever you need it, and revoking it stops it at once. The endpoint, POST /api/w/<workspace>/events, answers browsers from any origin, without cookies. It takes up to 100 events and 64 KB a request, and limits requests per key and per address.

What Studio keeps: daily counts by intent, surface id, pattern, event type, component key, capability, reason code, generated or authored, and person or agent, plus sums of completion times and feedback ratings. Every one of those must be a short code, so a value in the wrong place is left out. An event with a property the schema doesn't define is dropped whole, and the answer says how many were. Never kept: the events, session ids, timestamps, values, experiment variants, the Direction or the generator's name. Counts are kept for 90 days, the same on every workspace, since Studio has no plans yet. The workspace owner can delete them all from the Insights page.

Team#

Workspaces, invites with roles (design-system, designer, product, engineer, viewer), sign-in through better-auth (email and password with verification, Google when configured), API keys, and ingest keys for Insights. Owners can take someone out of a workspace, and an invite can be withdrawn before it is used.

Plans#

Plans apply to the hosted Studio only. You pay per editor: a viewer is always free, and every other role (owner, design-system, designer, product, engineer) is an editor.

Free Pro Team Enterprise
Price $0 $8 a month, or $80 a year $12 per editor a month, or $120 a year talk to us
Workspaces you own 1 3 unlimited unlimited
Editors 2 1 unlimited unlimited
Design systems 1 unlimited unlimited unlimited
Directions 1 unlimited unlimited unlimited
Published screens 10 unlimited unlimited unlimited
Fetches by key a month (screens, Directions, tokens) 10,000 250,000 1,000,000 10 million or more
Version history last 10 all all all

Roles#

Role How many What they can do
Owner exactly one Everything, plus billing, deleting the workspace and handing it to someone else
Admin any number Everything except those three: invite, remove, change roles, edit tokens, rules, screens and Directions
Design system any number Tokens, components, rules, releases
Designer any number Direction, reviews, exemplars
Product any number Capabilities, journeys, Insights
Engineer any number Components, capabilities, integrations
Viewer any number Everything, read only — and always free

Owner is never invited or set from the role list. It moves only by handing the workspace over (Team → Make owner), which makes the new person owner and the previous one an admin in the same step, so there is always exactly one. Everyone but a viewer counts as an editor for your plan's seats.

Support#

The people who run a hosted Studio can open /admin: find any workspace or person, set a plan by hand, hand a workspace to a new owner when the old one has gone, take someone out, and read what Stripe says about a customer. It never writes to Stripe.

Who counts is the SUPER_ADMINS secret — email addresses separated by commas, checked against the signed-in person on every request. It is deliberately not a column, so editing the database grants nobody access. Every change is recorded with the address that made it, what it was before and after, and the reason given.

A plan set by hand is separate from a Stripe subscription. Enterprise is the one a Stripe event never overwrites, so use it for comps, design partners and deals invoiced elsewhere.

Run it yourself#

git clone https://github.com/visualfart/polyxd && cd polyxd && npm install
npm run db:migrate -w @polyxd/studio      # local D1
npm run dev -w @polyxd/studio             # http://localhost:8789

For production: a D1 database, an R2 bucket, SECRETS_KEY and AUTH_SECRET secrets, RESEND_API_KEY for email, and npm run deploy -w @polyxd/studio. The README in apps/studio has the exact steps. Your own Studio has no plans and no limits: as many editors, design systems, Directions, screens and fetches as you like, and no Billing page.

The licence lets you run Studio for your own team or company, change it, and share your changes. It does not let you offer Studio, or something substantially like it, as a service to others. Each version becomes Apache-2.0 two years after its release.

Polyxd is an early preview. Found something unclear? It will get better with your feedback.