UI surfaces consume the Determined design language
Status: open/active
Summary
Section titled “Summary”- Every live UI surface styles itself through the Determined design language:
--d-*tokens,dSemanticsintents, andD*components — never raw palette classes,dark:variants, or Nuxt UIcolor/variantliterals. - 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:rootshadow. - 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 forU*components). No raw Tailwind palette utilities, no hard-coded hexes, nodark:color variants. - Meaning routes through semantics:
intent/status/tierprops and thedSemanticshelpers, notcolor/variantliterals at call sites. - Dark mode is the
.darkclass owned by color-mode and read throughuseDTheme. Components and experiences never watchprefers-color-schemewhile 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.
How to apply
Section titled “How to apply”- 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.mdPhase 3): extend the layer, let the bridge restyle stockU*components, replace literals opportunistically. - Reviewers: a diff that adds
bg-gray-*,text-red-*, adark:color variant, ani-heroicons-*icon, or acolor=/variant=literal in an experience is asking for a token, an intent, or aD*component instead. - A grep-based repo check (repo tooling, not the sdlc gate registry) backstops review; until it lands, review carries the rule.
Anti-examples
Section titled “Anti-examples”class="bg-amber-50 dark:bg-amber-900/10"on a warning banner — that issurfaceClasses('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 isuseDTheme().resolvedTheme.- A bare
:root { --d-bg-base: #0f1115 }in an app stylesheet — that is a theme preset registered ondata-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-schemeand a local projection of the seed tokens. UButtonsoft/ghostutility 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.