Tokens
The --nyte-* custom properties every primitive is built from, read live from @nyte-ai/ui
Tokens are the only values a primitive is allowed to paint with. They are declared once in
packages/ui/src/platform-tokens.stylex.ts as stylex.defineVars groups, and every entry is a
literal --nyte-* name so the same token can be read from StyleX, from Tailwind, and from plain CSS.
platform-tokens.stylex.ts typed StyleX handles tokens.color["--nyte-color-primary"]
└─ sync-platform-tokens.mjs
└─ platform-tokens.css :root declarations var(--nyte-color-primary)
└─ tailwind.css @theme inline mapping bg-primary, text-body, rounded-lgpnpm --dir packages/ui typecheck runs check:tokens and fails if the CSS is stale. The tables on
this page are read from the generated CSS at build time, so they cannot disagree with the package.
Rules
- A primitive must read colors, radii, sizes, and motion through
tokens.*. Literal colors inpackages/ui/src/componentsare a defect. - Colors are
light-dark()pairs. The theme followscolor-schemeon an ancestor, not a class. A host that wants dark mode setscolor-scheme: dark; it does not restyle tokens. - Consumers override tokens on an ancestor, ideally the app root, and only the ones that carry the app's identity. See Theming.
--nyte-color-focus-ringisvar(--nyte-color-ring)by default and is the one token a host is expected to zero out at runtime. See Focus.
Color
Semantic roles, not a palette. primary is the filled button; muted is the hover wash and the
avatar background; border has four strengths; bubble-* and sidebar exist for the conversation
surface.
| Token | Value |
|---|---|
--nyte-color-background | light-dark(#fcfcfc, #181818) |
--nyte-color-foreground | light-dark(#141414, #fcfcfc) |
--nyte-color-popover | light-dark(#fcfcfc, #181818) |
--nyte-color-popover-foreground | light-dark(#141414, #fcfcfc) |
--nyte-color-primary | light-dark(#070707, #fafafa) |
--nyte-color-primary-hover | light-dark(#2f2f2f, #d5d5d5) |
--nyte-color-primary-foreground | light-dark(#fcfcfc, #141414) |
--nyte-color-muted | light-dark(#77777717, #7777772c) |
--nyte-color-muted-hover | light-dark(#7777772b, #77777752) |
--nyte-color-muted-foreground | light-dark(#14141499, #fcfcfc99) |
--nyte-color-tertiary-foreground | light-dark(#14141466, #fcfcfc66) |
--nyte-color-destructive | light-dark(#c21d2e, #ff5667) |
--nyte-color-destructive-muted | light-dark(#ff263c17, #ff263c2c) |
--nyte-color-destructive-hover | light-dark(#ff263c2b, #ff263c52) |
--nyte-color-warning | light-dark(#c27400, #ffaf38) |
--nyte-color-border-subtle | light-dark(#1414140d, #fcfcfc0d) |
--nyte-color-border-weak | light-dark(#1414141a, #fcfcfc1a) |
--nyte-color-border | light-dark(#14141426, #fcfcfc26) |
--nyte-color-border-strong | light-dark(#1414144d, #fcfcfc4d) |
--nyte-color-field-background | light-dark(#fcfcfc, #2f2f2f) |
--nyte-color-ring | light-dark(#14141466, #fcfcfc66) |
--nyte-color-focus-ring | var(--nyte-color-ring) |
--nyte-color-scrim | light-dark(#14141480, #141414b2) |
--nyte-color-sidebar | light-dark(#f7f7f7, #141414) |
--nyte-color-bubble-agent | light-dark(#eeeeee, #262626) |
--nyte-color-bubble-user | light-dark(#070707, #5a5a5a) |
--nyte-color-bubble-user-foreground | light-dark(#fcfcfc, #fcfcfc) |
--nyte-color-avatar-background | light-dark(#77777717, #7777772c) |
--nyte-color-avatar-foreground | light-dark(#3d3d3d, #b7b7b7) |
--nyte-color-avatar-neutral-solid | light-dark(#777777, #777777) |
--nyte-color-avatar-orange-background | light-dark(#ff670017, #ff67002c) |
--nyte-color-avatar-orange-foreground | light-dark(#c24e00, #ff8838) |
--nyte-color-avatar-orange-solid | light-dark(#ff6700, #ff6700) |
--nyte-color-avatar-blue-background | light-dark(#1084fe17, #1084fe2c) |
--nyte-color-avatar-blue-foreground | light-dark(#0c64c1, #459ffe) |
--nyte-color-avatar-blue-solid | light-dark(#1084fe, #1084fe) |
--nyte-color-avatar-violet-background | light-dark(#9159fe17, #9159fe2c) |
--nyte-color-avatar-violet-foreground | light-dark(#6e44c1, #a97efe) |
--nyte-color-avatar-violet-solid | light-dark(#9159fe, #9159fe) |
--nyte-color-avatar-green-background | light-dark(#00c97217, #00c9722c) |
--nyte-color-avatar-green-foreground | light-dark(#009957, #38d591) |
--nyte-color-avatar-green-solid | light-dark(#00c972, #00c972) |
Font
One UI family and a five-step type scale. label and body share a size and line height on
purpose: labels sit beside fields, and mixed sizes on one row misalign.
| Token | Value |
|---|---|
--nyte-font-family-ui | -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif |
--nyte-font-size-caption | 11px |
--nyte-font-size-detail | 12px |
--nyte-font-size-label | 13px |
--nyte-font-size-body | 13px |
--nyte-font-size-title | 14px |
--nyte-font-weight-regular | 400 |
--nyte-font-weight-medium | 500 |
--nyte-font-weight-semibold | 600 |
| Token | Value |
|---|---|
--nyte-leading-caption | 14px |
--nyte-leading-detail | 16px |
--nyte-leading-label | 18px |
--nyte-leading-body | 18px |
--nyte-leading-title | 22px |
Radius
control and field are the same today and are separate tokens so they can diverge. avatar is
a percentage so it scales with the size steps; pill is the circle.
| Token | Value |
|---|---|
--nyte-radius-control | 8px |
--nyte-radius-field | 8px |
--nyte-radius-menu | 10px |
--nyte-radius-dialog | 14px |
--nyte-radius-avatar | 42% |
--nyte-radius-pill | 9999px |
Control
The geometry of anything you press or type into. height-md (28px) is the default button and
input height; height-sm (24px) is the compact button and the icon button. menu-inset is the
left padding that leaves room for a radio or checkbox indicator.
| Token | Value |
|---|---|
--nyte-control-height-sm | 24px |
--nyte-control-height-md | 28px |
--nyte-control-textarea-min-height | 64px |
--nyte-control-padding-xs | 6px |
--nyte-control-padding-sm | 8px |
--nyte-control-padding-lg | 12px |
--nyte-control-menu-inset | 28px |
--nyte-control-gap-sm | 4px |
--nyte-control-disabled-opacity | 0.56 |
--nyte-control-focus-width | 1px |
--nyte-control-focus-offset | 1px |
Border
| Token | Value |
|---|---|
--nyte-border-control-width | 1px |
--nyte-border-hairline-width | .5px |
Avatar
| Token | Value |
|---|---|
--nyte-avatar-size-xs | 20px |
--nyte-avatar-size-sm | 24px |
--nyte-avatar-size-md | 28px |
--nyte-avatar-size-lg | 36px |
--nyte-avatar-font-xs | 9px |
--nyte-avatar-font-sm | 10px |
--nyte-avatar-font-md | 11px |
--nyte-avatar-font-lg | 14px |
--nyte-avatar-ring-width | .5px |
Space
Only three steps ship; layout spacing belongs to the app.
| Token | Value |
|---|---|
--nyte-space-1 | 4px |
--nyte-space-2 | 8px |
--nyte-space-4 | 16px |
Motion
fast for state changes on controls, normal for popups. Every primitive collapses its transition
to 0s under prefers-reduced-motion: reduce.
| Token | Value |
|---|---|
--nyte-motion-fast | 100ms |
--nyte-motion-normal | 160ms |
--nyte-motion-ease-out | cubic-bezier(.2,.8,.2,1) |
Elevation
Shadows are light-dark() too: the dark values are transparent, because on a dark surface a border
does the separating.
| Token | Value |
|---|---|
--nyte-elevation-field | 0 1px 3px 0 light-dark(#0000001f, #00000000) |
--nyte-elevation-menu | 0 10px 20px -3px light-dark(#0000001a, #00000000), 0 4px 6px -4px light-dark(#0000001a, #00000000), 0 0 0 1px light-dark(#e4e4e40a, #e4e4e400) |
--nyte-elevation-dialog | 0 22px 70px 4px light-dark(#0000008f, #00000000), 0 0 0 .5px light-dark(#0000001a, #00000000) |
Overlay
Sizes and stacking for dialogs and menus. --nyte-menu-max-width and -max-height read Base UI's
--available-width / --available-height, which the Positioner sets from the viewport.
| Token | Value |
|---|---|
--nyte-dialog-width | calc(100% - 32px) |
--nyte-dialog-max-width | 384px |
--nyte-dialog-max-height | calc(100dvh - 32px) |
| Token | Value |
|---|---|
--nyte-menu-min-width | 160px |
--nyte-menu-max-width | var(--available-width) |
--nyte-menu-max-height | var(--available-height) |
| Token | Value |
|---|---|
--nyte-layer-dialog | 50 |
--nyte-layer-menu | 60 |
Tailwind mapping
@nyte-ai/ui/tailwind.css maps tokens into Tailwind v4's @theme inline so utilities resolve to
the same variables. Import @nyte-ai/ui/styles.css for the tokens plus the mapping, or
@nyte-ai/ui/platform-tokens.css for tokens only.
| Tailwind token | Reads |
|---|---|
--font-sans | --nyte-font-family-ui |
--color-primary | --nyte-color-primary |
--color-muted | --nyte-color-muted |
--color-border | --nyte-color-border |
--color-ring | --nyte-color-ring |
--text-body | --nyte-font-size-body |
--text-body--line-height | --nyte-leading-body |
--radius-sm, --radius-md | --nyte-radius-control |
--radius-lg | --nyte-radius-menu |
--radius-xl | --nyte-radius-dialog |
The full list is the file itself: packages/ui/src/tailwind.css.
Desktop variables
The desktop app does not consume these tokens directly. It defines its own t.* constants in
desktop/src/renderer/src/theme/vars.stylex.ts over a separate runtime palette. See
Desktop › Theme variables.