Skip to content

Design tokens

Every visual aspect of the platform — color, spacing, radius, motion, shadows — is parameterized as a CSS custom property. The frontend SDK ships 500+ tokens in styles/variables.css, plus a Tailwind preset that exposes them as utilities. Plugin frontends should reference tokens, never hex codes.

Why tokens

When a deployment overrides the platform's brand color (a lab might want a different primary), every plugin re-themes automatically — no per-plugin update needed. Hardcoded #4F46E5 in your plugin breaks that.

Tokens also make light/dark/density work universally. The dark theme just changes the variable values; a plugin that uses var(--bg-primary) flips correctly without code changes.

Setup

Frontend scaffolding is included by default with mint init (skipped when --no-frontend is passed). Manual setup:

ts
// src/main.ts
import '@morscherlab/mint-sdk/styles'
ts
// tailwind.config.ts
import preset from '@morscherlab/mint-sdk/tailwind.preset'
export default {
  content: ['./index.html', './src/**/*.{vue,ts}'],
  presets: [preset],
}

Token families

Brand

VariableMeaningDefault light
--color-primaryIndigo brand color#4F46E5
--color-primary-hoverHover state#4338CA
--color-primary-activeActive/pressed#3730A3
--color-primary-softSoft tint for backgroundsrgba(79, 70, 229, 0.14)
--color-ctaOrange CTA#F97316
--color-cta-hoverCTA hover#EA580C

Use brand tokens for: links, primary buttons, focused inputs, the most-prominent action on a screen.

Semantic feedback

VariableUse
--mint-successSuccessful operations
--mint-errorErrors, destructive actions
--mint-warningWarnings, "needs review" states
--mint-infoInformational notices

Each ships variants: --mint-{name}-bg, --mint-{name}-border, --mint-{name}-text.

Surfaces

VariableUse
--bg-primaryThe main page background
--bg-secondaryCard / panel surface
--bg-tertiaryRecessed surface (e.g., inside a card)
--bg-overlayModal / popover backdrop
--border-defaultDefault 1px border color
--border-strongHigher-contrast border
--border-subtleLower-contrast border

Text

VariableUse
--text-primaryMain text color
--text-secondaryLess-emphasized text (labels, captions)
--text-mutedEven more recessed (helper text)
--text-inverseText on dark backgrounds

Focus

VariableUse
--focus-ringColor of the focus ring
--focus-ring-offsetBackground outside the ring
--focus-ring-widthRing thickness

Every interactive component honors these. Custom components should follow the same pattern.

Spacing and radius

Tailwind's standard scale (p-2, p-4, rounded-lg) backed by CSS variables. The SDK preset also exposes:

VariableUse
--radius-defaultComponent-level corner radius (0.5rem)
--radius-sm, --radius-md, --radius-lg, --radius-xlStandard scale
--shadow-sm, --shadow-md, --shadow-lgElevation
--transition-fast150ms
--transition-default200ms

Motion

VariableUse
--ease-defaultStandard easing curve
--ease-in-outSymmetric easing
--motion-disabled0ms — set when prefers-reduced-motion: reduce

The SDK respects prefers-reduced-motion globally. Custom animations should multiply their duration by var(--motion-disabled, 1) or check the media query.

Tailwind utilities

The preset maps every token to a utility. Use these in templates:

vue
<div class="bg-bg-primary text-text-primary border border-border-default p-4 rounded-default">
  <h2 class="text-text-primary font-semibold">Title</h2>
  <p class="text-text-secondary text-sm">Subtitle</p>
  <div class="bg-bg-secondary p-3 rounded-md mt-2">Recessed content</div>
</div>
Utility prefixMaps to
bg-bg-*var(--bg-*)
text-text-*var(--text-*)
border-border-*var(--border-*)
bg-mint-{success,error,warning,info} / bg-mint-{name}-bgSemantic backgrounds
text-mint-{success,error,warning,info}Semantic text
text-color-primary, bg-color-primary*Brand color
rounded-default, rounded-sm, rounded-md, …Token-backed radii

Custom CSS

When utility classes aren't enough:

vue
<style scoped>
.my-card {
  background: var(--bg-secondary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-default);
  box-shadow: var(--shadow-md);
  transition: box-shadow var(--transition-default) var(--ease-default);
}

.my-card:hover {
  box-shadow: var(--shadow-lg);
}

.my-card:focus-within {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: 2px;
}
</style>

Don'ts

  • Don't hardcode hex codescolor: #4F46E5; won't re-theme. Use var(--color-primary) or the Tailwind utility.
  • Don't reach into the platform's frontend — your plugin is its own bundle and shouldn't import platform code. Tokens are the contract.
  • Don't reinvent semantic colors--mint-success already exists. A different green from yours will look out of place.
  • Don't bake in transition-duration: 150ms; — use var(--transition-fast) so reduced-motion users get the right behavior.

Auditing your plugin

A quick check before merging plugin frontend changes:

bash
# Find any hardcoded hex codes in your plugin's Vue / CSS
grep -rE "#[0-9a-fA-F]{6}\b" src/ frontend/src/

Hits are usually candidates for var(--…) substitution.

Notes

  • Token names are stable across SDK minor versions. Major bumps may introduce new families or rename existing ones — check the changelog.
  • The platform may override variables in its own root stylesheet (packages/sdk-frontend/src/styles/variables.css is the master; the platform's frontend/src/style.css can :root { --color-primary: ...; } to re-skin).
  • Plugin Histoire stories should be reviewed in light, dark, AND white backgrounds — every story file declares them.
  • Theming — palette overrides, density, accessibility
  • Components — every component reads tokens
  • The token source: packages/sdk-frontend/src/styles/variables.css

MINT is open source. Made by the Morscher Lab.