본문으로 건너뛰기
cccskills
무료GitHub에 공개

web-styling-design-tokens

Design token architecture - primitive/semantic/component tiers, naming grammar, CSS custom property delivery, and DTCG build pipelines

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(7)

  • SKILL.md19.5 KB
  • examples/core.md13.3 KB
  • examples/pipeline.md10.7 KB
  • examples/scales.md6.8 KB
  • examples/utility-framework-bridge.md6.1 KB
  • metadata.yaml541 B
  • reference.md10.7 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Design Token Patterns

Quick Guide: A token system is a one-way dependency graph — primitives hold raw values, semantic tokens name intent, component tokens hold local exceptions, and consumers only ever touch the top two tiers. Deliver tokens as CSS custom properties: primitives in :root, semantic aliases reassigned per scope, component tokens on the component's own selector. Name a token for its role, never for the mode it happens to appear in (--color-surface-raised, never --color-dark-bg). Store complete colour values rather than bare channel triplets. Where tokens must exist in more than one language, generate every output from one DTCG source.

Detailed Resources:

  • examples/core.md — tier construction, aliasing, naming grammar, custom property delivery, @property registration
  • examples/scales.md — generated spacing and type series, modular scales, fluid type, density multipliers
  • examples/pipeline.md — DTCG source files, build platforms, typed TypeScript export, publishing outside the owning repository
  • examples/utility-framework-bridge.md — projecting tokens into a layer that generates utility classes
  • reference.md — naming grammar table, DTCG $type reference, build config shape, authoring checklist

Which path applies

  • Tokens live only in this repository and are only ever read from CSS — hand-write the custom properties. Follow examples/core.md and examples/scales.md; a build step producing one file nobody else reads is cost with no payoff.
  • Tokens must exist in a second language or platform, or ship outside the repository that owns them — author a DTCG source and generate every output from it. Follow examples/pipeline.md; the tiers and names are identical, the source is JSON instead of CSS.

<critical_requirements>

Before writing design token code

Route every component style through a semantic or component token. A component that reads a primitive or a raw literal has bypassed the indirection layer, so a rebrand has to visit it individually.

Name tokens for their role. --color-surface-raised stays true in every mode; --color-dark-bg is only true in one, and every additional mode forks the name and every consumer.

Store complete colour values. A bare channel triplet (0 0% 100%) is not a colour, so color-mix(), oklch() interpolation and every opacity modifier produce invalid CSS that is dropped rather than reported.

Keep the dependency flow one-way: primitive → semantic → component. A primitive that references a semantic token has inverted the graph and made the palette un-regenerable; a cycle resolves to nothing at runtime.

Generate every additional output from one source once tokens exist in more than one language. A TypeScript constant maintained beside a CSS variable drifts on the first edit made in only one of them, and nothing fails loudly.

</critical_requirements>


Auto-detection: design tokens, primitive tokens, semantic tokens, component tokens, token tiers, token aliasing, DTCG, Design Tokens Community Group, $value, $type, Style Dictionary, transformGroup, buildPath, css/variables, outputReferences, @property, --color-, --space-, modular scale, density tokens

Applies to:

  • Standing up the token layer a design system sits on
  • Deciding how many tiers a token set needs and what belongs in each
  • Naming tokens so they survive a rebrand, a new mode, or a density change
  • Delivering tokens as custom properties with correct scoping
  • Building tokens from a DTCG source into CSS, TypeScript, or other platform outputs
  • Generating spacing and type series rather than hand-listing every step
  • Auditing an existing set for tier leaks, mode-named tokens, or drift between languages

Handled elsewhere:

  • Runtime mode selection — how a theme is chosen, persisted and applied without a flash of the wrong one. This skill defines the token contract those mechanics switch between
  • Writing markup against generated utility classes — this skill covers where tokens are declared, not how a generated class is used
  • Mapping component props to class combinations — a variant API consumes component tokens and defines none
  • A project with a handful of colours and no theming ambition — a short :root block is the right amount of structure, and tiers would be indirection nobody reads

<philosophy>

A token system is not a list of values. It is a one-way dependency graph with a naming contract, and its purpose is to put a layer of indirection between what a value is and what a value means, so the two can change independently.

The three tiers separate three kinds of change:

TierAnswersChanges whenWho reads it
Primitive"What is this value?"The palette or scale is regeneratedSemantic tokens only
Semantic"What is this value for?"The design language shifts, or a modeComponents, and component tokens
Component"What does this part use?"One component needs a local exceptionThat component only
  • Consumers touch semantics, never primitives. background: var(--color-surface-raised) survives a rebrand; var(--color-gray-100) does not.
  • Every tier is indirection paid for on every read. Three is the ceiling, not the target — component tokens are exceptions rather than a mirror of the component tree.
  • The token set is the contract; the delivery format is an implementation detail. Custom properties, a typed TypeScript object and a native platform file are three renderings of one graph, which is exactly why they are generated rather than typed twice.

A value used in one place still deserves a semantic token when the question "will everyone using this want it to change together?" has the answer yes. A token is a statement that a set of usages share a fate.

Two tiers suffice for a single brand, a single mode and no white-labelling: components read semantics directly.

</philosophy>
<patterns>

Core patterns

Pattern 1: Token tiers and the one-way graph

Primitives are the only tier holding literals. Semantic tokens alias primitives and name intent. Component tokens alias semantics and exist only where a component diverges.

:root {
  /* Tier 1 — literal values, no meaning attached */
  --palette-slate-50: oklch(0.98 0.003 250);
  --palette-slate-900: oklch(0.21 0.02 250);

  /* Tier 2 — names intent, aliases a primitive */
  --color-surface-default: var(--palette-slate-50);
  --color-text-default: var(--palette-slate-900);

  /* Tier 3 — a local exception, aliases a semantic */
  --card-surface: var(--color-surface-default);
}

A primitive references nothing, a semantic holds no literal, and a component token never reaches past the semantic tier. Cycles are a hard error in every conforming pipeline; in hand-written CSS they resolve to nothing at runtime. A component token that overrides nothing anywhere is indirection earning nothing — delete it and read the semantic directly.

Full code: examples/core.md


Pattern 2: Naming grammar

A name is a path read left to right, general to specific. Fix the segment order once and every name becomes predictable.

[namespace-]category-concept[-variant][-state][-scale]

--color-text-default            category=color  concept=text   variant=default
--color-text-danger-hover       + variant=danger + state=hover
--space-inline-sm               category=space  concept=inline scale=sm
--acme-color-surface-raised     namespace=acme (only when tokens ship outside the app)

Category first so tokens sort into groups; state always last so -hover and -pressed read consistently; an absent segment means "default". The mode belongs in the scope that assigns the value, never in the identifier:

--color-surface-default: var(--palette-slate-50);
[data-theme="dark"] {
  --color-surface-default: var(--palette-slate-900);
}

Numeric steps (50–950) suit primitive ramps where the number is a real position; t-shirt sizes (xs–xl) suit semantic steps where a number would imply arithmetic that does not hold. One convention per namespace.

Full code: examples/core.md


Pattern 3: CSS custom properties as the delivery format

Custom properties are the delivery format because they participate in the cascade — reassigning a semantic token inside a scope re-themes every descendant with no rebuild and no re-render.

:root {
  --palette-blue-600: oklch(0.55 0.19 258);
  --color-action-default: var(--palette-blue-600); /* semantic default */
}

/* A scope reassigns semantics; primitives never move */
[data-density="compact"] {
  --space-inline-md: var(--space-2);
}

.card {
  --card-padding: var(--space-inline-md); /* component token reads a semantic */
  padding: var(--card-padding);
}

Primitives live in :root and are never reassigned. Semantic tokens are the only tier a scope selector touches. Component tokens are declared on the component's own selector, so they inherit into its subtree and nowhere else.

Register a token with @property when it is animated, or when unintended inheritance would be a bug. syntax and inherits are both required descriptors, and initial-value is required for any syntax other than *:

@property --card-elevation-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

Registration buys typed interpolation — an unregistered custom property cannot be transitioned — plus a fallback to initial-value instead of an inherited garbage value. It costs a declaration per token, so register the animated and the leak-prone ones rather than the whole set.

Full code: examples/core.md


Pattern 4: Projecting tokens into a generated-class layer

Where something generates classes from a theme surface, that surface is a projection of the token set rather than a second place values are typed. Three decisions make the projection safe, and they hold whatever generates the classes:

/* 1. The tokens. Literal values and every mode swap live here. */
:root {
  --app-canvas: oklch(0.98 0.003 250);
}
[data-theme="dark"] {
  --app-canvas: oklch(0.21 0.02 250);
}

/* 2. The theme surface aliases them — one entry per token that should
      become a class. Nothing is re-typed. */
  • Alias, never redeclare. A value written in both places drifts on the first edit that lands in one of them, and the mismatch surfaces as a subtle colour difference no checker catches.
  • Project only what should become a class. Internal plumbing — a focus-ring width, an interaction duration — stays in :root and is read directly, so the generated surface stays to things anyone would write in markup.
  • Replace the generator's default palette rather than extending it. Defaults left in place are an untracked parallel token set, and every default colour class is a value that bypasses the semantic tier.

Full code: examples/utility-framework-bridge.md


Pattern 5: DTCG source and a build pipeline

Source lives in DTCG format ($value / $type / $description, {alias} references), and every output — CSS, typed TypeScript, native files — is generated from it.

{
  "color": {
    "$type": "color",
    "palette": { "slate-50": { "$value": "#f8fafc" } },
    "surface": {
      "default": {
        "$value": "{color.palette.slate-50}",
        "$description": "Page background in the default mode"
      }
    }
  }
}
// one source, many platforms
export const config = {
  source: ["tokens/**/*.json"],
  platforms: {
    css: {
      transformGroup: "css",
      buildPath: "build/css/", // trailing slash is required
      files: [
        {
          destination: "tokens.css",
          format: "css/variables",
          options: { outputReferences: true }, // preserves var() aliasing
        },
      ],
    },
    ts: {
      transformGroup: "js",
      buildPath: "build/ts/",
      files: [{ destination: "tokens.ts", format: "javascript/es6" }],
    },
  },
};

$type hoists to the closest ancestor group that declares it, so declare it once per group. A {group.token} alias always resolves to a whole $value — property-level access needs JSON Pointer (#/color/blue/$value). DTCG ($value) and the original format (value) cannot be combined in one instance. outputReferences: true is what keeps var() aliasing in the CSS output instead of flattening every semantic token to a literal.

Full code: examples/pipeline.md


Pattern 6: Scales as generated series

Spacing and type are series, not sets. Express the generator — a base and a ratio, or a base and a multiplier — so every step is derivable and no step can be individually wrong.

:root {
  --space-base: 0.25rem;
  --space-1: calc(var(--space-base) * 1);
  --space-2: calc(var(--space-base) * 2);
  --space-4: calc(var(--space-base) * 4);
  --space-8: calc(var(--space-base) * 8);
}

Density is a multiplier on the semantic tier, not a second scale. Applied where primitives become semantics, one token re-scales the system and component tokens inherit density for free:

:root {
  --density-scale: 1;
  --space-inline-md: calc(var(--space-4) * var(--density-scale));
}
[data-density="compact"] {
  --density-scale: 0.75;
}

Type scales take the same shape with a ratio in place of the multiplier, and fluid steps use clamp() with both bounds derived from the scale rather than hand-picked. A calc() chain much past three levels becomes hard to debug — resolve those in the pipeline instead.

Full code: examples/scales.md

</patterns>

<decision_framework>

Decision framework

How many tiers?

Does the product need more than one visual mode, brand, or tenant?
|-- NO --> Does any component need a value that diverges from the system default?
|   |-- NO  --> Two tiers: primitive + semantic
|   |-- YES --> Two tiers, plus component tokens for the divergent components only
|-- YES --> Three tiers. Semantic tokens are the swap point; primitives never move.

Which tier does this value belong to?

Is it a literal (a hex, a rem, a ms)?
|-- YES --> Primitive. It gets a position-in-a-ramp name, not a meaning name.
|-- NO  --> Does more than one component share this decision?
    |-- YES --> Semantic. Name the role.
    |-- NO  --> Is the component genuinely diverging, or just the only current user?
        |-- Diverging --> Component token
        |-- Only user --> Semantic. One user today is still a shared decision tomorrow.

Where does this token get declared?

Is it a primitive?
|-- YES --> :root, once, never reassigned
|-- NO  --> Does it change per mode, density, or tenant?
    |-- YES --> :root for the default, reassigned on the scope selector
    |-- NO  --> Is it local to one component?
        |-- YES --> On the component's own selector
        |-- NO  --> :root alongside the other semantics

</decision_framework>


<red_flags>

Red flags

Breaks at runtime:

  • Channel triplets as token values (--color-surface: 0 0% 100%) — not a colour, so color-mix(), oklch() interpolation and every opacity modifier produce invalid CSS that is silently dropped. Store complete colour values.
  • An inverted or cyclic graph — a primitive referencing a semantic, or two tokens aliasing each other. Pipelines error on cycles; hand-written CSS resolves to nothing and the declaration disappears.
  • A @property rule missing syntax or inherits — the whole rule is invalid and ignored, so the property stays unregistered and the transition that depended on it does nothing.
  • initial-value that is not computationally independent (3em) — same outcome: the rule is dropped whole with nothing in the console.
  • Fallbacks in consumers (var(--color-surface, #fff)) — hides a missing token behind a plausible value, so the bug ships looking almost right.

Costs a rebrand:

  • A component reading a primitive (background: var(--palette-slate-100)) — the indirection layer is bypassed, so a palette change has to visit every component instead of one semantic file.
  • Hex or px literals in component CSS — an untracked token. It will not move with the system, will not respond to a mode change, and will not appear in any audit.
  • Mode baked into the name (--color-dark-bg, --text-light-muted) — the token count multiplies by the number of modes and every consumer needs a conditional. Reassign one role-named token per scope.
  • Semantic names that describe appearance (--color-blue-action) — half a semantic token. The role survives a rebrand; the colour word becomes a lie.
  • Parallel hand-maintained copies in TypeScript and CSS — they drift on the first one-sided change and nothing fails.
  • Skipping outputReferences in a generated CSS platform — every semantic token flattens to a literal, so the aliasing the tiers were built for disappears from the output.

Surprising behaviour:

  • Custom properties inherit by default, so a component token declared on a component selector reaches every descendant including slotted children. inherits: false via @property is the fix where that matters.
  • CSS.registerProperty() takes precedence over @property for the same name, so a stray JS registration silently overrides the stylesheet.
  • calc() chains through several tiers resolve at use time, so a unit error three aliases up surfaces as a dropped declaration at the consumer with no indication of which link broke.
  • DTCG {alias} always resolves to a complete $value. Reaching into part of a composite needs JSON Pointer; referencing a group is an error.
  • $type flows from the closest ancestor group that declares it, and a node must never carry both $value and child tokens — that shape is neither a group nor a token.
  • Token names cannot start with $ or contain {, } or . — the period is reserved for alias paths, so a name containing one produces an unresolvable reference.
  • Mixing DTCG ($value) with the original format (value) in one instance makes the unrecognised half read as plain groups, so a half-finished migration looks like tokens that simply vanished.

Sprawl:

  • Token-per-usage (--button-primary-hover-icon-margin-left) — a token per property per element is a stylesheet with extra steps. Tokens encode shared decisions; a value shared with nothing is just a value.
  • A component tier mirroring the component tree — component tokens are exceptions, not coverage.
  • Mixed scale conventions in one namespace (--space-sm beside --space-400) — every lookup becomes a guess about which convention applies.
  • Density as a second spacing scale rather than a multiplier applied where primitives become semantics — the token count multiplies by the number of density levels and every component gains a branch.

</red_flags>

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

agents-inc/skills252026년 10월 17일 업데이트

agents-inc의 스킬 모두 보기

이 스킬의 문제 신고