Skip to content

DOL Figma Variable Selection - Decision Tree

activeUpdated

Given a design task (“I want to X”), pick the right DS-Token variable in ≤3 decisions.

🛑 Match-and-refuse (entry-level): nếu bạn sắp invent tên variable chưa có trong registry, hoặc sắp bind text-color/* cho icon node, STOP - grep variable-registry.md trước + consult naming-convention.md §1 (4-category rule). Hex fallback chỉ khi genuinely không có token.

Flow:

  1. Identify category (4-category rule): is it a fill / text / icon / border?
  2. Identify family (semantic role): neutral / brand / info / success / warning / danger / special / ai
  3. Identify level (intensity): dim (subtle bg) / soft (hover) / primary (default) / secondary (focus/subordinate) / subtle (de-emphasized text)

Then look up in variable-registry.md for the exact name + hex.


§1 Category decision - the 4-category rule

Section titled “§1 Category decision - the 4-category rule”

First question always: what am I styling?

Visual propertyCategoryFigma target
Background of a frame/shape/componentfill/* or surface-page/*Frame fill
Text inside a text nodetext-color/*Text node fill
Icon vector (SVG path fills)icon-color/*Vector fill (NOT text-color even if same hex)
Stroke on frame / border line / divider / focus ringborder-color/*Stroke

Critical: don’t use text-color/* on icons - even if hex identical today, future mode variants may diverge. See anti-pattern figma-naming-6-binding#02.


§2 Family decision - the 8 semantic roles

Section titled “§2 Family decision - the 8 semantic roles”

Second question: what does this element mean?

FamilyMeaningTypical use
neutralNo semantic meaning; structure/copyBody text, card borders, page bg, default dividers
brandDOL identity; CTA emphasisPrimary button, logo-adjacent accent, hero CTA (use sparingly)
infoInformational / in-progress / activeTooltips, progress bars, active tab, “processing” state
successConfirmation / correct / positiveToast success, correct-answer border, completion badge
warningAttention / caution / pre-errorTime-running-low indicator, warning banner, expiring status
dangerError / destructive / negativeError message, form-validation fail, destructive action button
specialPremium / featured / highlightedUpgrade CTA, special-offer card (rare - reserve for true differentiation)
aiAI-generated / AI-initiated contentNarration indicator, AI chat message bg, AI-explanation card

Color mapping (per ../ds-guideline/DIRECTION.md §4 + color.md):

FamilyAnchor hex (GM mode)Underlying palette
brand#D42525red/primary
info / progress#2B52D4blue
success#329546green
warning#E0A011amber/yellow
danger#E24B19orange (NOT red - red is brand!)
special#5B37D2purple/violet
ai#006B99cyan
neutral#2B3138 / #6C7885 / #E2E5E9slate

DOL divergence from typical convention: danger ≠ red in DOL because red = brand. Most UIs use red for error; DOL uses orange. This is intentional.


§3 Level decision - the 6 intensity modifiers

Section titled “§3 Level decision - the 6 intensity modifiers”

Third question: how strong should the treatment be?

LevelUse forVisual character
dimSubtle tint backgrounds, de-emphasized stateLightest shade (200-300 range)
softHover states, gentle emphasisMid-light (400 range)
primaryDefault active state, main emphasisCanonical shade (600)
secondarySubordinate to primary, focus rings, secondary textNear-primary (700 range)
subtleVery muted body / caption / placeholderMid-dark (700-800 for text)
stressMaximum emphasis (rare)Darkest shade (900+ for text)
disabledInactive / non-interactiveLow-contrast grayed variant
highlightFocus indicator, selection emphasisDistinct + saturated
deepStrong divider / dark borderHigher-contrast border variant
inverseText on dark/vivid backgroundWhite / near-white

Not all families have all levels - refer to variable-registry.md for exact availability per family.


§4 Common design task → variable lookup

Section titled “§4 Common design task → variable lookup”

Direct recipes for frequently-built components. Each recipe names the Figma variable you bind.

TaskCategoryFamilyLevelVariable name
Body text on pagetext-colorneutralprimary--text-color-neutral-primary
Muted caption / supporting texttext-colorneutraldim--text-color-neutral-dim
Placeholder text in inputtext-colorneutraldisabled--text-color-neutral-disabled
Heading emphasis (strongest)text-colorneutralstress--text-color-neutral-stress
Brand emphasis (CTA label in colored button)text-coloron-inverseprimary--text-color-on-inverse-primary
Link defaulttext-colorinfoprimary--text-color-info-primary
Error message inlinetext-colordangerprimary--text-color-danger-primary
Success confirmation inlinetext-colorsuccessprimary--text-color-success-primary
TaskFillTextBorderNotes
Primary CTA--fill-brand-primary (#D42525)--text-color-on-inverse-primary (white)none or matching fillDOL brand color
Primary info action (learn more, OK)--fill-info-primary--text-color-on-inverse-primarynoneBlue #2B52D4
Success confirm--fill-success-primary--text-color-on-inverse-primarynoneGreen #329546
Danger destructive--fill-danger-primary--text-color-on-inverse-primarynoneOrange - NOT red
Secondary (outlined)transparent / surface-page-primary--text-color-neutral-primary--border-color-neutral-primaryStroke carries emphasis
Ghost (subtle)transparent--text-color-neutral-secondarynoneHover adds fill-neutral-dim
Disabled--fill-neutral-dim--text-color-neutral-disablednoneLow contrast
StateFillBorderTextIcon
Default--input-color-bg-color-default--border-color-neutral-primary--text-color-neutral-primary--icon-color-neutral-subtle
Focus--input-color-bg-color-default--border-color-progress-secondary (focus-ring global)--text-color-neutral-primary--icon-color-neutral-primary
Error--input-color-bg-color-default--border-color-danger-primary--text-color-neutral-primary--icon-color-danger-primary
Disabled--input-color-bg-color-disable--border-color-neutral-subtle--text-color-neutral-disabled--icon-color-neutral-disabled
Readonlytransparenttransparent--text-color-neutral-primary-

Focus ring rule: ALL components share ONE focus border token - --border-color-progress-secondary. Never per-component colors (anti-pattern figma-naming-6-binding#04).

SeverityFill (bg)BorderIconText
Info--fill-info-dim--border-color-info-secondary--icon-color-info-primary--text-color-info-primary
Success--fill-success-dim--border-color-success-secondary--icon-color-success-primary--text-color-success-primary
Warning--fill-warning-dim--border-color-warning-secondary--icon-color-warning-primary--text-color-warning-primary
Danger / Error--fill-danger-dim--border-color-danger-secondary--icon-color-danger-primary--text-color-danger-primary

Pattern: always fill-{family}-dim + border-{family}-secondary + icon-{family}-primary + text-{family}-primary. Four variables per alert.

TaskFillBorderShadow
Default card on white page--surface-page-neutral-primary (white)--border-color-neutral-primary--shadow-neutral-to-bot-2
Elevated (e.g., modal, dropdown)--surface-page-neutral-primary--border-color-neutral-subtle--shadow-neutral-to-bot-3
Interactive hover--surface-page-neutral-primary--border-color-neutral-deep--shadow-neutral-to-bot-3
Tinted section bg (subtle)--surface-page-neutral-dimnonenone

White-on-white rule: white card on white page MUST have border (--border-color-neutral-primary or subtle). Shadow alone doesn’t suffice. See direction-10-color-brand#09.

StatusFillText
Info / neutral--fill-info-dim--text-color-info-primary
Active / “in progress”--fill-progress-dim (same as info)--text-color-progress-primary
Success / complete--fill-success-dim--text-color-success-primary
Warning / pending--fill-warning-dim--text-color-warning-primary
Error / failed--fill-danger-dim--text-color-danger-primary
Neutral / default--fill-neutral-dim--text-color-neutral-primary
TaskVariable
Default inline icon--icon-color-neutral-subtle
Active / selected icon--icon-color-neutral-primary
Decorative muted icon--icon-color-neutral-dim
Icon inside colored button (white-on-brand)inherit text color (--text-color-on-inverse-primary) via Figma inheritance, OR bind icon-color directly if Figma can’t inherit
Error icon--icon-color-danger-primary
Success checkmark--icon-color-success-primary
AI identity (narration, chat, etc.)--icon-color-ai-primary (cyan)

§4.8 AI-initiated surfaces (narration, chat, inline AI)

Section titled “§4.8 AI-initiated surfaces (narration, chat, inline AI)”
ElementVariable
AI message bubble bg--fill-ai-dim
AI message text--text-color-ai-primary
AI identity icon--icon-color-ai-primary
AI indicator border--border-color-ai-secondary
AI-specific shadow--shadow-ai-to-bot-3 (soft cyan tint - reserved for AI context)

Why AI has its own semantic family: signals “this content is AI-generated” without confusing with brand/info. Cyan (#006B99) is DOL’s AI color per practice.md §5 Skill Color Usage.

PurposeVariable
Between list items--border-color-neutral-subtle
Between form sections--border-color-neutral-primary
Heavy divider (section break)--border-color-neutral-deep
Colored section divider (rare)Match section family: --border-color-{family}-secondary

§4.10 Feature-specific (dashboard, progress, leaderboard)

Section titled “§4.10 Feature-specific (dashboard, progress, leaderboard)”

Feature tokens live in --stat-*, --program-skill-*, --leaderboard-*, --table-color-* namespaces. See variable-registry.md §1.6 and §1.7 for full enumeration.


TaskVariableFont
Body text--font-family-bodyInter
Heading--font-family-headingPlus Jakarta Sans
KID domain (all text)--font-family-kid-*Quicksand
Monospace / code(none - use Figma default or add to DS if needed)-

DOL font-size tokens are role-prefixed, not raw scale. Pick the role first, then the size.

RoleSizes availableUse for
--font-size-display-*2xl/4xl/5xl/6xl/7xl/8xl/9xl (24-64px)Hero display numbers, marketing headline
--font-size-heading-*base/md/lg/xl/2xl/3xl/4xl/5xl/6xl/7xl (14-56px)Page / section / card headings
--font-size-body-*xs/sm/base/md/lg/xl/2xl (10-24px)Paragraphs, card body, form copy
--font-size-label-*xs/sm/base (10-14px)Tiny labels, captions, tags
--font-size-quote-*lg/xl/2xl (18-24px)Pull quotes, testimonial copy

Common picks:

  • Body default = --font-size-body-base (14px) - minimum for body per DIRECTION §6
  • Card title = --font-size-heading-md (16px) or -lg (18px)
  • Section heading = --font-size-heading-2xl (24px) or -3xl (28px)
  • Hero display = --font-size-display-5xl (40px) or larger
  • Caption = --font-size-label-xs (10px) - use sparingly

Line height tokens follow pattern --line-height-<role>-<size>-<variant> where variant = compact | standard | extended.

Reading contextVariantExample
Headings, labelscompact--line-height-heading-2xl-compact
Body paragraph defaultstandard--line-height-body-base-standard
Long-form reading (article, explanation)extended--line-height-body-base-extended

For raw numeric leading (rare), use --leading-scale-{4..72} (in 4px increments).

UseVariableValue
Light (rare, display only)--font-weight-light300
Body default--font-weight-regular400
Medium emphasis--font-weight-medium500
Button labels, strong emphasis--font-weight-bold700
NEVER on buttons--font-weight-black900 - anti landing-1.7-hero#05

Canonical button weight = --font-weight-bold (700). Don’t mix bold + black in same component hierarchy.


ScopeVariable prefix
Inside a component (padding, internal gap)--inset-* or --spacing-*
Between UI elements in same block--spacing-xs/sm/md/lg/xl
Between page sections--section-gap-* (32-80px range)
Page outer margin (responsive)--page-margin-*
Max container width--max-width-* (per responsive viewport)

Rhythm rule (anti-pattern direction-10-spacing-depth#02): vary gap values across atomic levels. Atom gap-2, molecule gap-3, organism gap-4, section gap-8+. Using gap-4 everywhere = slop-04 monotonous-spacing.


DOL radius scale is numeric (--radius-0 through --radius-50, plus half-steps -0-5, -1-5, -2-5, -3-5) with resolved values in 4px increments. Pick component-scoped variant when available.

Element typeVariableApprox value
Sharp corners--radius-00
Tiny (tag, chip, subtle)--radius-14px
Small (icon button)--radius-28px
Default baseline (most inputs)--radius-312px
Medium (card, alert)--radius-416px
Large (modal, panel)--radius-520px
Extra large (hero card)--radius-6 / --radius-724-28px
Pill (full rounded)--radius-max or --pill-shape999
Component-specific preferred--button-radius-* / --input-radius-* / --modal-radius-* / --alert-radius-* / --dropdown-radius-* / --tooltip-radius-* / --uploader-radius-*-

Preference order:

  1. Component-scoped (--button-radius-md) - bound to specific component type
  2. Control sizing (--control-md) - if component doesn’t have its own
  3. Raw radius (--radius-3) - last resort for atypical elements

Why component-scoped beats raw: if DS adjusts “buttons should be slightly rounder”, one change to --button-radius-* updates all buttons; raw --radius-3 would affect every element using that value indiscriminately.


PurposeVariable
Card elevation (default, downward)--shadow-neutral-to-bot-{1..5} (1 = soft, 5 = heavy)
Modal / overlay--shadow-neutral-to-bot-4 or -5
Floating toolbar (upward shadow)--shadow-neutral-to-top-{1..5}
Horizontal elevation--shadow-neutral-to-left-{2,3} or -to-right-{2,3}
AI-specific soft glow--shadow-ai-to-bot-{3..5}
Brand campaign element (rare)--shadow-brand-to-bot-{3..5}
Paper / card-stock aesthetic--shadow-paper-to-bot-{3..5}

NEVER use --shadow-{family}-* for decorative glow on neutral components. Colored shadow = DIRECTION §10 slop-07 (dark-glow). Reserved for campaign / special state only.


Every token selection above targets GM mode. 95% of Figma work.

Switch to KM when working on DOL Kid domain surfaces. Variable names identical - Figma mode switch resolves to KM values internally.

When to switch: user explicitly mentions KID / DOL Kid context, OR file is within KID-scoped design file.

Rule: don’t mix GM + KM tokens in same component (anti-pattern figma-naming-6-binding#06 - covered by Q-gate F6).

Most tokens have darkValue defined - Figma mode auto-resolves. Check variable-registry.md Dark column; if blank, token uses same value across modes.

Rule: don’t hand-bind dark-specific hex; use the semantic token and let mode-swap handle.


“I don’t know which family to use” → usually neutral. Semantic families require explicit semantic meaning. Default to neutral when in doubt.

“I don’t know which level” → usually primary. Levels refine when you need subtle/emphasized variants.

“The exact variable doesn’t seem to exist”:

  1. Search variable-registry.md with grep - try alternate family names
  2. Check if component layer has a composed token (e.g., --button-radius-lg vs raw --control-lg)
  3. If still missing, flag to DS-Token team. DON’T invent (anti-pattern figma-naming-6-binding#05)
  4. Last resort: documented hex fallback - mark as workaround in component description

“Two tokens look interchangeable” → prefer the more specific one:

  • Component-scoped > semantic-scoped > base-raw
  • button-radius-md > radius-md > control-md

  • naming-convention.md - 4-category rule + intensity modifiers + WHY
  • variable-registry.md - all 838 AI-facing tokens categorized with hex values
  • ../anti-pattern-registry.md - figma-naming-6-binding scope (binding anti-patterns)
  • ../ds-guideline/color.md - color philosophy + shade scale math
  • ../ds-guideline/typography.md - typography deep reference
  • ../ai-entry/FIGMA-AI.md §5.1 - F1-F7 quantified gates