Skip to content

Repository files navigation

Manti UI Theme Studio

Build a Manti UI theme by moving sliders, not by guessing hex codes.

studio.manti.design · Docs · Components


Pick a color, drag the roundness, retype the whole system, and watch a real product surface re-skin under your hands: an app bar, stat cards, a form with fifteen controls, a sortable table, overlays, alerts, a calendar. When it looks right, take it with you as CSS, as a prompt for your coding agent, as JSON, or as a link.

What you can tune

Group Knobs
Presets Six starting points (Manti, Violet, Ocean, Forest, Rose, Graphite), every value still editable afterwards
Color A base color per variant (primary, secondary, tertiary, success, info, danger), each expanded into the full --variant-* role vocabulary
Neutral One hue drives every gray, surface, border and text role; light/dark switch beside it
Shape One radius factor rescaling the whole xs to 2xl ramp
Typography Sans font stack and the base text size every other size derives from
Density Spacing unit and control heights, plus focus-ring width

Not sure where to start? Surprise me rolls a coherent random theme.

Taking it with you

The Copy theme dialog hands the result over four ways:

  • CSS tokens. A stylesheet you drop into your app, with the import order spelled out in its header.
  • AI prompt. A brief for Claude Code, Cursor or any agent working in your repo: where to put the file, why it must stay unlayered, and how Manti's token tiers relate.
  • JSON. The raw studio state, for storing themes in your own tooling.
  • Share link. The whole theme base64url-encoded into ?t=, so a URL reopens the studio exactly as you left it. No account, no server.

How it works

Every knob writes a public token override, the same escape hatch a consumer uses. The studio collects them into one unlayered <style> element in <head>, which beats Manti's layered @layer manti.tokens defaults without a single !important.

That is why the CSS in the export dialog is byte-for-byte the CSS the page you are looking at is running: one builder feeds both. Nothing here is studio-only. If the preview looks right, pasting the exported CSS into your app gives you the same result.

/* what a themed app ends up importing */
@import '@manti-ui/styles/index.css';
@import './manti-theme.css'; /* the studio's output, unlayered, last */

Run it locally

Node >=22.12.0, pnpm 10.

pnpm install
pnpm dev          # http://localhost:5173
pnpm verify       # lint + typecheck + production build
pnpm preview      # serve the built dist/ on :4173

Code map

Path What lives there
src/theme/customTheme.ts The theme store: state, token-CSS generation, persistence, URL sharing
src/theme/useTheme.ts Light/dark, driven by data-theme on <html>
src/studio/ThemeControls.tsx The control rail: presets, colors, hue, shape, type, density
src/studio/ThemePreview.tsx The preview canvas, a product-shaped tour of the component set
src/studio/ExportDialog.tsx CSS / AI prompt / JSON / share-link hand-off
src/styles/studio.css App chrome, in its own @layer studio

Adding a knob touches two places: ThemeConfig plus buildThemeCss() in customTheme.ts, which is also what gets exported, and a control in ThemeControls.tsx. The preview needs no change; it reads tokens like every other Manti consumer.

The app depends on the published @manti-ui/* packages rather than a workspace link, so it exercises the design system the way a third party does. To try an unreleased library change, pnpm link the package from the manti-ui/ui checkout.

Upstream backport: src/styles/studio.css ends with a small block that repairs Slider marks positioning in @manti-ui/styles 0.8.0. It is fixed upstream, so delete the block once this app depends on the release carrying it.

Deploy

Netlify, from netlify.toml: pnpm build into dist/, a www to apex 301, and a CSP that allows the preview's avatar portraits (i.pravatar.cc).

There is deliberately no SPA catch-all. The app has one route and a shared theme rides in the query string, so /?t=... already resolves index.html as a static file. A /* rewrite would answer every typo with a 200 and the full studio, which reads to a crawler as unlimited URLs serving identical content; unmatched paths get 404.html and a real 404 instead. Bring the rewrite back the day this app grows a router.

License

MIT, same as Manti UI.

About

Theme Studio for Manti UI

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages