홈 / Skills / 웹·UI 디자인 / Design System (토큰과 슬라이드)
웹·UI 디자인

Design System (토큰과 슬라이드)

claudekitMITSkill 언어: 영어
라이선스 확인됨스크립트 포함중간 위험
데모실행 기록 · claude-sonnet-5-5 · 2026년 10월
Using the design-system skill, define a three-layer design token set (primitive, semantic, component) for a SaaS dashboard primary button, including hover and disabled states, and show the CSS variables.
Skill: design-system

Here's a three-layer token set for a SaaS dashboard primary button. It follows the skill's rules: no raw values in components, and the semantic layer is the theme switch.

/* ==========================================================
   LAYER 1 — PRIMITIVE: raw values, no meaning attached
   ========================================================== */
:root {
  /* Blue scale */
  --color-blue-50:  #EFF6FF;
  --color-blue-500: #3B82F6;
  --color-blue-600: #2563EB;
  --color-blue-700: #1D4ED8;
  --color-blue-800: #1E40AF;

  /* Neutral scale */
  --color-gray-50:  #F9FAFB;
  --color-gray-100: #F3F4F6;
  --color-gray-200: #E5E7EB;
  --color-gray-400: #9CA3AF;
  --color-gray-900: #111827;
  --color-white:    #FFFFFF;

  /* Spacing (4px base) */
  --space-2: 0.5rem;
  --space-4: 1rem;

  /* Radius, type, motion */
  --radius-md: 0.375rem;
  --font-size-sm: 0.875rem;
  --font-weight-medium: 500;
  --duration-fast: 150ms;
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

  /* Shadow */
  --shadow-sm: 0 1px 2px rgb(17 24 39 / 0.08);
  --shadow-md: 0 4px 8px rgb(17 24 39 / 0.12);
}

/* ==========================================================
   LAYER 2 — SEMANTIC: purpose-based aliases (theme layer)
   ========================================================== */
:root {
  --color-primary:          var(--color-blue-600);
  --color-primary-hover:    var(--color-blue-700);
  --color-primary-active:   var(--color-blue-800);
  --color-on-primary:       var(--color-white);

  --color-muted:            var(--color-gray-100);
  --color-muted-foreground: var(--color-gray-400);
  --color-muted-border:     var(--color-gray-200);

  --color-focus-ring:       var(--color-blue-500);

  --control-padding-y:      var(--space-2);
  --control-padding-x:      var(--space-4);
  --control-radius:         var(--radius-md);
  --elevation-low:          var(--shadow-sm);
  --elevation-mid:          var(--shadow-md);
  --transition-interactive: var(--duration-fast) var(--ease-standard);
}

/* Dark theme: only semantic values are remapped */
[data-theme="dark"] {
  --color-primary:          var(--color-blue-500);
  --color-primary-hover:    var(--color-blue-600);
  --color-primary-active:   var(--color-blue-700);
  --color-on-primary:       var(--color-gray-900);
  --color-muted:            var(--color-gray-900);
  --color-muted-foreground: var(--color-gray-400);
  --color-muted-border:     var(--color-gray-400);
}

/* ==========================================================
   LAYER 3 — COMPONENT: button-specific, references semantic only
   ========================================================== */
:root {
  /* Default */
  --button-primary-bg:      var(--color-primary);
  --button-primary-fg:      var(--color-on-primary);
  --button-primary-border:  transparent;
  --button-primary-shadow:  var(--elevation-low);

  /* Hover */
  --button-primary-bg-hover:     var(--color-primary-hover);
  --button-primary-fg-hover:     var(--color-on-primary);
  --button-primary-shadow-hover: var(--elevation-mid);

  /* Active (pressed) */
  --button-primary-bg-active:     var(--color-primary-active);
  --button-primary-shadow-active: none;

  /* Disabled */
  --button-primary-bg-disabled:     var(--color-muted);
  --button-primary-fg-disabled:     var(--color-muted-foreground);
  --button-primary-border-disabled: var(--color-muted-border);
  --button-primary-shadow-disabled: none;

  /* Shape & type */
  --button-primary-padding: var(--control-padding-y) var(--control-padding-x);
  --button-primary-radius:  var(--control-radius);
  --button-primary-font-size:   var(--font-size-sm);
  --button-primary-font-weight: var(--font-weight-medium);
  --button-primary-focus-ring:  var(--color-focus-ring);
}

Usage: the component rule consumes only layer-3 tokens.

.btn-primary {
  padding: var(--button-primary-padding);
  font-size: var(--button-primary-font-size);
  font-weight: var(--button-primary-font-weight);
  border: 1px solid var(--button-primary-border);
  border-radius: var(--button-primary-radius);
  background: var(--button-primary-bg);
  color: var(--button-primary-fg);
  box-shadow: var(--button-primary-shadow);
  transition: background var(--transition-interactive),
              box-shadow var(--transition-interactive);
  cursor: pointer;
}
.btn-primary:hover:not(:disabled) {
  background: var(--button-primary-bg-hover);
  color: var(--button-primary-fg-hover);
  box-shadow: var(--button-primary-shadow-hover);
}
.btn-primary:active:not(:disabled) {
  background: var(--button-primary-bg-active);
  box-shadow: var(--button-primary-shadow-active);
}
.btn-primary:focus-visible {
  outline: 2px solid var(--button-primary-focus-ring);
  outline-offset: 2px;
}
.btn-primary:disabled,
.btn-primary[aria-disabled="true"] {
  background: var(--button-primary-bg-disabled);
  color: var(--button-primary-fg-disabled);
  border-color: var(--button-primary-border-disabled);
  box-shadow: var(--button-primary-shadow-disabled);
  cursor: not-allowed;
}

State matrix (matches the skill's spec pattern):

Property Default Hover Active Disabled
Background blue-600 blue-700 blue-800 gray-100
Text white white white gray-400
Border none none none gray-200
Shadow sm md none none

Notes

  • Layer rule: components reference semantic tokens, and semantic tokens reference primitives. Nothing skips a layer.
  • Rebranding: change --color-primary and the button follows, with no component edits.
  • Dark theme: the dark block flips --color-on-primary to dark text because blue-500 with white text is only about 3.7:1 contrast. Verify contrast against your final palette.
  • Disabled state: it has a visible border and muted text, so it doesn't rely on color alone.

I haven't run the skill's generate-tokens.cjs or validate-tokens.cjs scripts, and I haven't written these tokens to a file. If you want, I can put them in the starter JSON format, generate tokens.css, and run the validator over src/.

Real run in an isolated folder with only this skill installed. Only the Skill and Read tools were enabled; the model answered from SKILL.md without opening the reference files, and no bundled script was run.

하는 일

모델이 디자인 토큰을 세우고 일관되게 쓰도록 돕습니다. 3계층 구조(기본값, 시맨틱 별칭, 컴포넌트 토큰), CSS 변수, 간격과 타이포그래피 스케일, 컴포넌트 상태 명세(hover, active, disabled), Tailwind 연동을 다룹니다. 스토리 전략, 레이아웃, 타이포그래피, 색상, 카피 공식 등의 데이터 표를 갖춘 슬라이드 시스템도 있어 HTML 프레젠테이션을 만들 수 있습니다.

동작 방식

  1. SKILL.md가 토큰 구조를 설명하고, 계층별·컴포넌트 명세·상태·Tailwind 참고 문서를 안내합니다.
  2. generate-tokens.cjs는 JSON 토큰 파일을 CSS 변수나 Tailwind 조각으로 바꾸고, validate-tokens.cjs는 코드 속 하드코딩된 값을 찾습니다.
  3. 슬라이드를 만들 때는 search-slides.py가 동봉된 CSV에서 덱 구조와 레이아웃을 검색하고, generate-slide.py가 HTML 슬라이드를 출력하며, 검증 스크립트가 토큰 준수 여부를 확인합니다.

이런 때 좋습니다

디자인 토큰 체계를 처음 세울 때, 컴포넌트 상태를 정리할 때, 디자인 결정을 CSS 변수로 옮길 때, 브랜드에 맞는 피치 덱을 만들 때.

참고 및 위험

Node.js와 Python 스크립트가 들어 있으며 로컬 파일을 읽습니다. 파일을 쓰는 것은 두 가지입니다. `generate-tokens.cjs`는 `-o`로 지정한 경로에 CSS를 쓰고(같은 이름의 파일은 묻지 않고 덮어쓰며 없는 폴더는 만듭니다), `generate-slide.py`는 지정한 경로 또는 스크립트의 기본 출력 폴더에 HTML 슬라이드를 씁니다. 임시 폴더나 버전 관리되는 위치를 지정하세요. 스크립트 자체는 네트워크 요청을 하지 않으며, `fetch-background.py`는 Pexels 이미지 주소와 검색 링크를 만들기만 합니다. 생성된 슬라이드를 브라우저에서 열면 공개 CDN에서 Chart.js와 글꼴을 불러옵니다. 계정이나 키는 필요 없습니다. 참고: SKILL.md에는 이 패키지에 포함되지 않은 프로젝트 파일과 도구(`docs/brand-guidelines.md`, `assets/design-tokens.json`, `/slides:create` 명령, 같은 계층의 `brand` Skill 등)가 언급되므로, 슬라이드 작업은 프로젝트에 해당 파일이 이미 있거나 직접 만들어 둘 때 가장 잘 동작합니다. 이 사이트의 등록본에는 테스트 파일이 빠져 있습니다. `generate-slide.py`에는 항상 `-o`를 지정하세요. 기본 출력 폴더는 Skill 설치 위치에서 스크립트 기준 4단계 위로 계산되므로, 사용자 수준으로 설치한 경우 프로젝트 밖에 쓰이게 됩니다.