fayzfayz sdk
PTEN

Design system and theming

A theme isn't a color. It's a system of chained decisions: you pick a brand, and from it comes a derived palette, a per-mode semantics (light/dark), a typography scale, a spacing grid, a radius scale, an elevation set, and a motion vocabulary. Changing "the app's color" actually means pulling on one end of that chain — and the whole point of a design system is to guarantee the rest of the chain stays coherent.

In Fayz, the manifest's theme block is the condensed expression of that system. You declare very little; the platform runtime fills in the rest consistently. Before you write that block, it's worth understanding the full system it summarizes.

The eight token families

A mature design system organizes its decisions into families. The values below come from Fayz's official design system — they're a concrete example of how much richness each family carries.

FamilyWhat it decidesExample (Fayz design system)
Brand + tintsthe signature color and its derived variationsignite #2FDD4Bdeep #119A27, soft #DFFBE3, bg #F2FDF4
Neutralsthe gray scale that carries text and surfacespaper #FAFAF8near-black #0B0F0E (12 steps)
Secondariesoccasional accents, used sparinglyyellow #F2DB0F, cyan #17C1EA, coral #FF6B5C
Per-mode semanticsthe bg / surface / border / text mapping per themelight uses paper + dark text; dark flips to near-black + white text
Typographyfamilies and hierarchy by weightOutfit (display), DM Sans (body), JetBrains Mono (code)
Spacingthe grid that aligns everythinga 4px grid (4, 8, 12, 16, 24, 32…)
Radiusthe personality of the cornersxs 4pxsm 8pxmd 14pxlg 22pxpill 9999px
Elevation + motionshadows, the signature glow, and the timingshadow-smshadow-glow (green halo); ease-out cubic-bezier(0.22,1,0.36,1), 240ms

There's a ninth piece too, optional but characteristic of the brand: frosted glass (rgba(255,255,255,0.55) + blur 24px) and the aurora — a green radial-gradient bleeding out behind the hero. These are recipes, not loose colors: each one combines background, border, blur, and glow into a single named token.

These docs use that very system: the body is Inter, code is JetBrains Mono, the primary button carries the ignite-green glow, and the hero carries the aurora. A well-built design system applies just as well to an admin panel as to a docs page.

From one color to a system

The point of a design system shows up when you follow one decision all the way through. Take the brand ignite #2FDD4B:

  • As a fill (a button, a block), the vivid green #2FDD4B works with near-black text on top — contrast stays high because the green is light.
  • As link text on a white background, #2FDD4B fails contrast. So the system uses deep #119A27 — the same brand, darkened — to keep the link readable.
  • In dark mode, the logic flips: the background becomes near-black, the text becomes white, and the same green now glows harder (the aurora's opacity goes from 0.55 to 0.65).
  • For selection and hover halos, soft #DFFBE3 steps in; for subtle surface accents in light mode, bg #F2FDF4.

Notice that reasoning about contrast and accessibility is part of the design system, not an afterthought. One brand decision turned into four tokens with distinct roles, each chosen to stay readable in the right context. That's why hand-declaring dozens of colors is fragile — and deriving them from a single root is robust.

What the Fayz runtime does with your theme today

In the manifest you write very little:

{
  "theme": {
    "brand": "teal",
    "radius": "lg",
    "mode": "system"
  }
}
  • brand — the brand color. Ready-made values: blue, violet, green, orange, red, pink, teal.
  • radius — the corner rounding: none, sm, md, lg, full.
  • mode — the color mode: light, dark, or system.

Today fayz doctor validates the structure of the manifest (the presence and shape of the theme block), but not yet the values of these enums — validation that brand/radius/mode fall inside those lists is on the way (an SDK fix is in PR).

From there, the shell's theme engine (createTheme / applyTheme, in @fayz-ai/saas) does the derivation. Verified in the code, from a single brand color it produces:

  • primary = the brand, with a white primaryForeground — readable text over the colored fill.
  • ring = the brand — the focus ring inherits the identity.
  • accent = the brand with the hue rotated ~50° (same saturation and lightness), automatically generating a second harmonic tone.
  • An optional colored sidebar: the engine tints the rail's background with the brand, forces white text, darkens the border by ~12% and the active item by ~15% lightness, and mutes the secondary icons (hue 35% 82%) — all derived from the same root so text stays readable over the saturated rail.

Everything else — neutrals, surfaces, borders, success/warning/destructive semantics, the radius scale, the shadows, and the typography — comes from a curated base preset (the default is the classic admin) that your brand slots into. applyTheme then writes each value as a CSS variable on the document (--primary, --ring, --accent, --sidebar, --button-radius, --font-family, --shadow-sm/md/lg, and the glass tokens like --surface-backdrop-filter). In other words: your brand is one input into a larger, already-consistent token system — exactly the design system model.

The derivation happens in the Fayz platform runtime, which consumes the theme block to paint the whole shell. In the scaffold's local preview — today an anchor screen — the palette isn't applied yet. That's why you validate this step with doctor (valid config) while the full visual result shows up in the runtime. Never count on the brand rendering in the isolated local preview.

Where this is headed Experimental

Today the manifest exposes three decisions (brand, radius, mode). The direction is to bring in the richer families from the official design system — the type scale, elevation, frosted glass, and motion — as customization seams, without bloating the manifest. When you outgrow what config can solve, the path already exists: levels 5–7 of the ladder in Customization let you register your own tokens and components, referenced as data, without forking the SDK.

The order that works best is outside-in:

  1. Design the system as an artifact first — a tokens.css with the eight families, the way Fayz's own design system was built in an AI design session. Having the colors, tints, typography, and elevation written in one place forces the contrast decisions before any code.
  2. Summarize it in the manifest — the theme block becomes the condensed expression of that artifact: the brand root, the corner personality, and the mode strategy. The runtime derives the rest in the direction your artifact already set.
  3. Validate with doctor — and iterate on the artifact when you need more richness, promoting tokens into the customization seams as it grows.

That way the manifest is never where you "discover" the theme — it's where you declare it, after you've already thought through the whole system.

See also: the full token reference — a quick lookup for the eight families and the CSS variables the runtime writes.


Next: the seams for going beyond config in Customization.