Skip to content

UI surfaces consume the Determined design language

Status: open/active

  • Every live UI surface styles itself through the Determined design language: --d-* tokens, dSemantics intents, and D* components — never raw palette classes, dark: variants, or Nuxt UI color/variant literals.
  • Theming happens through the language’s seams: a theme preset registered via registerTheme(), a token override outside the cascade layer — never a component fork or a :root shadow.
  • Named escape hatches only (see Notes); everything else is a violation.

A surface with a UI (app, product, experience, site, generated report) expresses appearance through the design language:

  • Colors and chrome come from --d-* tokens (directly, or via the --ui-* ← --d-* bridge for U* components). No raw Tailwind palette utilities, no hard-coded hexes, no dark: color variants.
  • Meaning routes through semantics: intent/status/tier props and the dSemantics helpers, not color/variant literals at call sites.
  • Dark mode is the .dark class owned by color-mode and read through useDTheme. Components and experiences never watch prefers-color-scheme while running inside a shell.
  • Icons are Lucide via Iconify (i-lucide-*).
  • Themes are data: a solution or product that wants its own look registers a preset (Tier-1 seeds keyed on data-theme) through the same public API the built-ins use. If the vocabulary cannot express the theme, the vocabulary is extended (spec first) — components are never forked.

The canonical vocabulary is packages/ts/ui/docs/design-system.md; the laws and their rationale are D-DLNG-design-language.

Five apps sharing one framework stack diverged into four palettes, three icon sets, and three dark-mode mechanisms because nothing bound them to the shared layer. The design language exists, is themable, and restyles correctly only when appearance flows through its tokens — every literal is a pixel the theme system cannot reach. One edit retuning the whole suite is the property this rule preserves.

  • New surfaces extend @determined/ui (directly or via a host layer such as @determined/shell) from the first commit.
  • Existing live surfaces migrate per the adoption plan (docs/planning/design-language/PLAN.md Phase 3): extend the layer, let the bridge restyle stock U* components, replace literals opportunistically.
  • Reviewers: a diff that adds bg-gray-*, text-red-*, a dark: color variant, an i-heroicons-* icon, or a color=/variant= literal in an experience is asking for a token, an intent, or a D* component instead.
  • A grep-based repo check (repo tooling, not the sdlc gate registry) backstops review; until it lands, review carries the rule.
  • class="bg-amber-50 dark:bg-amber-900/10" on a warning banner — that is surfaceClasses('warning')’s job, or a status token.
  • <UButton color="error" variant="solid"> at a call site — that is <DButton intent="danger">.
  • window.matchMedia('(prefers-color-scheme: dark)') inside an experience to pick a canvas theme — that is useDTheme().resolvedTheme.
  • A bare :root { --d-bg-base: #0f1115 } in an app stylesheet — that is a theme preset registered on data-theme, not a root shadow.

Named escape hatches:

  • Frozen/legacy surfaces (apps/legacy-polish, apps/ontological-legacy) are exempt until retired.
  • The Obsidian plugin (solutions/agent-pants) inherits the host vault’s theme by design and ships no styling of its own.
  • Standalone static surfaces with no shell (single-file dashboards, generated static pages) may use prefers-color-scheme and a local projection of the seed tokens.
  • UButton soft/ghost utility rows inside experiences: allowed within the boundary the design-system spec defines; the boundary’s final wording is a Phase 2 item in the design-language plan.

← Back to Standards