# Featuring Design System ## Overview Featuring Design System은 [피처링](https://featuring.co)의 모든 프로덕트에서 일관된 사용자 경험을 제공하기 위한 React 디자인 시스템입니다. 1,000개 이상의 디자인 토큰, 30개 이상의 React 컴포넌트, 350개 이상의 아이콘을 제공하며, Zero-runtime CSS와 타입 안전성을 기반으로 설계되었습니다. ## Packages | 패키지 | 설명 | 버전 | | ------------------------------------------------------ | ---------------------------------------- | -------------- | | [`@featuring-corp/design-tokens`](/docs/design-tokens) | 색상, 간격, 타이포그래피, 반지름, 그림자 토큰 | CSS 변수 + JS 객체 | | [`@featuring-corp/components`](/docs/components) | Layout primitives + UI 컴포넌트 + CSS Preset | React 18+ | | [`@featuring-corp/icons`](/docs/icons) | 333개 시스템 아이콘 + 26개 서비스 아이콘 | SVG React 컴포넌트 | ## Quick Start ### 1. 설치 npm pnpm yarn bun ```bash npm install @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash pnpm add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash yarn add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash bun add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ### 2. CSS Import ```tsx // Preset (권장) — reset + normalize + 테마 토큰 + CSS Layer 순서 포함 import '@featuring-corp/components/preset/featuring'; ``` ### 3. 컴포넌트 사용 ```tsx import { Box, HStack, Typo, Button } from '@featuring-corp/components'; import { IconSearchOutline } from '@featuring-corp/icons'; function SearchSection() { return ( }> Search Search ); } ``` ## 핵심 특징 ### $css Prop `Box`, `Flex`, `HStack`, `VStack`, `Center`, `Grid`, `Typo` 컴포넌트에서 사용하는 토큰 기반 스타일링 API입니다. 반응형 레이아웃과 인터랙티브 색상을 하나의 prop에서 선언합니다. ```tsx Responsive & Interactive ``` [$css Prop 자세히 보기](/docs/guide/css-prop) ### 3-Layer Token Architecture | 레이어 | 역할 | 예시 | | ------------- | ----------- | ---------------------------------------------------------- | | **Global** | 원시 값 | `--global-colors-red-50`, `--global-spacing-400` | | **Semantic** | 용도 매핑 | `--semantic-color-text-1`, `--semantic-color-background-1` | | **Color Set** | 브랜드 Primary | `--global-colors-primary-60` | [Design Philosophy 자세히 보기](/docs/guide/design-philosophy) ### Polymorphic Rendering `render` prop으로 시맨틱 HTML을 유지하면서 디자인 시스템의 스타일링을 활용합니다. ```tsx } $css={{ padding: '$spacing-400' }}> } variant="$heading-1">Title } variant="$body-1">Description ``` ### Multi-Brand Theming Preset 교체만으로 Featuring(보라색) ↔ DataEffect(파란색) 테마를 전환합니다. ```tsx // Featuring 테마 import '@featuring-corp/components/preset/featuring'; // DataEffect 테마 import '@featuring-corp/components/preset/dataEffect'; ``` [Theming 자세히 보기](/docs/guide/theming) ## 문서 구조 | 섹션 | 내용 | | ------------------------------------ | --------------------------------------- | | [Guide](/docs/guide) | 설치, 프레임워크 설정, 설계 원칙, $css prop, 반응형, 테마 | | [Design Tokens](/docs/design-tokens) | 색상, 간격, 타이포그래피, 반지름, 그림자 | | [Components](/docs/components) | Layout, UI 컴포넌트, Legacy (Core\*), Utils | | [Icons](/docs/icons) | 시스템 아이콘, 서비스 아이콘 | # Components ## @featuring-corp/components Layout Primitives, UI 컴포넌트, CSS Preset을 제공하는 React 컴포넌트 패키지입니다. ### 설치 npm pnpm yarn bun ```bash npm install @featuring-corp/components ``` ```bash pnpm add @featuring-corp/components ``` ```bash yarn add @featuring-corp/components ``` ```bash bun add @featuring-corp/components ``` ### CSS Preset ```tsx // 앱 진입점 — Reset + Normalize + 브랜드 토큰 + CSS Layer 순서 import '@featuring-corp/components/preset/featuring'; ``` ### 컴포넌트 분류 | 분류 | 설명 | | ------------------------------------------------ | ----------------------------------------------------------------------------------- | | [Layout](/docs/components/layout/box) | Box, Flex, HStack, VStack, Center, Grid, Typo — `$css` prop + `render` prop 기반 | | [Components](/docs/components/components/button) | Button, Tag, Link, Toggle, Checkbox, Radio 등 — Context 기반 상태 공유, `$css`/`render` 지원 | | [Legacy](/docs/components/legacy/core-button) | Core\* 컴포넌트 — `forwardRef` + `clsx` + recipe 기반 (마이그레이션 대상) | | [Utils](/docs/components/utils/use-render) | useRender, mergeProps — Base UI 유틸리티 | ### 마이그레이션 현황 기존 Core\* 패턴에서 `$css` + `render` + Context 기반 패턴으로 순차적으로 마이그레이션하고 있습니다. | 컴포넌트 | Legacy | 신규 |   | | ---------------- | ---------------------- | ---------------------------------------------------------------------------------------- | :-: | | Button | `CoreButton` | `Button.Root` + `Button.Icon` + `Button.Text` | ✅ | | IconButton | — | `IconButton` | ✅ | | Tag | `CoreTag` | `Tag.Root` + `Tag.Icon` + `Tag.Text` + `Tag.RemoveButton` | ✅ | | StatusBadge | `CoreStatusBadge` | `StatusBadge.Root` + `StatusBadge.Icon` + `StatusBadge.Text` | ✅ | | IconBadge | `CoreIconBadge` | `IconBadge` | ✅ | | Dot | `CoreDot` | `Dot` | ✅ | | Loader | `CoreLoader` | `Loader` | ✅ | | Link | `CoreLink` | `Link.Root` + `Link.Icon` + `Link.Text` | ✅ | | Switch | `CoreToggle` | `Switch.Root` + `Switch.Thumb` | ✅ | | Checkbox | `CoreCheckbox` | `Checkbox.Root` + `Checkbox.Input` + `Checkbox.Label` | ✅ | | Radio | `CoreRadio` | `Radio.Group` + `Radio.Root` + `Radio.Input` + `Radio.Label` | ✅ | | SegmentedControl | `CoreSegmentedControl` | `SegmentedControl.Group` + `SegmentedControl.Item` | ✅ | | TextInput | `CoreTextInput` | `TextInput.Root` + `TextInput.Input` + `TextInput.Icon` + `TextInput.Text` | ✅ | | TextArea | `CoreTextArea` | `TextArea` | ✅ | | Select | `CoreSelect` | `Select.Root` + `Select.Trigger` + `Select.Popup` + `Select.Item` + … | ✅ | | Tabs | `CoreTabs` | `Tabs.Root` + `Tabs.List` + `Tabs.Tab` + `Tabs.Panel` | ✅ | | Tooltip | `CoreTooltip` | `Tooltip.Root` + `Tooltip.Trigger` + `Tooltip.Content` + `Tooltip.Arrow` | ✅ | | Field | — | `Field.Root` + `Field.Label` + `Field.Description` + `Field.Message` + `Field.Count` | ✅ | | Avatar | `CoreAvatar` | `Avatar.Root` + `Avatar.Image` + `Avatar.Fallback` + `Avatar.Indicator` + `Avatar.Group` | ✅ | | SectionMessage | `CoreSectionMessage` | `SectionMessage.Root` + `SectionMessage.Icon` + `SectionMessage.Content` + … | ✅ | | Modal | `CoreModal` | — | 🔜 | | Toast | `CoreToast` | — | 🔜 | | Dropdown | `CoreDropdown` | — | 🔜 | | Pagination | `CorePagination` | — | 🔜 | | SideNavigation | `CoreSideNavigation` | — | 🔜 | | Table | `CoreTable` | — | 🔜 | | Calendar | `CoreCalendar` | — | 🔜 | ### 패턴 비교 #### 신규 패턴 (Button, Tag) ```tsx import { Button } from '@featuring-corp/components'; Search ``` * `$css` prop — 토큰 기반 atomic CSS * `render` prop — 시맨틱 HTML 변경 (``, `` 등) * Context — 부모→자식 상태 공유 (size, loading, disabled) * `className`/`style` — state callback 지원 #### Legacy 패턴 (Core\*) ```tsx import { CoreButton } from '@featuring-corp/components'; Search ``` * `forwardRef` + `clsx` * Recipe 기반 variants * Legacy sprinkles 시스템 # Base Styles 디자인 시스템을 사용하기 전에 브라우저 기본 스타일을 초기화하고 일관된 기본 스타일을 적용해야 합니다. ## 사용 방법 ### Preset 사용 (권장) Preset 하나로 Reset, Normalize, 브랜드 토큰, CSS Layer 순서가 모두 설정됩니다. ```tsx // _app.tsx, main.tsx, 또는 layout.tsx import '@featuring-corp/components/preset/featuring'; ``` Preset은 `@layer ft.reset, ft.normalize, ft.components` 순서를 선언하며, reset.css와 normalize.css가 각각 `ft.reset`, `ft.normalize` 레이어에 포함됩니다. ### 개별 Import (고급) Tailwind 등 자체 reset을 사용하는 프로젝트에서는 개별 import도 가능합니다. ```tsx // _app.tsx, main.tsx, 또는 layout.tsx import '@featuring-corp/design-tokens/style/reset.css'; import '@featuring-corp/design-tokens/style/normalize.css'; import '@featuring-corp/design-tokens/style/featuring.css'; // 테마 CSS ``` ## 요약 ### reset.css | 적용 대상 | 스타일 | | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | 모든 HTML 요소 | `margin: 0`, `padding: 0`, `border: 0` | | 모든 HTML 요소 | `font-size: 100%`, `font: inherit`, `vertical-align: baseline` | | `article`, `aside`, `details`, `figcaption`, `figure`, `footer`, `header`, `hgroup`, `menu`, `nav`, `section` | `display: block` | | `body` | `line-height: 1` | | `ol`, `ul` | `list-style: none` | | `blockquote`, `q` | `quotes: none` | | `blockquote:before`, `blockquote:after`, `q:before`, `q:after` | `content: ''`, `content: none` | | `table` | `border-collapse: collapse`, `border-spacing: 0` | ### normalize.css | 적용 대상 | 스타일 | | --------------------------------------------------- | --------------------------------------------------------------------------- | | `html` | `text-size-adjust: none` | | `body` | `margin: 0`, `color: var(--semantic-color-text-1)` | | `*`, `*::before`, `*::after`, `span` | `box-sizing: border-box` | | `a`, `button`, `input`, `select` | `cursor: pointer` | | `button`, `input`, `optgroup`, `select`, `textarea` | `font-family: inherit`, `font-size: 100%`, `line-height: 1.15`, `margin: 0` | | `img` | `border-style: none`, `max-inline-size: 100%`, `max-block-size: 100%` | | `[disabled]` | `pointer-events: none`, `cursor: not-allowed` | | `*:focus:not(:focus-visible)` | `outline: none` | | `ol`, `li`, `ul`, `menu`, `summary` | `list-style: none` | | `table` | `border-collapse: collapse` | | `::placeholder` | `color: unset` | *** ## reset.css Eric Meyer's CSS Reset v2.0 기반으로 브라우저의 모든 기본 스타일을 초기화합니다. `@featuring-corp/design-tokens/style/reset.css` ```css @layer reset; @layer reset { /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 License: none (public domain) */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } table { border-collapse: collapse; border-spacing: 0; } } ``` *** ## normalize.css 브라우저 간 일관된 스타일을 적용하고 기본적인 UX를 개선합니다. `@featuring-corp/design-tokens/style/normalize.css` ```css @layer normalize; @layer normalize { html { -moz-text-size-adjust: none; -webkit-text-size-adjust: none; text-size-adjust: none; } body { margin: 0; color: var(--semantic-color-text-1); } main { display: block; } hr { box-sizing: content-box; height: 0; overflow: visible; } pre { font-family: monospace, monospace; font-size: 1em; } a { background-color: transparent; } abbr[title] { border-bottom: none; text-decoration: underline; } b, strong { font-weight: bolder; } code, kbd, samp { font-family: monospace, monospace; font-size: 1em; } small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } img { border-style: none; max-inline-size: 100%; max-block-size: 100%; } a, button, input, select { cursor: pointer; } button, input, optgroup, select, textarea { font-family: inherit; font-size: 100%; line-height: 1.15; margin: 0; } button, input { overflow: visible; } button, select { text-transform: none; } button, [type='button'], [type='reset'], [type='submit'] { -webkit-appearance: button; appearance: button; } button::-moz-focus-inner, [type='button']::-moz-focus-inner, [type='reset']::-moz-focus-inner, [type='submit']::-moz-focus-inner { border-style: none; padding: 0; } button:-moz-focusring, [type='button']:-moz-focusring, [type='reset']:-moz-focusring, [type='submit']:-moz-focusring { outline: 1px dotted ButtonText; } fieldset { padding: 0.35em 0.75em 0.625em; } legend { box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal; } progress { vertical-align: baseline; } textarea { overflow: auto; white-space: revert; } input, textarea { -webkit-user-select: auto; } [type='checkbox'], [type='radio'] { box-sizing: border-box; padding: 0; } [type='number']::-webkit-inner-spin-button, [type='number']::-webkit-outer-spin-button { height: auto; } [type='search'] { -webkit-appearance: textfield; appearance: textfield; outline-offset: -2px; } [type='search']::-webkit-search-decoration { -webkit-appearance: none; } ::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; } details { display: block; } summary { display: list-item; } template { display: none; } [hidden] { display: none; } [disabled] { pointer-events: none; cursor: not-allowed; } *:focus:not(:focus-visible) { outline: none; } ol, li, ul, menu, summary { list-style: none; } table { border-collapse: collapse; } meter { -webkit-appearance: revert; appearance: revert; } ::placeholder { color: unset; } *:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) { all: unset; display: revert; } :where([contenteditable]:not([contenteditable='false'])) { -moz-user-modify: read-write; -webkit-user-modify: read-write; overflow-wrap: break-word; -webkit-line-break: after-white-space; -webkit-user-select: auto; } :where([draggable='true']) { -webkit-user-drag: element; } :where(dialog:modal) { all: revert; box-sizing: border-box; } :where(pre) { all: revert; box-sizing: border-box; } :where([hidden]) { display: none; } *, *::before, *::after, span { box-sizing: border-box; } .os-scrollbar { --os-size: 14px !important; --os-handle-bg: rgb(66 66 66 / 25%) !important; --os-handle-bg-hover: var(--global-colors-gray-60) !important; --os-padding-perpendicular: 3px !important; } } ``` # Colors 우리의 컬러 시스템은 유연합니다. 그러나 고유한 가치를 잃지 않습니다. 링블루의 액센트를 더한 대담한 팔레트는 어디에서 사용되건 본래의 가치를 돋보이게 합니다. 상태와 피드백 등의 색상의 명확한 기준을 준수하고, 시각적 연속성을 제공해 브랜드의 가치를 효과적으로 전달할 수 있도록 합니다. ## Usage ```tsx import { global, semantic, colorSet } from '@featuring-corp/design-tokens'; // Global Colors (primary 제외) console.log(global.colors.gray[90]); // #242424 console.log(global.colors.secondary[60]); // #bbbbbb console.log(global.colors.red[50]); // #e97259 // Color Set (Primary Colors - 테마별로 다름) console.log(colorSet.featuringPrimary[60]); // #5e51ff (Featuring 테마) console.log(colorSet.dataEffectPrimary[60]); // #0065ff (DataEffect 테마) // Semantic Colors console.log(semantic.color.background[1]); // var(--global-colors-white) console.log(semantic.color.text[1]); // var(--global-colors-gray-90) ``` ## Global Colors CSS 변수: `--global-colors-{color}-{shade}` ### Featuring Primary Featuring 브랜드의 메인 컬러입니다.
Code Value
primary-10
\#ecefff
primary-20
\#dce2ff
primary-30
\#c0c8ff
primary-40
\#9aa3ff
primary-50
\#7273ff
primary-60
\#5e51ff
primary-70
\#5032f9
primary-80
\#3821b2
primary-90
\#31238c
primary-100
\#1f1551
### DataEffect Primary DataEffect 브랜드의 메인 컬러입니다.
Code Value
primary-10
\#edf5ff
primary-20
\#d0e2ff
primary-30
\#a6c8ff
primary-40
\#4c9aff
primary-50
\#2684ff
primary-60
\#0065ff
primary-70
\#0052cc
primary-80
\#0747a6
primary-90
\#001d6c
primary-100
\#001141
### Secondary
Code Value
secondary-10
\#fafafa
secondary-20
\#f6f6f6
secondary-30
\#ebebeb
secondary-40
\#e0e0e0
secondary-50
\#d2d2d2
secondary-60
\#bbbbbb
secondary-70
\#959595
secondary-80
\#707070
secondary-90
\#424242
secondary-100
\#242424
### Gray (Neutral)
Code Value
white
\#ffffff
black
\#000000
gray-5
\#fafafa
gray-10
\#f6f6f6
gray-15
\#f0f0f0
gray-20
\#ebebeb
gray-30
\#e0e0e0
gray-40
\#d2d2d2
gray-50
\#bbbbbb
gray-60
\#959595
gray-70
\#707070
gray-80
\#424242
gray-90
\#242424
### Color Palette
Code Value
red-10
\#fcedea
red-20
\#f6c8bf
red-30
\#f2ae9f
red-40
\#ec8974
red-50
\#e97259
red-60
\#e34f2f
red-70
\#cf482b
red-80
\#a13821
red-90
\#7d2b1A
red-100
\#5f2114
orange-10
\#fcf2e6
orange-20
\#f5d5b0
orange-30
\#f0c18a
orange-40
\#eaa554
orange-50
\#e59333
orange-60
\#df7800
orange-70
\#cb6d00
orange-80
\#9e5500
orange-90
\#7b4200
orange-100
\#5e3200
yellow-10
\#fffbe6
yellow-20
\#fff1b3
yellow-30
\#ffea8e
yellow-40
\#ffe15a
yellow-50
\#ffdb3a
yellow-60
\#ffd209
yellow-70
\#e8bf08
yellow-80
\#b59506
yellow-90
\#8c7405
yellow-100
\#6b5804
lightGreen-10
\#f5faf0
lightGreen-20
\#dfefd0
lightGreen-30
\#d0e8b9
lightGreen-40
\#bbdd98
lightGreen-50
\#add685
lightGreen-60
\#99cc66
lightGreen-70
\#8bba5d
lightGreen-80
\#6d9148
lightGreen-90
\#547038
lightGreen-100
\#40562b
green-10
\#ebf6f1
green-20
\#c2e4d4
green-30
\#a5d7bf
green-40
\#7cc5a2
green-50
\#62ba90
green-60
\#3ba974
green-70
\#369a6a
green-80
\#2a7852
green-90
\#205d40
green-100
\#194731
cyan-10
\#f2fcfb
cyan-20
\#d8f6f3
cyan-30
\#c5f1ee
cyan-40
\#aaebe6
cyan-50
\#99e7e1
cyan-60
\#80e1d9
cyan-70
\#74cdc5
cyan-80
\#5ba09a
cyan-90
\#467c77
cyan-100
\#365f5b
teal-10
\#eff8f7
teal-20
\#cce9e6
teal-30
\#b4deda
teal-40
\#91cfc9
teal-50
\#7cc5bf
teal-60
\#5bb7af
teal-70
\#53a79f
teal-80
\#41827c
teal-90
\#326560
teal-100
\#264d4a
lightBlue-10
\#f1f9fe
lightBlue-20
\#d3ebfc
lightBlue-30
\#bee1fa
lightBlue-40
\#a1d3f8
lightBlue-50
\#8ecbf6
lightBlue-60
\#72bef4
lightBlue-70
\#68adde
lightBlue-80
\#5187ad
lightBlue-90
\#3f6986
lightBlue-100
\#305066
blue-10
\#eef4ff
blue-20
\#d8e6fe
blue-30
\#aacbff
blue-40
\#78b0fd
blue-50
\#4d93f7
blue-60
\#246ee1
blue-70
\#0b54ab
blue-80
\#014186
blue-90
\#032d60
blue-100
\#001639
indigo-10
\#f1f1ff
indigo-20
\#d5d4fd
indigo-30
\#c0bffd
indigo-40
\#a4a2fc
indigo-50
\#9290fb
indigo-60
\#7774fa
indigo-70
\#6c6ae4
indigo-80
\#5452b2
indigo-90
\#41408a
indigo-100
\#323169
purple-10
\#f2eeff
purple-20
\#d5cbff
purple-30
\#c1b1ff
purple-40
\#a58eff
purple-50
\#9378ff
purple-60
\#7856ff
purple-70
\#6d4ee8
purple-80
\#553db5
purple-90
\#422f8c
purple-100
\#32246b
magenta-10
\#faf2fc
magenta-20
\#efd8f4
magenta-30
\#e7c5ef
magenta-40
\#dbaae8
magenta-50
\#d599e3
magenta-60
\#ca80dc
magenta-70
\#b874c8
magenta-80
\#8f5b9c
magenta-90
\#6f4679
magenta-100
\#55365c
burgundy-10
\#f7eef1
burgundy-20
\#e7ccd2
burgundy-30
\#dcb3bc
burgundy-40
\#cb909e
burgundy-50
\#c17a8b
burgundy-60
\#b2596e
burgundy-70
\#a25164
burgundy-80
\#7e3f4e
burgundy-90
\#62313d
burgundy-100
\#4b252e
## Semantic Colors CSS 변수: `--semantic-color-{token}` 시맨틱 컬러는 용도에 따라 미리 정의된 색상입니다. 글로벌 컬러를 CSS 변수로 참조합니다. ### Background
Code Value
background-1 var(--global-colors-white)
background-2 var(--global-colors-gray-5)
background-3 var(--global-colors-gray-10)
background-4 var(--global-colors-gray-20)
background-5 var(--global-colors-gray-90)
### Border
Code Value
border-default var(--global-colors-gray-15)
border-1 var(--global-colors-gray-30)
border-2 var(--global-colors-gray-40)
border-3 var(--global-colors-gray-50)
border-4 var(--global-colors-gray-60)
### Text
Code Value
text-1 var(--global-colors-gray-90)
text-2 var(--global-colors-gray-80)
text-3 var(--global-colors-gray-70)
text-4 var(--global-colors-gray-60)
text-5 var(--global-colors-gray-50)
text-6 var(--global-colors-white)
### Support Error
Code Value
support-error-1 var(--global-colors-red-10)
support-error-2 var(--global-colors-red-20)
support-error-3 var(--global-colors-red-50)
support-error-4 var(--global-colors-red-100)
### Support Warning
Code Value
support-warning-1 var(--global-colors-orange-10)
support-warning-2 var(--global-colors-orange-20)
support-warning-3 var(--global-colors-orange-50)
support-warning-4 var(--global-colors-orange-100)
### Support Success
Code Value
support-success-1 var(--global-colors-green-10)
support-success-2 var(--global-colors-green-20)
support-success-3 var(--global-colors-green-50)
support-success-4 var(--global-colors-green-100)
### Support Info
Code Value
support-info-1 var(--global-colors-primary-10)
support-info-2 var(--global-colors-primary-20)
support-info-3 var(--global-colors-primary-60)
support-info-4 var(--global-colors-primary-100)
### Focus
Code Value
focus rgb(from var(--global-colors-primary-50) r g b / 20%)
### Icon
Code Value
icon-primary var(--global-colors-gray-90)
icon-secondary var(--global-colors-gray-70)
icon-tertiary var(--global-colors-gray-50)
icon-interactive var(--global-colors-primary-60)
icon-on-color-default var(--global-colors-white)
icon-on-color-disabled var(--global-colors-gray-60)
### Toggle
Code Value
toggle-primary-1 var(--global-colors-primary-60)
toggle-primary-2 var(--global-colors-primary-70)
toggle-primary-3 var(--global-colors-primary-100)
toggle-disabled-bg var(--global-colors-gray-20)
toggle-disabled-border var(--global-colors-gray-40)
toggle-disabled-text var(--global-colors-gray-60)
toggle-off-bg var(--global-colors-gray-50)
toggle-off-hover var(--global-colors-gray-60)
## CSS 파일로 사용하기 디자인 토큰을 CSS 변수로 사용하려면 Preset을 import하세요. Reset, Normalize, 브랜드 토큰, CSS Layer 순서가 한 줄로 설정됩니다. ### Featuring 테마 ```tsx // 앱 진입점 (예: _app.tsx, main.tsx, layout.tsx) import '@featuring-corp/components/preset/featuring'; ``` ### DataEffect 테마 ```tsx // 앱 진입점 (예: _app.tsx, main.tsx, layout.tsx) import '@featuring-corp/components/preset/dataEffect'; ``` 그러면 `:root`에 모든 CSS 변수가 등록되어 어디서든 사용 가능합니다: ```css .my-button { background-color: var(--global-colors-primary-60); color: var(--semantic-color-text-6); border-radius: var(--global-radius-100); padding: var(--global-spacing-200) var(--global-spacing-400); } ``` ## 전체 CSS 토큰 목록 LLM이 토큰을 검색할 수 있도록 전체 CSS 파일 내용을 제공합니다. ### Featuring 테마 (`featuring.css`) `@featuring-corp/design-tokens/style/featuring.css` ```css :root { --global-colors-primary-10: #ecefff; --global-colors-primary-20: #dce2ff; --global-colors-primary-30: #c0c8ff; --global-colors-primary-40: #9aa3ff; --global-colors-primary-50: #7273ff; --global-colors-primary-60: #5e51ff; --global-colors-primary-70: #5032f9; --global-colors-primary-80: #3821b2; --global-colors-primary-90: #31238c; --global-colors-primary-100: #1f1551; --global-colors-secondary-10: #fafafa; --global-colors-secondary-20: #f6f6f6; --global-colors-secondary-30: #ebebeb; --global-colors-secondary-40: #e0e0e0; --global-colors-secondary-50: #d2d2d2; --global-colors-secondary-60: #bbbbbb; --global-colors-secondary-70: #959595; --global-colors-secondary-80: #707070; --global-colors-secondary-90: #424242; --global-colors-secondary-100: #242424; --global-colors-red-10: #fcedea; --global-colors-red-20: #f6c8bf; --global-colors-red-30: #f2ae9f; --global-colors-red-40: #ec8974; --global-colors-red-50: #e97259; --global-colors-red-60: #e34f2f; --global-colors-red-70: #cf482b; --global-colors-red-80: #a13821; --global-colors-red-90: #7d2b1A; --global-colors-red-100: #5f2114; --global-colors-orange-10: #fcf2e6; --global-colors-orange-20: #f5d5b0; --global-colors-orange-30: #f0c18a; --global-colors-orange-40: #eaa554; --global-colors-orange-50: #e59333; --global-colors-orange-60: #df7800; --global-colors-orange-70: #cb6d00; --global-colors-orange-80: #9e5500; --global-colors-orange-90: #7b4200; --global-colors-orange-100: #5e3200; --global-colors-yellow-10: #fffbe6; --global-colors-yellow-20: #fff1b3; --global-colors-yellow-30: #ffea8e; --global-colors-yellow-40: #ffe15a; --global-colors-yellow-50: #ffdb3a; --global-colors-yellow-60: #ffd209; --global-colors-yellow-70: #e8bf08; --global-colors-yellow-80: #b59506; --global-colors-yellow-90: #8c7405; --global-colors-yellow-100: #6b5804; --global-colors-lightGreen-10: #f5faf0; --global-colors-lightGreen-20: #dfefd0; --global-colors-lightGreen-30: #d0e8b9; --global-colors-lightGreen-40: #bbdd98; --global-colors-lightGreen-50: #add685; --global-colors-lightGreen-60: #99cc66; --global-colors-lightGreen-70: #8bba5d; --global-colors-lightGreen-80: #6d9148; --global-colors-lightGreen-90: #547038; --global-colors-lightGreen-100: #40562b; --global-colors-green-10: #ebf6f1; --global-colors-green-20: #c2e4d4; --global-colors-green-30: #a5d7bf; --global-colors-green-40: #7cc5a2; --global-colors-green-50: #62ba90; --global-colors-green-60: #3ba974; --global-colors-green-70: #369a6a; --global-colors-green-80: #2a7852; --global-colors-green-90: #205d40; --global-colors-green-100: #194731; --global-colors-cyan-10: #f2fcfb; --global-colors-cyan-20: #d8f6f3; --global-colors-cyan-30: #c5f1ee; --global-colors-cyan-40: #aaebe6; --global-colors-cyan-50: #99e7e1; --global-colors-cyan-60: #80e1d9; --global-colors-cyan-70: #74cdc5; --global-colors-cyan-80: #5ba09a; --global-colors-cyan-90: #467c77; --global-colors-cyan-100: #365f5b; --global-colors-teal-10: #eff8f7; --global-colors-teal-20: #cce9e6; --global-colors-teal-30: #b4deda; --global-colors-teal-40: #91cfc9; --global-colors-teal-50: #7cc5bf; --global-colors-teal-60: #5bb7af; --global-colors-teal-70: #53a79f; --global-colors-teal-80: #41827c; --global-colors-teal-90: #326560; --global-colors-teal-100: #264d4a; --global-colors-lightBlue-10: #f1f9fe; --global-colors-lightBlue-20: #d3ebfc; --global-colors-lightBlue-30: #bee1fa; --global-colors-lightBlue-40: #a1d3f8; --global-colors-lightBlue-50: #8ecbf6; --global-colors-lightBlue-60: #72bef4; --global-colors-lightBlue-70: #68adde; --global-colors-lightBlue-80: #5187ad; --global-colors-lightBlue-90: #3f6986; --global-colors-lightBlue-100: #305066; --global-colors-blue-10: #eef4ff; --global-colors-blue-20: #d8e6fe; --global-colors-blue-30: #aacbff; --global-colors-blue-40: #78b0fd; --global-colors-blue-50: #4d93f7; --global-colors-blue-60: #246ee1; --global-colors-blue-70: #0b54ab; --global-colors-blue-80: #014186; --global-colors-blue-90: #032d60; --global-colors-blue-100: #001639; --global-colors-indigo-10: #f1f1ff; --global-colors-indigo-20: #d5d4fd; --global-colors-indigo-30: #c0bffd; --global-colors-indigo-40: #a4a2fc; --global-colors-indigo-50: #9290fb; --global-colors-indigo-60: #7774fa; --global-colors-indigo-70: #6c6ae4; --global-colors-indigo-80: #5452b2; --global-colors-indigo-90: #41408a; --global-colors-indigo-100: #323169; --global-colors-purple-10: #f2eeff; --global-colors-purple-20: #d5cbff; --global-colors-purple-30: #c1b1ff; --global-colors-purple-40: #a58eff; --global-colors-purple-50: #9378ff; --global-colors-purple-60: #7856ff; --global-colors-purple-70: #6d4ee8; --global-colors-purple-80: #553db5; --global-colors-purple-90: #422f8c; --global-colors-purple-100: #32246b; --global-colors-magenta-10: #faf2fc; --global-colors-magenta-20: #efd8f4; --global-colors-magenta-30: #e7c5ef; --global-colors-magenta-40: #dbaae8; --global-colors-magenta-50: #d599e3; --global-colors-magenta-60: #ca80dc; --global-colors-magenta-70: #b874c8; --global-colors-magenta-80: #8f5b9c; --global-colors-magenta-90: #6f4679; --global-colors-magenta-100: #55365c; --global-colors-burgundy-10: #f7eef1; --global-colors-burgundy-20: #e7ccd2; --global-colors-burgundy-30: #dcb3bc; --global-colors-burgundy-40: #cb909e; --global-colors-burgundy-50: #c17a8b; --global-colors-burgundy-60: #b2596e; --global-colors-burgundy-70: #a25164; --global-colors-burgundy-80: #7e3f4e; --global-colors-burgundy-90: #62313d; --global-colors-burgundy-100: #4b252e; --global-colors-white: #ffffff; --global-colors-black: #000000; --global-colors-gray-5: #fafafa; --global-colors-gray-10: #f6f6f6; --global-colors-gray-15: #f0f0f0; --global-colors-gray-20: #ebebeb; --global-colors-gray-30: #e0e0e0; --global-colors-gray-40: #d2d2d2; --global-colors-gray-50: #bbbbbb; --global-colors-gray-60: #959595; --global-colors-gray-70: #707070; --global-colors-gray-80: #424242; --global-colors-gray-90: #242424; --global-elevation-2: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 1px 2px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-4: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 2px 4px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-8: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 4px 8px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-16: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 8px 16px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-28: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 14px 28px 0px rgb(from var(--global-colors-black) r g b / 24%); --global-elevation-64: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 32px 64px 0px rgb(from var(--global-colors-black) r g b / 24%); --global-radius-50: 2px; --global-radius-100: 4px; --global-radius-200: 8px; --global-radius-300: 12px; --global-radius-400: 16px; --global-radius-full: 999px; --global-spacing-0: 0; --global-spacing-25: 0.0625rem; --global-spacing-50: 0.125rem; --global-spacing-100: 0.25rem; --global-spacing-150: 0.375rem; --global-spacing-200: 0.5rem; --global-spacing-250: 0.625rem; --global-spacing-300: 0.75rem; --global-spacing-400: 1rem; --global-spacing-500: 1.25rem; --global-spacing-600: 1.5rem; --global-spacing-800: 2rem; --global-spacing-1000: 2.5rem; --global-spacing-1200: 3rem; --global-spacing-1600: 4rem; --global-spacing-2000: 5rem; --global-typography-font-sans: Pretendard Variable, Pretendard, sans-serif; --global-typography-fontSize-100: 0.6875rem; --global-typography-fontSize-200: 0.75rem; --global-typography-fontSize-300: 0.875rem; --global-typography-fontSize-400: 1rem; --global-typography-fontSize-450: 1.125rem; --global-typography-fontSize-500: 1.25rem; --global-typography-fontSize-600: 1.5rem; --global-typography-fontSize-700: 2rem; --global-typography-fontSize-800: 2.5rem; --global-typography-fontWeight-thin: 100; --global-typography-fontWeight-extraLight: 200; --global-typography-fontWeight-light: 300; --global-typography-fontWeight-regular: 400; --global-typography-fontWeight-medium: 500; --global-typography-fontWeight-semiBold: 600; --global-typography-fontWeight-bold: 700; --global-typography-fontWeight-extraBold: 800; --global-typography-fontWeight-black: 900; --global-typography-lineHeight-100: 1rem; --global-typography-lineHeight-200: 1.125rem; --global-typography-lineHeight-300: 1.375rem; --global-typography-lineHeight-400: 1.5rem; --global-typography-lineHeight-450: 1.625rem; --global-typography-lineHeight-500: 1.75rem; --global-typography-lineHeight-600: 2.125rem; --global-typography-lineHeight-700: 2.5rem; --global-typography-lineHeight-800: 3.25rem; --semantic-color-background-1: var(--global-colors-white); --semantic-color-background-2: var(--global-colors-gray-5); --semantic-color-background-3: var(--global-colors-gray-10); --semantic-color-background-4: var(--global-colors-gray-20); --semantic-color-background-5: var(--global-colors-gray-90); --semantic-color-border-1: var(--global-colors-gray-30); --semantic-color-border-2: var(--global-colors-gray-40); --semantic-color-border-3: var(--global-colors-gray-50); --semantic-color-border-4: var(--global-colors-gray-60); --semantic-color-border-default: var(--global-colors-gray-15); --semantic-color-text-1: var(--global-colors-gray-90); --semantic-color-text-2: var(--global-colors-gray-80); --semantic-color-text-3: var(--global-colors-gray-70); --semantic-color-text-4: var(--global-colors-gray-60); --semantic-color-text-5: var(--global-colors-gray-50); --semantic-color-text-6: var(--global-colors-white); --semantic-color-support-error-1: var(--global-colors-red-10); --semantic-color-support-error-2: var(--global-colors-red-20); --semantic-color-support-error-3: var(--global-colors-red-50); --semantic-color-support-error-4: var(--global-colors-red-100); --semantic-color-support-warning-1: var(--global-colors-orange-10); --semantic-color-support-warning-2: var(--global-colors-orange-20); --semantic-color-support-warning-3: var(--global-colors-orange-50); --semantic-color-support-warning-4: var(--global-colors-orange-100); --semantic-color-support-success-1: var(--global-colors-green-10); --semantic-color-support-success-2: var(--global-colors-green-20); --semantic-color-support-success-3: var(--global-colors-green-50); --semantic-color-support-success-4: var(--global-colors-green-100); --semantic-color-support-info-1: var(--global-colors-primary-10); --semantic-color-support-info-2: var(--global-colors-primary-20); --semantic-color-support-info-3: var(--global-colors-primary-60); --semantic-color-support-info-4: var(--global-colors-primary-100); --semantic-color-focus: rgb(from var(--global-colors-primary-50) r g b / 20%); --semantic-color-icon-primary: var(--global-colors-gray-90); --semantic-color-icon-secondary: var(--global-colors-gray-70); --semantic-color-icon-tertiary: var(--global-colors-gray-50); --semantic-color-icon-on-color-default: var(--global-colors-white); --semantic-color-icon-on-color-disabled: var(--global-colors-gray-60); --semantic-color-icon-interactive: var(--global-colors-primary-60); --semantic-color-toggle-primary-1: var(--global-colors-primary-60); --semantic-color-toggle-primary-2: var(--global-colors-primary-70); --semantic-color-toggle-primary-3: var(--global-colors-primary-100); --semantic-color-toggle-disabled-bg: var(--global-colors-gray-20); --semantic-color-toggle-disabled-border: var(--global-colors-gray-40); --semantic-color-toggle-disabled-text: var(--global-colors-gray-60); --semantic-color-toggle-off-bg: var(--global-colors-gray-50); --semantic-color-toggle-off-hover: var(--global-colors-gray-60); --semantic-color-toggle-hover-bg: var(--global-colors-primary-10); --semantic-typography-heading-1-fontSize: var(--global-typography-fontSize-200); --semantic-typography-heading-1-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-1-lineHeight: var(--global-typography-lineHeight-200); --semantic-typography-heading-2-fontSize: var(--global-typography-fontSize-300); --semantic-typography-heading-2-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-2-lineHeight: var(--global-typography-lineHeight-300); --semantic-typography-heading-3-fontSize: var(--global-typography-fontSize-400); --semantic-typography-heading-3-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-3-lineHeight: var(--global-typography-lineHeight-400); --semantic-typography-heading-4-fontSize: var(--global-typography-fontSize-500); --semantic-typography-heading-4-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-4-lineHeight: var(--global-typography-lineHeight-500); --semantic-typography-heading-5-fontSize: var(--global-typography-fontSize-600); --semantic-typography-heading-5-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-5-lineHeight: var(--global-typography-lineHeight-600); --semantic-typography-heading-6-fontSize: var(--global-typography-fontSize-700); --semantic-typography-heading-6-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-6-lineHeight: var(--global-typography-lineHeight-700); --semantic-typography-heading-7-fontSize: var(--global-typography-fontSize-800); --semantic-typography-heading-7-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-7-lineHeight: var(--global-typography-lineHeight-800); --semantic-typography-body-1-fontSize: var(--global-typography-fontSize-200); --semantic-typography-body-1-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-1-lineHeight: var(--global-typography-lineHeight-200); --semantic-typography-body-2-fontSize: var(--global-typography-fontSize-300); --semantic-typography-body-2-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-2-lineHeight: var(--global-typography-lineHeight-300); --semantic-typography-body-3-fontSize: var(--global-typography-fontSize-400); --semantic-typography-body-3-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-3-lineHeight: var(--global-typography-lineHeight-400); --semantic-typography-body-4-fontSize: var(--global-typography-fontSize-450); --semantic-typography-body-4-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-4-lineHeight: var(--global-typography-lineHeight-450); --semantic-typography-caption-1-fontSize: var(--global-typography-fontSize-100); --semantic-typography-caption-1-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-caption-1-lineHeight: var(--global-typography-lineHeight-100); --semantic-typography-caption-2-fontSize: var(--global-typography-fontSize-200); --semantic-typography-caption-2-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-caption-2-lineHeight: var(--global-typography-lineHeight-200); } ``` ### DataEffect 테마 (`dataEffect.css`) `@featuring-corp/design-tokens/style/dataEffect.css` ```css :root { --global-colors-primary-10: #edf5ff; --global-colors-primary-20: #d0e2ff; --global-colors-primary-30: #a6c8ff; --global-colors-primary-40: #4c9aff; --global-colors-primary-50: #2684ff; --global-colors-primary-60: #0065ff; --global-colors-primary-70: #0052cc; --global-colors-primary-80: #0747a6; --global-colors-primary-90: #001d6c; --global-colors-primary-100: #001141; --global-colors-secondary-10: #fafafa; --global-colors-secondary-20: #f6f6f6; --global-colors-secondary-30: #ebebeb; --global-colors-secondary-40: #e0e0e0; --global-colors-secondary-50: #d2d2d2; --global-colors-secondary-60: #bbbbbb; --global-colors-secondary-70: #959595; --global-colors-secondary-80: #707070; --global-colors-secondary-90: #424242; --global-colors-secondary-100: #242424; --global-colors-red-10: #fcedea; --global-colors-red-20: #f6c8bf; --global-colors-red-30: #f2ae9f; --global-colors-red-40: #ec8974; --global-colors-red-50: #e97259; --global-colors-red-60: #e34f2f; --global-colors-red-70: #cf482b; --global-colors-red-80: #a13821; --global-colors-red-90: #7d2b1A; --global-colors-red-100: #5f2114; --global-colors-orange-10: #fcf2e6; --global-colors-orange-20: #f5d5b0; --global-colors-orange-30: #f0c18a; --global-colors-orange-40: #eaa554; --global-colors-orange-50: #e59333; --global-colors-orange-60: #df7800; --global-colors-orange-70: #cb6d00; --global-colors-orange-80: #9e5500; --global-colors-orange-90: #7b4200; --global-colors-orange-100: #5e3200; --global-colors-yellow-10: #fffbe6; --global-colors-yellow-20: #fff1b3; --global-colors-yellow-30: #ffea8e; --global-colors-yellow-40: #ffe15a; --global-colors-yellow-50: #ffdb3a; --global-colors-yellow-60: #ffd209; --global-colors-yellow-70: #e8bf08; --global-colors-yellow-80: #b59506; --global-colors-yellow-90: #8c7405; --global-colors-yellow-100: #6b5804; --global-colors-lightGreen-10: #f5faf0; --global-colors-lightGreen-20: #dfefd0; --global-colors-lightGreen-30: #d0e8b9; --global-colors-lightGreen-40: #bbdd98; --global-colors-lightGreen-50: #add685; --global-colors-lightGreen-60: #99cc66; --global-colors-lightGreen-70: #8bba5d; --global-colors-lightGreen-80: #6d9148; --global-colors-lightGreen-90: #547038; --global-colors-lightGreen-100: #40562b; --global-colors-green-10: #ebf6f1; --global-colors-green-20: #c2e4d4; --global-colors-green-30: #a5d7bf; --global-colors-green-40: #7cc5a2; --global-colors-green-50: #62ba90; --global-colors-green-60: #3ba974; --global-colors-green-70: #369a6a; --global-colors-green-80: #2a7852; --global-colors-green-90: #205d40; --global-colors-green-100: #194731; --global-colors-cyan-10: #f2fcfb; --global-colors-cyan-20: #d8f6f3; --global-colors-cyan-30: #c5f1ee; --global-colors-cyan-40: #aaebe6; --global-colors-cyan-50: #99e7e1; --global-colors-cyan-60: #80e1d9; --global-colors-cyan-70: #74cdc5; --global-colors-cyan-80: #5ba09a; --global-colors-cyan-90: #467c77; --global-colors-cyan-100: #365f5b; --global-colors-teal-10: #eff8f7; --global-colors-teal-20: #cce9e6; --global-colors-teal-30: #b4deda; --global-colors-teal-40: #91cfc9; --global-colors-teal-50: #7cc5bf; --global-colors-teal-60: #5bb7af; --global-colors-teal-70: #53a79f; --global-colors-teal-80: #41827c; --global-colors-teal-90: #326560; --global-colors-teal-100: #264d4a; --global-colors-lightBlue-10: #f1f9fe; --global-colors-lightBlue-20: #d3ebfc; --global-colors-lightBlue-30: #bee1fa; --global-colors-lightBlue-40: #a1d3f8; --global-colors-lightBlue-50: #8ecbf6; --global-colors-lightBlue-60: #72bef4; --global-colors-lightBlue-70: #68adde; --global-colors-lightBlue-80: #5187ad; --global-colors-lightBlue-90: #3f6986; --global-colors-lightBlue-100: #305066; --global-colors-blue-10: #eef4ff; --global-colors-blue-20: #d8e6fe; --global-colors-blue-30: #aacbff; --global-colors-blue-40: #78b0fd; --global-colors-blue-50: #4d93f7; --global-colors-blue-60: #246ee1; --global-colors-blue-70: #0b54ab; --global-colors-blue-80: #014186; --global-colors-blue-90: #032d60; --global-colors-blue-100: #001639; --global-colors-indigo-10: #f1f1ff; --global-colors-indigo-20: #d5d4fd; --global-colors-indigo-30: #c0bffd; --global-colors-indigo-40: #a4a2fc; --global-colors-indigo-50: #9290fb; --global-colors-indigo-60: #7774fa; --global-colors-indigo-70: #6c6ae4; --global-colors-indigo-80: #5452b2; --global-colors-indigo-90: #41408a; --global-colors-indigo-100: #323169; --global-colors-purple-10: #f2eeff; --global-colors-purple-20: #d5cbff; --global-colors-purple-30: #c1b1ff; --global-colors-purple-40: #a58eff; --global-colors-purple-50: #9378ff; --global-colors-purple-60: #7856ff; --global-colors-purple-70: #6d4ee8; --global-colors-purple-80: #553db5; --global-colors-purple-90: #422f8c; --global-colors-purple-100: #32246b; --global-colors-magenta-10: #faf2fc; --global-colors-magenta-20: #efd8f4; --global-colors-magenta-30: #e7c5ef; --global-colors-magenta-40: #dbaae8; --global-colors-magenta-50: #d599e3; --global-colors-magenta-60: #ca80dc; --global-colors-magenta-70: #b874c8; --global-colors-magenta-80: #8f5b9c; --global-colors-magenta-90: #6f4679; --global-colors-magenta-100: #55365c; --global-colors-burgundy-10: #f7eef1; --global-colors-burgundy-20: #e7ccd2; --global-colors-burgundy-30: #dcb3bc; --global-colors-burgundy-40: #cb909e; --global-colors-burgundy-50: #c17a8b; --global-colors-burgundy-60: #b2596e; --global-colors-burgundy-70: #a25164; --global-colors-burgundy-80: #7e3f4e; --global-colors-burgundy-90: #62313d; --global-colors-burgundy-100: #4b252e; --global-colors-white: #ffffff; --global-colors-black: #000000; --global-colors-gray-5: #fafafa; --global-colors-gray-10: #f6f6f6; --global-colors-gray-15: #f0f0f0; --global-colors-gray-20: #ebebeb; --global-colors-gray-30: #e0e0e0; --global-colors-gray-40: #d2d2d2; --global-colors-gray-50: #bbbbbb; --global-colors-gray-60: #959595; --global-colors-gray-70: #707070; --global-colors-gray-80: #424242; --global-colors-gray-90: #242424; --global-elevation-2: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 1px 2px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-4: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 2px 4px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-8: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 4px 8px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-16: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 8px 16px 0px rgb(from var(--global-colors-black) r g b / 14%); --global-elevation-28: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 14px 28px 0px rgb(from var(--global-colors-black) r g b / 24%); --global-elevation-64: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 32px 64px 0px rgb(from var(--global-colors-black) r g b / 24%); --global-radius-50: 2px; --global-radius-100: 4px; --global-radius-200: 8px; --global-radius-300: 12px; --global-radius-400: 16px; --global-radius-full: 999px; --global-spacing-0: 0; --global-spacing-25: 0.0625rem; --global-spacing-50: 0.125rem; --global-spacing-100: 0.25rem; --global-spacing-150: 0.375rem; --global-spacing-200: 0.5rem; --global-spacing-250: 0.625rem; --global-spacing-300: 0.75rem; --global-spacing-400: 1rem; --global-spacing-500: 1.25rem; --global-spacing-600: 1.5rem; --global-spacing-800: 2rem; --global-spacing-1000: 2.5rem; --global-spacing-1200: 3rem; --global-spacing-1600: 4rem; --global-spacing-2000: 5rem; --global-typography-font-sans: Pretendard Variable, Pretendard, sans-serif; --global-typography-fontSize-100: 0.6875rem; --global-typography-fontSize-200: 0.75rem; --global-typography-fontSize-300: 0.875rem; --global-typography-fontSize-400: 1rem; --global-typography-fontSize-450: 1.125rem; --global-typography-fontSize-500: 1.25rem; --global-typography-fontSize-600: 1.5rem; --global-typography-fontSize-700: 2rem; --global-typography-fontSize-800: 2.5rem; --global-typography-fontWeight-thin: 100; --global-typography-fontWeight-extraLight: 200; --global-typography-fontWeight-light: 300; --global-typography-fontWeight-regular: 400; --global-typography-fontWeight-medium: 500; --global-typography-fontWeight-semiBold: 600; --global-typography-fontWeight-bold: 700; --global-typography-fontWeight-extraBold: 800; --global-typography-fontWeight-black: 900; --global-typography-lineHeight-100: 1rem; --global-typography-lineHeight-200: 1.125rem; --global-typography-lineHeight-300: 1.375rem; --global-typography-lineHeight-400: 1.5rem; --global-typography-lineHeight-450: 1.625rem; --global-typography-lineHeight-500: 1.75rem; --global-typography-lineHeight-600: 2.125rem; --global-typography-lineHeight-700: 2.5rem; --global-typography-lineHeight-800: 3.25rem; --semantic-color-background-1: var(--global-colors-white); --semantic-color-background-2: var(--global-colors-gray-5); --semantic-color-background-3: var(--global-colors-gray-10); --semantic-color-background-4: var(--global-colors-gray-20); --semantic-color-background-5: var(--global-colors-gray-90); --semantic-color-border-1: var(--global-colors-gray-30); --semantic-color-border-2: var(--global-colors-gray-40); --semantic-color-border-3: var(--global-colors-gray-50); --semantic-color-border-4: var(--global-colors-gray-60); --semantic-color-border-default: var(--global-colors-gray-15); --semantic-color-text-1: var(--global-colors-gray-90); --semantic-color-text-2: var(--global-colors-gray-80); --semantic-color-text-3: var(--global-colors-gray-70); --semantic-color-text-4: var(--global-colors-gray-60); --semantic-color-text-5: var(--global-colors-gray-50); --semantic-color-text-6: var(--global-colors-white); --semantic-color-support-error-1: var(--global-colors-red-10); --semantic-color-support-error-2: var(--global-colors-red-20); --semantic-color-support-error-3: var(--global-colors-red-50); --semantic-color-support-error-4: var(--global-colors-red-100); --semantic-color-support-warning-1: var(--global-colors-orange-10); --semantic-color-support-warning-2: var(--global-colors-orange-20); --semantic-color-support-warning-3: var(--global-colors-orange-50); --semantic-color-support-warning-4: var(--global-colors-orange-100); --semantic-color-support-success-1: var(--global-colors-green-10); --semantic-color-support-success-2: var(--global-colors-green-20); --semantic-color-support-success-3: var(--global-colors-green-50); --semantic-color-support-success-4: var(--global-colors-green-100); --semantic-color-support-info-1: var(--global-colors-primary-10); --semantic-color-support-info-2: var(--global-colors-primary-20); --semantic-color-support-info-3: var(--global-colors-primary-60); --semantic-color-support-info-4: var(--global-colors-primary-100); --semantic-color-focus: rgb(from var(--global-colors-primary-50) r g b / 20%); --semantic-color-icon-primary: var(--global-colors-gray-90); --semantic-color-icon-secondary: var(--global-colors-gray-70); --semantic-color-icon-tertiary: var(--global-colors-gray-50); --semantic-color-icon-on-color-default: var(--global-colors-white); --semantic-color-icon-on-color-disabled: var(--global-colors-gray-60); --semantic-color-icon-interactive: var(--global-colors-primary-60); --semantic-color-toggle-primary-1: var(--global-colors-primary-60); --semantic-color-toggle-primary-2: var(--global-colors-primary-70); --semantic-color-toggle-primary-3: var(--global-colors-primary-100); --semantic-color-toggle-disabled-bg: var(--global-colors-gray-20); --semantic-color-toggle-disabled-border: var(--global-colors-gray-40); --semantic-color-toggle-disabled-text: var(--global-colors-gray-60); --semantic-color-toggle-off-bg: var(--global-colors-gray-50); --semantic-color-toggle-off-hover: var(--global-colors-gray-60); --semantic-color-toggle-hover-bg: var(--global-colors-primary-10); --semantic-typography-heading-1-fontSize: var(--global-typography-fontSize-200); --semantic-typography-heading-1-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-1-lineHeight: var(--global-typography-lineHeight-200); --semantic-typography-heading-2-fontSize: var(--global-typography-fontSize-300); --semantic-typography-heading-2-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-2-lineHeight: var(--global-typography-lineHeight-300); --semantic-typography-heading-3-fontSize: var(--global-typography-fontSize-400); --semantic-typography-heading-3-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-3-lineHeight: var(--global-typography-lineHeight-400); --semantic-typography-heading-4-fontSize: var(--global-typography-fontSize-500); --semantic-typography-heading-4-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-4-lineHeight: var(--global-typography-lineHeight-500); --semantic-typography-heading-5-fontSize: var(--global-typography-fontSize-600); --semantic-typography-heading-5-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-5-lineHeight: var(--global-typography-lineHeight-600); --semantic-typography-heading-6-fontSize: var(--global-typography-fontSize-700); --semantic-typography-heading-6-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-6-lineHeight: var(--global-typography-lineHeight-700); --semantic-typography-heading-7-fontSize: var(--global-typography-fontSize-800); --semantic-typography-heading-7-fontWeight: var(--global-typography-fontWeight-medium); --semantic-typography-heading-7-lineHeight: var(--global-typography-lineHeight-800); --semantic-typography-body-1-fontSize: var(--global-typography-fontSize-200); --semantic-typography-body-1-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-1-lineHeight: var(--global-typography-lineHeight-200); --semantic-typography-body-2-fontSize: var(--global-typography-fontSize-300); --semantic-typography-body-2-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-2-lineHeight: var(--global-typography-lineHeight-300); --semantic-typography-body-3-fontSize: var(--global-typography-fontSize-400); --semantic-typography-body-3-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-3-lineHeight: var(--global-typography-lineHeight-400); --semantic-typography-body-4-fontSize: var(--global-typography-fontSize-450); --semantic-typography-body-4-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-body-4-lineHeight: var(--global-typography-lineHeight-450); --semantic-typography-caption-1-fontSize: var(--global-typography-fontSize-100); --semantic-typography-caption-1-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-caption-1-lineHeight: var(--global-typography-lineHeight-100); --semantic-typography-caption-2-fontSize: var(--global-typography-fontSize-200); --semantic-typography-caption-2-fontWeight: var(--global-typography-fontWeight-regular); --semantic-typography-caption-2-lineHeight: var(--global-typography-lineHeight-200); } ``` # Elevation `--global-elevation-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.elevation[8]); ``` ```css /* CSS에서 사용 */ .card { box-shadow: var(--global-elevation-8); } ``` ## Tokens
Code Value Preview
2 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 1px 2px 0px rgb(from var(--global-colors-black) r g b / 14%)
4 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 2px 4px 0px rgb(from var(--global-colors-black) r g b / 14%)
8 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 4px 8px 0px rgb(from var(--global-colors-black) r g b / 14%)
16 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 8px 16px 0px rgb(from var(--global-colors-black) r g b / 14%)
28 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 14px 28px 0px rgb(from var(--global-colors-black) r g b / 24%)
64 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 32px 64px 0px rgb(from var(--global-colors-black) r g b / 24%)
## 전체 토큰 목록 ``` --global-elevation-2: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 1px 2px 0px rgb(from var(--global-colors-black) r g b / 14%) --global-elevation-4: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 2px 4px 0px rgb(from var(--global-colors-black) r g b / 14%) --global-elevation-8: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 4px 8px 0px rgb(from var(--global-colors-black) r g b / 14%) --global-elevation-16: 0px 0px 2px 0px rgb(from var(--global-colors-black) r g b / 12%), 0px 8px 16px 0px rgb(from var(--global-colors-black) r g b / 14%) --global-elevation-28: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 14px 28px 0px rgb(from var(--global-colors-black) r g b / 24%) --global-elevation-64: 0px 0px 8px 0px rgb(from var(--global-colors-black) r g b / 20%), 0px 32px 64px 0px rgb(from var(--global-colors-black) r g b / 24%) ``` # Font Size `--global-typography-fontSize-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.typography.fontSize[300]); // "0.875rem" console.log(global.typography.fontSize[400]); // "1rem" ``` ```css /* CSS에서 사용 */ .text-base { font-size: var(--global-typography-fontSize-400); } ``` ## Tokens
Code Value Preview
100 0.6875rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

200 0.75rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

300 0.875rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

400 1rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

450 1.125rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

500 1.25rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

600 1.5rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

700 2rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

800 2.5rem

다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet

## 전체 토큰 목록 ``` --global-typography-fontSize-100: 0.6875rem --global-typography-fontSize-200: 0.75rem --global-typography-fontSize-300: 0.875rem --global-typography-fontSize-400: 1rem --global-typography-fontSize-450: 1.125rem --global-typography-fontSize-500: 1.25rem --global-typography-fontSize-600: 1.5rem --global-typography-fontSize-700: 2rem --global-typography-fontSize-800: 2.5rem ``` # Font Weight `--global-typography-fontWeight-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.typography.fontWeight.regular); // "400" console.log(global.typography.fontWeight.bold); // "700" console.log(global.typography.fontWeight.medium); // "500" ``` ```css /* CSS에서 사용 */ .text-bold { font-weight: var(--global-typography-fontWeight-bold); } ``` ## Tokens
Code Value Preview
thin 100

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

extraLight 200

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

light 300

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

regular 400

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

medium 500

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

semiBold 600

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

bold 700

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

extraBold 800

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

black 900

다람쥐 헌 쳇바퀴에 타고파
Lorem ipsum dolor sit amet

## 전체 토큰 목록 ``` --global-typography-fontWeight-thin: 100 --global-typography-fontWeight-extraLight: 200 --global-typography-fontWeight-light: 300 --global-typography-fontWeight-regular: 400 --global-typography-fontWeight-medium: 500 --global-typography-fontWeight-semiBold: 600 --global-typography-fontWeight-bold: 700 --global-typography-fontWeight-extraBold: 800 --global-typography-fontWeight-black: 900 ``` # Design Tokens ## @featuring-corp/design-tokens 3-Layer 토큰 아키텍처 기반의 디자인 토큰 패키지입니다. CSS 변수와 JavaScript 객체로 제공됩니다. ### 설치 npm pnpm yarn bun ```bash npm install @featuring-corp/design-tokens ``` ```bash pnpm add @featuring-corp/design-tokens ``` ```bash yarn add @featuring-corp/design-tokens ``` ```bash bun add @featuring-corp/design-tokens ``` ### 3-Layer 아키텍처 | 레이어 | 역할 | 예시 | | ------------- | ---------------- | ---------------------------------------------------------- | | **Global** | 브랜드 독립적 원시 값 | `--global-colors-red-50`, `--global-spacing-400` | | **Semantic** | UI 용도에 매핑 | `--semantic-color-text-1`, `--semantic-color-background-1` | | **Color Set** | 브랜드별 Primary 팔레트 | `--global-colors-primary-60` | ### 토큰 목록 | 토큰 | 설명 | | ---------------------------------------------- | ------------------------------- | | [Colors](/docs/design-tokens/colors) | 16개 색상 스케일 × 10단계 + Primary 팔레트 | | [Spacing](/docs/design-tokens/spacing) | 0\~2000 간격 스케일 | | [Typography](/docs/design-tokens/typography) | Heading, Body, Caption 타이포그래피 | | [Font Size](/docs/design-tokens/font-size) | 폰트 크기 스케일 | | [Font Weight](/docs/design-tokens/font-weight) | 폰트 두께 | | [Line Height](/docs/design-tokens/line-height) | 줄 높이 스케일 | | [Radius](/docs/design-tokens/radius) | 모서리 반지름 | | [Elevation](/docs/design-tokens/elevation) | 그림자 / 엘리베이션 | | [Base Styles](/docs/design-tokens/base-styles) | Reset + Normalize CSS | ### CSS 변수 사용 ```css .my-card { background-color: var(--semantic-color-background-1); color: var(--semantic-color-text-1); padding: var(--global-spacing-400); border-radius: var(--global-radius-200); } ``` ### JavaScript 사용 ```tsx import { global, semantic } from '@featuring-corp/design-tokens'; global.spacing[400] // '1rem' global.colors.red[50] // '#e97259' semantic.color.background[1] // 'var(--global-colors-white)' ``` # Line Height `--global-typography-lineHeight-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.typography.lineHeight[300]); // "1.375rem" console.log(global.typography.lineHeight[400]); // "1.5rem" ``` ```css /* CSS에서 사용 */ .text-base { font-size: var(--global-typography-fontSize-400); line-height: var(--global-typography-lineHeight-400); } ``` ## Tokens
Code Value Preview
100 1rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
200 1.125rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
300 1.375rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
400 1.5rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
450 1.625rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
500 1.75rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
600 2.125rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
700 2.5rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
800 3.25rem
다람쥐 헌 쳇바퀴에 타고파 Lorem ipsum dolor sit amet
## 전체 토큰 목록 ``` --global-typography-lineHeight-100: 1rem --global-typography-lineHeight-200: 1.125rem --global-typography-lineHeight-300: 1.375rem --global-typography-lineHeight-400: 1.5rem --global-typography-lineHeight-450: 1.625rem --global-typography-lineHeight-500: 1.75rem --global-typography-lineHeight-600: 2.125rem --global-typography-lineHeight-700: 2.5rem --global-typography-lineHeight-800: 3.25rem ``` # Radius `--global-radius-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.radius[200]); // "8px" console.log(global.radius.full); // "999px" ``` ```css /* CSS에서 사용 */ .button { border-radius: var(--global-radius-100); } .avatar { border-radius: var(--global-radius-full); } ``` ## Tokens
Code Value Preview
50 2px
100 4px
200 8px
300 12px
400 16px
full 999px
## 전체 토큰 목록 ``` --global-radius-50: 2px --global-radius-100: 4px --global-radius-200: 8px --global-radius-300: 12px --global-radius-400: 16px --global-radius-full: 999px ``` # Spacing `--global-spacing-{code}` ## Usage ```tsx import { global } from '@featuring-corp/design-tokens'; // TypeScript에서 사용 console.log(global.spacing[400]); // "1rem" console.log(global.spacing[800]); // "2rem" ``` ```css /* CSS에서 사용 */ .container { padding: var(--global-spacing-400); gap: var(--global-spacing-200); } ``` ## Tokens
Code Value Preview
0 0
25 0.0625rem
50 0.125rem
100 0.25rem
150 0.375rem
200 0.5rem
250 0.625rem
300 0.75rem
400 1rem
500 1.25rem
600 1.5rem
800 2rem
1000 2.5rem
1200 3rem
1600 4rem
2000 5rem
## 전체 토큰 목록 ``` --global-spacing-0: 0 --global-spacing-25: 0.0625rem --global-spacing-50: 0.125rem --global-spacing-100: 0.25rem --global-spacing-150: 0.375rem --global-spacing-200: 0.5rem --global-spacing-250: 0.625rem --global-spacing-300: 0.75rem --global-spacing-400: 1rem --global-spacing-500: 1.25rem --global-spacing-600: 1.5rem --global-spacing-800: 2rem --global-spacing-1000: 2.5rem --global-spacing-1200: 3rem --global-spacing-1600: 4rem --global-spacing-2000: 5rem ``` # Typography **Font:** Pretendard Variable, Pretendard, sans-serif ## Usage ```tsx import { global, semantic } from '@featuring-corp/design-tokens'; // Global Typography console.log(global.typography.font.sans); // "Pretendard Variable, Pretendard, sans-serif" console.log(global.typography.fontSize[400]); // "1rem" console.log(global.typography.fontWeight.medium); // "500" console.log(global.typography.lineHeight[400]); // "1.5rem" // Semantic Typography console.log(semantic.typography.heading[3].fontSize); // "var(--global-typography-fontSize-400)" console.log(semantic.typography.body[2].lineHeight); // "var(--global-typography-lineHeight-300)" ``` ```css /* CSS에서 사용 */ .heading { font-size: var(--semantic-typography-heading-3-fontSize); font-weight: var(--semantic-typography-heading-3-fontWeight); line-height: var(--semantic-typography-heading-3-lineHeight); } ``` ## Heading ### Heading 1 | Property | Token | Value | | ----------- | -------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-200)` | 0.75rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-200)` | 1.125rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 2 | Property | Token | Value | | ----------- | -------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-300)` | 0.875rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-300)` | 1.375rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 3 | Property | Token | Value | | ----------- | -------------------------------------------- | ------ | | Font Size | `var(--global-typography-fontSize-400)` | 1rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-400)` | 1.5rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 4 | Property | Token | Value | | ----------- | -------------------------------------------- | ------- | | Font Size | `var(--global-typography-fontSize-500)` | 1.25rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-500)` | 1.75rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 5 | Property | Token | Value | | ----------- | -------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-600)` | 1.5rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-600)` | 2.125rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 6 | Property | Token | Value | | ----------- | -------------------------------------------- | ------ | | Font Size | `var(--global-typography-fontSize-700)` | 2rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-700)` | 2.5rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Heading 7 | Property | Token | Value | | ----------- | -------------------------------------------- | ------- | | Font Size | `var(--global-typography-fontSize-800)` | 2.5rem | | Font Weight | `var(--global-typography-fontWeight-medium)` | 500 | | Line Height | `var(--global-typography-lineHeight-800)` | 3.25rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

## Body ### Body 1 | Property | Token | Value | | ----------- | --------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-200)` | 0.75rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-200)` | 1.125rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Body 2 | Property | Token | Value | | ----------- | --------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-300)` | 0.875rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-300)` | 1.375rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Body 3 | Property | Token | Value | | ----------- | --------------------------------------------- | ------ | | Font Size | `var(--global-typography-fontSize-400)` | 1rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-400)` | 1.5rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Body 4 | Property | Token | Value | | ----------- | --------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-450)` | 1.125rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-450)` | 1.625rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

## Caption ### Caption 1 | Property | Token | Value | | ----------- | --------------------------------------------- | --------- | | Font Size | `var(--global-typography-fontSize-100)` | 0.6875rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-100)` | 1rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

### Caption 2 | Property | Token | Value | | ----------- | --------------------------------------------- | -------- | | Font Size | `var(--global-typography-fontSize-200)` | 0.75rem | | Font Weight | `var(--global-typography-fontWeight-regular)` | 400 | | Line Height | `var(--global-typography-lineHeight-200)` | 1.125rem |

다람쥐 헌 쳇바퀴에 타고파

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

## 전체 토큰 목록 ### Heading ``` --semantic-typography-heading-1-fontSize: var(--global-typography-fontSize-200) --semantic-typography-heading-1-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-1-lineHeight: var(--global-typography-lineHeight-200) --semantic-typography-heading-2-fontSize: var(--global-typography-fontSize-300) --semantic-typography-heading-2-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-2-lineHeight: var(--global-typography-lineHeight-300) --semantic-typography-heading-3-fontSize: var(--global-typography-fontSize-400) --semantic-typography-heading-3-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-3-lineHeight: var(--global-typography-lineHeight-400) --semantic-typography-heading-4-fontSize: var(--global-typography-fontSize-500) --semantic-typography-heading-4-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-4-lineHeight: var(--global-typography-lineHeight-500) --semantic-typography-heading-5-fontSize: var(--global-typography-fontSize-600) --semantic-typography-heading-5-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-5-lineHeight: var(--global-typography-lineHeight-600) --semantic-typography-heading-6-fontSize: var(--global-typography-fontSize-700) --semantic-typography-heading-6-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-6-lineHeight: var(--global-typography-lineHeight-700) --semantic-typography-heading-7-fontSize: var(--global-typography-fontSize-800) --semantic-typography-heading-7-fontWeight: var(--global-typography-fontWeight-medium) --semantic-typography-heading-7-lineHeight: var(--global-typography-lineHeight-800) ``` ### Body ``` --semantic-typography-body-1-fontSize: var(--global-typography-fontSize-200) --semantic-typography-body-1-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-body-1-lineHeight: var(--global-typography-lineHeight-200) --semantic-typography-body-2-fontSize: var(--global-typography-fontSize-300) --semantic-typography-body-2-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-body-2-lineHeight: var(--global-typography-lineHeight-300) --semantic-typography-body-3-fontSize: var(--global-typography-fontSize-400) --semantic-typography-body-3-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-body-3-lineHeight: var(--global-typography-lineHeight-400) --semantic-typography-body-4-fontSize: var(--global-typography-fontSize-450) --semantic-typography-body-4-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-body-4-lineHeight: var(--global-typography-lineHeight-450) ``` ### Caption ``` --semantic-typography-caption-1-fontSize: var(--global-typography-fontSize-100) --semantic-typography-caption-1-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-caption-1-lineHeight: var(--global-typography-lineHeight-100) --semantic-typography-caption-2-fontSize: var(--global-typography-fontSize-200) --semantic-typography-caption-2-fontWeight: var(--global-typography-fontWeight-regular) --semantic-typography-caption-2-lineHeight: var(--global-typography-lineHeight-200) ``` # $css Prop `$css` prop은 Featuring Design System의 핵심 스타일링 API입니다. Rainbow Sprinkles를 기반으로, 디자인 토큰 값과 임의 CSS 값을 하나의 prop에서 사용할 수 있습니다. ## 기본 사용법 모든 레이아웃 컴포넌트(`Box`, `Flex`, `HStack`, `VStack`, `Center`, `Grid`, `Typo`)와 `Button` 컴포넌트에서 `$css` prop을 사용할 수 있습니다. Hello World `} /> ## 토큰 값 vs 임의 값 `$css` prop은 토큰 참조와 임의 CSS 값을 모두 지원합니다. 토큰 이름을 문자열로 전달하면 자동으로 CSS 변수로 변환됩니다. 토큰 + 임의 값 혼합 `} /> ## 레이아웃 속성 레이아웃 관련 속성은 **반응형 조건**(mobile/tablet/desktop/wide)을 지원합니다. ### Display & Flex flex: 1 flex: 2 flex: 1 `} /> ### Spacing spacing-200 spacing-400 spacing-600 `} /> ### Sizing ```tsx ``` ### Border & Radius 100 200 full `} /> ### Grid {[1,2,3,4,5,6].map(i => ( {i} ))} `} /> ### Typography ```tsx ``` ### Position ```tsx ``` ### Visual ```tsx ``` ### Transform & Transition ```tsx ``` ## 색상 속성 색상 관련 속성은 **인터랙티브 조건**(hover/active/focus/disabled 등)을 지원합니다. ### 기본 색상 속성 background-1 background-2 primary-60 `} /> ### 인터랙티브 조건 색상 속성에 객체를 전달하면 상태별 스타일을 지정할 수 있습니다. 아래 박스에 마우스를 올려보세요. Hover & Click me `} /> ### 사용 가능한 조건 | 조건 | CSS 선택자 | 설명 | | ------------- | ---------------------- | --------- | | `default` | — | 기본 상태 | | `hover` | `&:hover` | 마우스 호버 | | `active` | `&:active` | 클릭 중 | | `focus` | `&:focus-visible` | 키보드 포커스 | | `disabled` | `&:disabled` | 비활성 상태 | | `readOnly` | `&:read-only` | 읽기 전용 | | `focusWithin` | `&:focus-within` | 자식 요소 포커스 | | `groupHover` | `[data-group]:hover &` | 부모 그룹 호버 | ### 사용 가능한 토큰 색상 토큰 색상은 이름으로 직접 참조합니다: **글로벌 색상**: `primary-10`~~`primary-100`, `gray-5`~~`gray-90`, `red-10`~~`red-100`, `blue-10`~~`blue-100`, `green-10`~~`green-100`, `orange-10`~~`orange-100`, `yellow-10`~~`yellow-100`, `purple-10`~~`purple-100` 등 **시맨틱 색상**: `background-1`~~`background-4`, `border-default`, `border-1`~~`border-4`, `text-1`~~`text-6`, `support-error-1`~~`support-error-4`, `support-success-1`\~`support-success-4` 등 **특수 값**: `transparent`, `currentColor`, `inherit` ## className / style 콜백 `$css` 외에도 상태 기반 className과 style을 사용할 수 있습니다. ```tsx state.disabled ? 'opacity-50' : ''} style={(state) => ({ transform: state.loading ? 'scale(0.98)' : undefined, })} > Submit ``` ## 다른 스타일링과 함께 사용 `$css` prop은 기존 className, style과 함께 사용할 수 있습니다. 병합 우선순위는 다음과 같습니다: 1. `$css`의 토큰 스타일 (가장 낮음) 2. `$css`의 className 3. 내부 컴포넌트 props 4. 소비자의 className / style (가장 높음) ```tsx Content ``` # CSS Usage CSS 파일을 import하면 모든 디자인 토큰이 CSS 변수로 `:root`에 등록됩니다. JavaScript 없이도 순수 CSS로 스타일링할 수 있습니다. ## CSS Preset Import 앱 진입점에서 Preset CSS를 import합니다. Preset 하나로 Reset, Normalize, 브랜드 토큰, CSS Layer 순서가 모두 설정됩니다. ### Featuring 테마 ```tsx // _app.tsx, main.tsx, 또는 layout.tsx import '@featuring-corp/components/preset/featuring'; ``` ### DataEffect 테마 ```tsx // _app.tsx, main.tsx, 또는 layout.tsx import '@featuring-corp/components/preset/dataEffect'; ``` > **CSS Cascade Layers**: Preset은 `ft.reset < ft.normalize < ft.components < ft.utilities` 레이어 순서를 선언합니다. `$css` prop의 유틸리티 스타일이 컴포넌트 기본 스타일보다 우선하며, 소비자 CSS는 레이어에 속하지 않으므로 항상 디자인 시스템 스타일보다 우선합니다. ## CSS 변수 사용법 ### 기본 예시 ```css .my-card { /* 배경색 */ background-color: var(--semantic-color-background-1); /* 테두리 */ border: 1px solid var(--semantic-color-border-default); border-radius: var(--global-radius-200); /* 간격 */ padding: var(--global-spacing-400); margin-bottom: var(--global-spacing-200); /* 그림자 */ box-shadow: var(--global-elevation-4); } .my-card-title { /* 타이포그래피 */ font-size: var(--semantic-typography-heading-2-font-size); font-weight: var(--semantic-typography-heading-2-font-weight); line-height: var(--semantic-typography-heading-2-line-height); /* 텍스트 색상 */ color: var(--semantic-color-text-1); } .my-card-description { font-size: var(--semantic-typography-body-2-font-size); color: var(--semantic-color-text-2); } ``` ### 버튼 스타일링 ```css .primary-button { background-color: var(--global-colors-primary-60); color: var(--global-colors-white); border: none; border-radius: var(--global-radius-100); padding: var(--global-spacing-150) var(--global-spacing-300); font-size: var(--global-typography-fontSize-300); font-weight: var(--global-typography-fontWeight-semiBold); cursor: pointer; transition: background-color 0.2s; } .primary-button:hover { background-color: var(--global-colors-primary-70); } .primary-button:active { background-color: var(--global-colors-primary-80); } .primary-button:disabled { background-color: var(--semantic-color-toggle-disabled-bg); color: var(--semantic-color-toggle-disabled-text); cursor: not-allowed; } ``` ### 상태 피드백 ```css .success-message { background-color: var(--semantic-color-support-success-4); border: 1px solid var(--semantic-color-support-success-1); color: var(--semantic-color-support-success-1); padding: var(--global-spacing-200) var(--global-spacing-300); border-radius: var(--global-radius-100); } .error-message { background-color: var(--semantic-color-support-error-4); border: 1px solid var(--semantic-color-support-error-1); color: var(--semantic-color-support-error-1); padding: var(--global-spacing-200) var(--global-spacing-300); border-radius: var(--global-radius-100); } .warning-message { background-color: var(--global-colors-yellow-10); border: 1px solid var(--semantic-color-support-warning-1); color: var(--semantic-color-support-warning-1); padding: var(--global-spacing-200) var(--global-spacing-300); border-radius: var(--global-radius-100); } ``` ## CSS 변수 목록 ### Colors (색상) #### Primary (테마별로 다름) | 변수명 | Featuring | DataEffect | | ----------------------------- | --------- | ---------- | | `--global-colors-primary-10` | `#ecefff` | `#edf5ff` | | `--global-colors-primary-20` | `#dce2ff` | `#d0e2ff` | | `--global-colors-primary-30` | `#c0c8ff` | `#a6c8ff` | | `--global-colors-primary-40` | `#9aa3ff` | `#4c9aff` | | `--global-colors-primary-50` | `#7273ff` | `#2684ff` | | `--global-colors-primary-60` | `#5e51ff` | `#0065ff` | | `--global-colors-primary-70` | `#5032f9` | `#0052cc` | | `--global-colors-primary-80` | `#3821b2` | `#0747a6` | | `--global-colors-primary-90` | `#31238c` | `#001d6c` | | `--global-colors-primary-100` | `#1f1551` | `#001141` | #### Gray | 변수명 | 값 | | ------------------------- | --------- | | `--global-colors-gray-5` | `#fafafa` | | `--global-colors-gray-10` | `#f6f6f6` | | `--global-colors-gray-20` | `#ebebeb` | | `--global-colors-gray-30` | `#e0e0e0` | | `--global-colors-gray-40` | `#d2d2d2` | | `--global-colors-gray-50` | `#bbbbbb` | | `--global-colors-gray-60` | `#959595` | | `--global-colors-gray-70` | `#707070` | | `--global-colors-gray-80` | `#424242` | | `--global-colors-gray-90` | `#242424` | | `--global-colors-white` | `#ffffff` | | `--global-colors-black` | `#000000` | 더 많은 색상 변수는 [Colors 문서](/docs/design-tokens/colors)를 참조하세요. ### Spacing (간격) | 변수명 | 값 | | ----------------------- | ----------- | | `--global-spacing-0` | `0` | | `--global-spacing-25` | `0.0625rem` | | `--global-spacing-50` | `0.125rem` | | `--global-spacing-100` | `0.25rem` | | `--global-spacing-150` | `0.375rem` | | `--global-spacing-200` | `0.5rem` | | `--global-spacing-250` | `0.625rem` | | `--global-spacing-300` | `0.75rem` | | `--global-spacing-400` | `1rem` | | `--global-spacing-500` | `1.25rem` | | `--global-spacing-600` | `1.5rem` | | `--global-spacing-800` | `2rem` | | `--global-spacing-1000` | `2.5rem` | | `--global-spacing-1200` | `3rem` | | `--global-spacing-1600` | `4rem` | | `--global-spacing-2000` | `5rem` | ### Radius (모서리) | 변수명 | 값 | | ---------------------- | ------- | | `--global-radius-50` | `2px` | | `--global-radius-100` | `4px` | | `--global-radius-200` | `8px` | | `--global-radius-300` | `12px` | | `--global-radius-400` | `16px` | | `--global-radius-full` | `999px` | ### Elevation (그림자) | 변수명 | 설명 | | ----------------------- | --------------- | | `--global-elevation-2` | 미세한 그림자 (2dp) | | `--global-elevation-4` | 카드 그림자 (4dp) | | `--global-elevation-8` | 팝오버 그림자 (8dp) | | `--global-elevation-16` | 드롭다운 그림자 (16dp) | | `--global-elevation-28` | 모달 그림자 (28dp) | | `--global-elevation-64` | 최상위 그림자 (64dp) | ### Typography (타이포그래피) #### Heading | 변수명 | font-size | font-weight | line-height | | ----------------------------------- | --------- | ----------- | ----------- | | `--semantic-typography-heading-1-*` | 0.75rem | 500 | 1.125rem | | `--semantic-typography-heading-2-*` | 0.875rem | 500 | 1.375rem | | `--semantic-typography-heading-3-*` | 1rem | 500 | 1.5rem | | `--semantic-typography-heading-4-*` | 1.25rem | 500 | 1.75rem | | `--semantic-typography-heading-5-*` | 1.5rem | 500 | 2.125rem | | `--semantic-typography-heading-6-*` | 2rem | 500 | 2.5rem | | `--semantic-typography-heading-7-*` | 2.5rem | 500 | 3.25rem | #### Body | 변수명 | font-size | font-weight | line-height | | -------------------------------- | --------- | ----------- | ----------- | | `--semantic-typography-body-1-*` | 0.75rem | 400 | 1.125rem | | `--semantic-typography-body-2-*` | 0.875rem | 400 | 1.375rem | | `--semantic-typography-body-3-*` | 1rem | 400 | 1.5rem | | `--semantic-typography-body-4-*` | 1.125rem | 400 | 1.625rem | #### Caption | 변수명 | font-size | font-weight | line-height | | ----------------------------------- | --------- | ----------- | ----------- | | `--semantic-typography-caption-1-*` | 0.6875rem | 400 | 1rem | | `--semantic-typography-caption-2-*` | 0.75rem | 400 | 1.125rem | ### Semantic Colors (시맨틱 색상) #### Background | 변수명 | 용도 | | ------------------------------- | ---------------- | | `--semantic-color-background-1` | 기본 배경 (white) | | `--semantic-color-background-2` | 보조 배경 (gray-5) | | `--semantic-color-background-3` | 강조 배경 (gray-10) | | `--semantic-color-background-4` | 최상위 배경 (gray-20) | #### Border | 변수명 | 용도 | | --------------------------------- | ---------------- | | `--semantic-color-border-default` | 기본 테두리 (gray-15) | | `--semantic-color-border-1` | 테두리 1 (gray-30) | | `--semantic-color-border-2` | 테두리 2 (gray-40) | | `--semantic-color-border-3` | 테두리 3 (gray-50) | | `--semantic-color-border-4` | 진한 테두리 (gray-60) | #### Text | 변수명 | 용도 | | ------------------------- | ----------------- | | `--semantic-color-text-1` | 기본 텍스트 (gray-90) | | `--semantic-color-text-2` | 보조 텍스트 (gray-80) | | `--semantic-color-text-3` | 비활성 텍스트 (gray-70) | | `--semantic-color-text-4` | 플레이스홀더 (gray-60) | | `--semantic-color-text-5` | 연한 텍스트 (gray-50) | | `--semantic-color-text-6` | 반전 텍스트 (white) | #### Support | 변수명 | 용도 | | ------------------------------------ | ------------------- | | `--semantic-color-support-error-1` | 에러 연한 (red-10) | | `--semantic-color-support-error-2` | 에러 (red-20) | | `--semantic-color-support-error-3` | 에러 강조 (red-50) | | `--semantic-color-support-error-4` | 에러 진한 (red-100) | | `--semantic-color-support-warning-1` | 경고 연한 (orange-10) | | `--semantic-color-support-warning-2` | 경고 (orange-20) | | `--semantic-color-support-warning-3` | 경고 강조 (orange-50) | | `--semantic-color-support-warning-4` | 경고 진한 (orange-100) | | `--semantic-color-support-success-1` | 성공 연한 (green-10) | | `--semantic-color-support-success-2` | 성공 (green-20) | | `--semantic-color-support-success-3` | 성공 강조 (green-50) | | `--semantic-color-support-success-4` | 성공 진한 (green-100) | | `--semantic-color-support-info-1` | 정보 연한 (primary-10) | | `--semantic-color-support-info-2` | 정보 (primary-20) | | `--semantic-color-support-info-3` | 정보 강조 (primary-60) | | `--semantic-color-support-info-4` | 정보 진한 (primary-100) | # Design Philosophy ## 핵심 철학 Featuring Design System은 **소비자(consumer) 우선**의 설계 철학을 따릅니다. 디자인 시스템은 제약이 아니라 도구여야 합니다. 소비자가 시스템과 싸우지 않고, 자연스럽게 확장하고 커스터마이징할 수 있어야 합니다. 이 철학은 세 가지 핵심 가치로 요약됩니다: 1. **소비자 CSS가 항상 이긴다** — CSS Cascade Layers를 통해 `!important` 없이 오버라이드 가능 2. **접근성은 기본값이다** — disabled, focus, hover 등 인터랙션 상태가 WAI-ARIA 패턴을 준수 3. **조합이 설정을 이긴다** — Compound Component 패턴으로 유연성과 타입 안전성을 동시에 확보 *** ## 설계 원칙 ### 1. Token-First Design 디자인의 모든 결정은 토큰으로 시작합니다. 색상, 간격, 타이포그래피, 그림자 — 모든 시각적 속성이 체계적인 토큰 시스템 위에 구축됩니다. {/* 토큰을 사용한 일관된 스타일링 */} Token-based Primary Error `} /> 토큰을 사용하면: * **일관성**: 모든 화면에서 동일한 시각적 언어를 사용합니다 * **유지보수**: 값을 한 곳에서 변경하면 전체에 반영됩니다 * **브랜드 전환**: CSS 파일 교체만으로 Featuring ↔ DataEffect 테마를 전환할 수 있습니다 ### 2. Consumer CSS Always Wins CSS Cascade Layers를 사용하여 디자인 시스템의 모든 CSS를 `@layer` 안에 배치합니다. 소비자 CSS는 레이어 밖에 있으므로 **항상 디자인 시스템 스타일보다 우선**합니다. ``` @layer ft.reset, ft.normalize, ft.components; /* 이 순서대로 우선순위가 결정됩니다: ft.reset → 가장 낮은 우선순위 ft.normalize → 브라우저 정규화 ft.components → 컴포넌트 스타일 (unlayered) → 소비자 CSS — 항상 이김 */ ``` 이것이 의미하는 것: * 소비자는 **`!important` 없이** 어떤 컴포넌트 스타일이든 오버라이드할 수 있습니다 * `$css` prop으로 토큰 기반 스타일링을 하되, 필요하면 일반 CSS로 자유롭게 확장 가능합니다 * 디자인 시스템이 소비자의 스타일링을 방해하지 않습니다 ### 3. Zero-Runtime CSS Vanilla Extract를 기반으로 모든 CSS가 빌드 타임에 생성됩니다. 런타임에 JavaScript로 스타일을 계산하지 않으므로: * **성능**: 스타일 계산으로 인한 JavaScript 번들 증가가 없습니다 * **예측 가능성**: 생성된 CSS는 정적이며, FOUC(Flash of Unstyled Content)가 발생하지 않습니다 * **디버깅**: 브라우저 DevTools에서 일반 CSS로 확인할 수 있습니다 ### 4. Accessible by Default 인터랙티브 컴포넌트의 상태 관리는 WAI-ARIA 패턴을 준수합니다. 특히 disabled 상태에서의 접근성을 두 가지 레벨로 구분합니다: **HTML `disabled`** — 요소를 완전히 비활성화합니다. 포커스, 클릭, 키보드 이벤트 모두 차단됩니다. **`aria-disabled="true"` + `data-disabled`** — 시각적으로는 비활성화되지만, 포커스는 가능합니다. 툴팁 표시, 스크린 리더 접근 등을 위해 사용합니다. ```tsx // 완전 비활성화 — 포커스 불가 저장 // 포커스 가능한 비활성화 — 툴팁, 접근성 유지 저장 ``` 이 패턴은 rainbow-sprinkles의 조건부 셀렉터에 반영되어 있습니다: * `hover` / `active` 조건은 `&:not([data-disabled])` 가드를 포함하여, 비활성화 상태에서 hover/active 스타일이 적용되지 않습니다 * `disabled` 조건은 `&:is(:disabled, [data-disabled])`로 양쪽 모두를 처리합니다 * `focus` 조건은 `&:focus-visible`로, `focusableWhenDisabled` 상태에서도 포커스 링이 표시됩니다 > **왜 비활성화 상태에서 hover를 차단할까?** disabled 버튼에 hover 효과가 적용되면 사용자에게 "클릭 가능하다"는 잘못된 신호를 줍니다. Material UI, Radix, Chakra UI 등 주요 디자인 시스템도 동일한 패턴을 따릅니다. ### 5. Composition over Configuration 하나의 거대한 컴포넌트보다 작고 조합 가능한 컴포넌트를 선호합니다. {/* Composition: 순서를 자유롭게 변경 가능 */} Icon + Text Text + Icon Both `} /> Compound Component 패턴을 통해: * 렌더링 순서를 자유롭게 변경할 수 있습니다 * 각 하위 요소에 개별적으로 스타일을 적용할 수 있습니다 * Context를 통해 부모 상태(size, loading, disabled)가 하위 컴포넌트에 자동 전달됩니다 * 타입 안전성을 유지하면서 유연한 API를 제공합니다 ### 6. Polymorphic Rendering 모든 레이아웃 컴포넌트는 `render` prop을 통해 렌더링할 HTML 요소를 변경할 수 있습니다. {/* div 대신 section으로 렌더링 */} } $css={{ padding: '$spacing-400', bgColor: '$background-2', borderRadius: '$radius-200', }}> <section>으로 렌더링 {/* h1으로 렌더링 */} } variant="$heading-3"> <h2>으로 렌더링된 Typo {/* span으로 렌더링 */} } $css={{ padding: '$spacing-200', bgColor: '$primary-10', borderRadius: '$radius-100', display: 'inline-block', }}> <span>으로 렌더링 `} /> 시맨틱 HTML을 유지하면서도 디자인 시스템의 스타일링 기능을 활용할 수 있습니다. ### 7. Responsive-First 모든 레이아웃 속성은 반응형을 기본으로 지원합니다. Mobile-first 접근 방식으로, 작은 화면에서 시작하여 큰 화면으로 확장합니다. 모바일: spacing-300 / 데스크톱: spacing-600 `} /> 4단계 브레이크포인트를 제공합니다: | 이름 | 최소 너비 | 용도 | | --------- | ------ | ------------ | | `mobile` | 0px | 기본값 (모바일 우선) | | `tablet` | 768px | 태블릿 및 소형 노트북 | | `desktop` | 1024px | 데스크톱 | | `wide` | 1440px | 와이드 모니터 | ### 8. `$css` — 단일 스타일링 표면 `$css` prop은 디자인 시스템의 **유일한 스타일링 인터페이스**입니다. 토큰 값(`$spacing-400`)과 임의 값(`16px`)을 모두 받아들이며, 반응형 조건과 인터랙션 조건을 단일 객체로 표현합니다. ```tsx ``` 이 설계의 핵심: * **학습 비용 최소화**: 하나의 prop만 익히면 모든 스타일링이 가능합니다 * **토큰 가이드**: `$` 접두사로 시작하는 값은 디자인 토큰이라는 시각적 힌트를 제공합니다 * **타입 안전성**: 토큰 이름과 CSS 속성 모두 자동완성됩니다 * **임의 값 허용**: 토큰에 없는 값도 자유롭게 사용할 수 있어 디자인 시스템이 제약이 되지 않습니다 *** ## 토큰 아키텍처 (3 Layers) 토큰 시스템은 세 가지 레이어로 구성됩니다: **Global Tokens** — 브랜드 독립적인 원시 값 ``` --global-colors-red-50: #e97259 --global-spacing-400: 1rem --global-radius-200: 8px ``` **Semantic Tokens** — UI 맥락에 매핑된 토큰. 글로벌 토큰을 참조합니다. ``` --semantic-color-text-1: var(--global-colors-gray-90) --semantic-color-background-1: var(--global-colors-white) --semantic-color-support-error-1: var(--global-colors-red-50) ``` **Color Sets** — 브랜드별 Primary 컬러 팔레트 ``` /* Featuring */ --global-colors-primary-60: #5e51ff /* DataEffect */ --global-colors-primary-60: #0065ff ``` 이 구조를 통해 시맨틱 토큰이 글로벌 토큰을 참조하고, Primary 색상만 브랜드별로 교체되므로 일관성과 유연성을 동시에 확보합니다. CSS 파일 하나를 교체하는 것만으로 전체 브랜드 테마가 전환됩니다. *** ## 기술 스택 | 기술 | 역할 | | ----------------------------------------------------------------------------- | --------------------------------------------- | | [Vanilla Extract](https://vanilla-extract.style/) | Zero-runtime CSS-in-JS. 빌드 타임 CSS 생성 | | [Rainbow Sprinkles](https://github.com/wayfair/rainbow-sprinkles) | 토큰 기반 atomic CSS. `$css` prop 제공 | | [Base UI](https://base-ui.com/) | `useRender`, `mergeProps` — 폴리모픽 렌더링과 prop 병합 | | [CSS Cascade Layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) | 소비자 CSS 우선순위 보장 | | [TypeScript](https://www.typescriptlang.org/) | 모든 토큰과 API에 타입 안전성 제공 | | [React](https://react.dev/) 18+ | UI 렌더링 라이브러리 | *** ## 컴포넌트 패밀리 디자인 시스템은 세 가지 컴포넌트 패밀리로 구성됩니다. ### Layout Primitives `Box`, `Flex`, `HStack`, `VStack`, `Center`, `Grid`, `Typo` — `useRenderComponent` 훅을 사용하며 `$css` prop, `render` prop, 상태 콜백 className/style을 지원합니다. }>제목 Left Right `} /> ### Compound Components (신규) `Button`, `Tag` — Namespace export 패턴(`Button.Root`, `Button.Icon`, `Button.Text`)을 사용합니다. Context를 통해 size, loading, disabled 등 상태를 하위 컴포넌트에 공유하며, 각 하위 요소의 렌더링 순서와 스타일을 자유롭게 제어할 수 있습니다. 추가 Active `} /> ### Legacy (Core\*) Components `CoreButton`, `CoreTextInput`, `CoreSelect`, `CoreModal`, `CoreTag` 등 — `forwardRef` 기반의 UI 컴포넌트. Recipe 기반 변형(variant)과 Vanilla Extract 스타일을 사용합니다. `} /> > **마이그레이션 방향**: 새로운 컴포넌트는 모두 Compound Component 패턴으로 개발됩니다. 기존 Core\* 컴포넌트는 하위 호환성을 유지하며 점진적으로 대체됩니다. 새 컴포넌트(Button, Tag)가 Core\* 대응물(CoreButton, CoreTag)보다 더 유연한 API를 제공합니다. # Getting Started ## Featuring Design System 피처링의 모든 프로덕트에서 일관된 사용자 경험을 만들기 위한 React 디자인 시스템입니다. 디자이너와 개발자가 같은 언어로 소통하고, 빠르게 프로덕션 품질의 UI를 구축할 수 있도록 설계되었습니다. ## 패키지 구성 세 개의 독립적인 패키지로 구성되어 있으며, 필요한 것만 선택하여 사용할 수 있습니다. | 패키지 | 설명 | | ------------------------------- | --------------------------------------------------------------------- | | `@featuring-corp/design-tokens` | 1,000개 이상의 디자인 토큰 — 색상, 간격, 타이포그래피, 반지름, 그림자. CSS 변수와 JS 객체로 제공 | | `@featuring-corp/components` | 30개 이상의 React UI 컴포넌트 — Layout primitives, Form, Feedback, Navigation | | `@featuring-corp/icons` | 333개 시스템 아이콘 + 26개 서비스 아이콘. React 컴포넌트로 제공 | ### 패키지 의존성 ``` design-tokens ← icons (독립) ← components (둘 다 의존) ``` `design-tokens`는 다른 패키지에 의존하지 않으며, `icons`는 독립적이고, `components`가 두 패키지 모두를 사용합니다. ## 핵심 개념 ### $css Prop 레이아웃 컴포넌트(`Box`, `Flex`, `HStack`, `VStack`, `Center`, `Grid`, `Typo`)에서 사용하는 토큰 기반 스타일링 API입니다. ```tsx import { Box } from '@featuring-corp/components'; Token-based styling ``` 토큰 값(`$spacing-400`)과 임의 CSS 값(`100%`, `16px`)을 모두 지원하며, 반응형과 인터랙티브 조건을 네이티브로 지원합니다. 자세한 내용: [$css Prop](/docs/guide/css-prop) ### 반응형 디자인 레이아웃 속성에 객체를 전달하면 브레이크포인트별로 다른 값이 적용됩니다. ```tsx Responsive content ``` 4단계 브레이크포인트: `mobile`(0px) → `tablet`(768px) → `desktop`(1024px) → `wide`(1440px) 자세한 내용: [Responsive Design](/docs/guide/responsive) ### render Prop 모든 레이아웃 컴포넌트는 `render` prop으로 렌더링할 HTML 요소를 변경할 수 있습니다. ```tsx import { Box, Typo } from '@featuring-corp/components'; // section으로 렌더링 } $css={{ padding: '$spacing-400' }}> Content // h1으로 렌더링 } variant="$heading-1"> Page Title ``` ### 3-Layer Token System | 레이어 | 역할 | 예시 | | --------- | ---------------- | ---------------------------------------------------------- | | Global | 브랜드 독립적 원시 값 | `--global-colors-red-50`, `--global-spacing-400` | | Semantic | UI 맥락에 매핑 | `--semantic-color-text-1`, `--semantic-color-background-1` | | Color Set | 브랜드별 Primary 팔레트 | `--global-colors-primary-60` (Featuring: `#5e51ff`) | 자세한 내용: [Design Philosophy](/docs/guide/design-philosophy) ## 브랜드 테마 두 가지 브랜드 테마를 지원합니다. CSS 파일 교체만으로 전체 테마가 전환됩니다. | 테마 | Primary 색상 | CSS 파일 | | -------------- | --------------- | ---------------- | | **Featuring** | `#5e51ff` (보라색) | `featuring.css` | | **DataEffect** | `#0065ff` (파란색) | `dataEffect.css` | ## 기술 스택 | 기술 | 역할 | | ----------------------------------------------------------------- | ------------------------------------------------- | | [React](https://react.dev/) 18+ | UI 렌더링 | | [TypeScript](https://www.typescriptlang.org/) | 타입 안전성 | | [Vanilla Extract](https://vanilla-extract.style/) | Zero-runtime CSS-in-JS | | [Rainbow Sprinkles](https://github.com/wayfair/rainbow-sprinkles) | `$css` prop (토큰 기반 atomic CSS) | | [Base UI](https://base-ui.com/) | Polymorphic rendering (`useRender`, `mergeProps`) | ## 빠른 시작 ```tsx // 1. CSS import (앱 진입점) import '@featuring-corp/components/preset/featuring'; // 2. 컴포넌트 사용 import { Box, HStack, Typo, Button } from '@featuring-corp/components'; import { IconSearchOutline } from '@featuring-corp/icons'; function App() { return ( }> Welcome Search ); } ``` 다음: [Installation](/docs/guide/installation) # Installation ## 시스템 요구사항 * **React**: 18 이상 * **Node.js**: 18 이상 * **패키지 매니저**: npm, yarn, pnpm 모두 지원 ## 1. NPM Registry 설정 패키지는 GitHub Packages에 배포되어 있습니다. 프로젝트 루트에 `.npmrc` 파일을 생성합니다. ```bash title=".npmrc" @featuring-corp:registry=https://npm.pkg.github.com always-auth=true //npm.pkg.github.com/:_authToken= ``` > ``을 GitHub Personal Access Token (read:packages 권한)으로 교체하세요. ## 2. 패키지 설치 npm pnpm yarn bun ```bash npm install @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash pnpm add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash yarn add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash bun add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` 토큰만 사용하거나 아이콘만 필요한 경우, 개별적으로 설치할 수 있습니다: npm pnpm yarn bun ```bash npm install @featuring-corp/design-tokens ``` ```bash pnpm add @featuring-corp/design-tokens ``` ```bash yarn add @featuring-corp/design-tokens ``` ```bash bun add @featuring-corp/design-tokens ``` ## 3. Peer Dependencies 설치 일부 컴포넌트(`CoreDropdown`, `CoreModal`, `CoreSelectPrim`, `CoreMultiSelectPrim`)는 커스텀 스크롤바를 위해 [OverlayScrollbars](https://github.com/KingSora/OverlayScrollbars)를 사용합니다. 해당 컴포넌트를 사용하는 경우 별도 설치가 필요합니다. npm pnpm yarn bun ```bash npm install overlayscrollbars overlayscrollbars-react ``` ```bash pnpm add overlayscrollbars overlayscrollbars-react ``` ```bash yarn add overlayscrollbars overlayscrollbars-react ``` ```bash bun add overlayscrollbars overlayscrollbars-react ``` > 위 컴포넌트를 사용하지 않는다면 이 단계를 건너뛸 수 있습니다. ## 4. CSS Preset Import 앱 진입점에서 Preset CSS를 import합니다. **이 단계는 필수입니다.** Preset 하나로 CSS Reset, Normalize, 브랜드 토큰, CSS Layer 순서가 모두 설정됩니다. ```tsx title="앱 진입점 (_app.tsx, main.tsx, layout.tsx 등)" // Featuring 테마 import '@featuring-corp/components/preset/featuring'; // 또는 DataEffect 테마 import '@featuring-corp/components/preset/dataEffect'; ``` ### Preset이 포함하는 것 | 내용 | 설명 | | ---------------------- | -------------------------------------------------------- | | `@layer ft.reset` | 브라우저 기본 스타일 초기화 (design-tokens의 `reset.css`) | | `@layer ft.normalize` | 브라우저 간 렌더링 일관성 (design-tokens의 `normalize.css`) | | `@layer ft.components` | 컴포넌트 CSS가 속하는 레이어 (자동 적용) | | `@layer ft.utilities` | `$css` prop 등 atomic CSS 유틸리티 레이어 (rainbow-sprinkles) | | 브랜드 토큰 | Featuring(`#5e51ff`) 또는 DataEffect(`#0065ff`) 테마의 CSS 변수 | ### CSS Cascade Layers Preset은 [CSS Cascade Layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer)를 사용합니다. 레이어 우선순위: ``` ft.reset < ft.normalize < ft.components < ft.utilities < (unlayered — 소비자 CSS) ``` **소비자 CSS는 레이어에 속하지 않으므로 항상 디자인 시스템 스타일보다 우선합니다.** 별도의 `!important` 없이 컴포넌트 스타일을 오버라이드할 수 있습니다. ### 개별 Import (고급) Preset 대신 개별 CSS를 직접 import할 수도 있습니다. Tailwind 등 자체 reset을 사용하는 프로젝트에 유용합니다. ```tsx // design-tokens에서 개별 import import '@featuring-corp/design-tokens/style/reset.css'; import '@featuring-corp/design-tokens/style/normalize.css'; import '@featuring-corp/design-tokens/style/featuring.css'; ``` > **참고**: 개별 import 시 CSS Layer 순서가 자동으로 설정되지 않으므로, 소비자 CSS 오버라이드가 보장되지 않습니다. 특별한 이유가 없다면 Preset 사용을 권장합니다. ## 5. Portal 스태킹 설정 Tooltip, Modal 등 Portal 기반 컴포넌트는 `document.body`에 렌더링됩니다. 앱 콘텐츠보다 항상 위에 표시되도록 **앱 루트 요소에 CSS `isolation: isolate`를 추가**하세요. ```css title="Vite (index.css)" #root { isolation: isolate; } ``` ```css title="Next.js Pages Router (globals.css)" #__next { isolation: isolate; } ``` ```css title="Next.js App Router (globals.css)" /* layout.tsx에서
로 감싼 경우 */ #app-root { isolation: isolate; } ``` 이 설정으로 앱 내부의 `z-index` 값들이 Portal과 경쟁하지 않게 됩니다. 별도의 z-index 관리가 필요 없습니다. > **원리**: `isolation: isolate`는 앱 루트를 하나의 [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Stacking_context)로 만듭니다. 내부의 `z-index: 20` 같은 값은 그 안에서만 유효하고, Portal은 루트 바깥(`document.body`)에 렌더링되므로 자연스럽게 위에 표시됩니다. ## 6. 사용 시작 ### 레이아웃 컴포넌트 ```tsx import { Box, HStack, VStack, Typo } from '@featuring-corp/components'; function Card({ title, description }: { title: string; description: string }) { return ( }>{title} {description} ); } ``` ### Button 컴포넌트 ```tsx import { Button, IconButton } from '@featuring-corp/components'; import { IconSearchOutline, IconPlusOutline } from '@featuring-corp/icons'; // 텍스트 버튼 저장 // 아이콘 + 텍스트 버튼 검색 // 아이콘 전용 버튼 ``` ### Core 컴포넌트 ```tsx import { CoreTextInput, CoreSelect, CoreTag } from '@featuring-corp/components'; 관리자 멤버 ``` ### 아이콘 ```tsx import { IconHomeFilled, IconSearchOutline, IconSettingsOutline } from '@featuring-corp/icons'; ``` 아이콘은 `size`와 `color` prop을 지원합니다. 시스템 아이콘은 `currentColor`를 사용하므로 부모의 `color` 속성을 상속합니다. ### 디자인 토큰 직접 사용 ```css /* CSS에서 직접 사용 */ .my-element { background-color: var(--semantic-color-background-1); color: var(--semantic-color-text-1); padding: var(--global-spacing-400); border-radius: var(--global-radius-200); box-shadow: var(--global-elevation-4); } ``` ```tsx // JavaScript에서 사용 import { global, semantic } from '@featuring-corp/design-tokens'; const spacing = global.spacing[400]; // '1rem' const color = global.colors.red[50]; // '#e97259' ``` ## 프레임워크별 설정 프레임워크에 따라 추가 설정이 필요할 수 있습니다. * [Next.js 설정 가이드](/docs/guide/nextjs) * [Vite 설정 가이드](/docs/guide/vite) ## 다음 단계 * [$css Prop 이해하기](/docs/guide/css-prop) — 핵심 스타일링 시스템 * [Responsive Design](/docs/guide/responsive) — 반응형 레이아웃 * [Design Philosophy](/docs/guide/design-philosophy) — 설계 원칙과 아키텍처 * [Design Tokens](/docs/design-tokens/colors) — 색상, 간격, 타이포그래피 토큰 # LLMs.txt ## LLMs.txt란? Featuring Corp. Design System의 문서를 LLM(Large Language Model)이 잘 이해하도록 구조화한 텍스트 파일입니다. AI 코딩 도구와 함께 사용하면 디자인 시스템을 더 효과적으로 활용할 수 있습니다. ## 제공 파일 | 파일명 | 설명 | 용도 | | ------------------------------- | ---------------- | ------------ | | [llms.txt](/llms.txt) | 전체 라우트 및 카테고리 개요 | 빠른 참조, 구조 파악 | | [llms-full.txt](/llms-full.txt) | 모든 문서의 전체 내용 | 상세 정보, 코드 생성 | ## AI 도구와 함께 사용하기 ### Cursor Cursor IDE에서 `@Docs` 기능을 사용하여 디자인 시스템 문서를 참조할 수 있습니다. 1. **Docs 추가**: Settings → Features → Docs → Add new doc 2. **URL 입력**: `https://your-docs-url.com/llms-full.txt` 3. **사용하기**: 채팅에서 `@Docs`로 참조 자세한 내용: [Cursor @Docs 문서](https://docs.cursor.com/context/@-symbols/@-docs) ### 프롬프트 예시 ``` @Docs CoreButton 컴포넌트를 사용해서 Primary 버튼을 만들어줘 ``` ``` @Docs semantic color 토큰 중에서 에러 상태를 나타내는 색상을 알려줘 ``` ``` @Docs CoreModal을 사용해서 확인/취소 모달을 구현해줘 ``` ## 포함된 정보 ### 컴포넌트 (`@featuring-corp/components`) 각 컴포넌트 문서에는 다음 정보가 포함됩니다: * **Props 테이블**: 모든 props의 타입, 기본값, 설명 * **스타일 토큰**: 사용된 디자인 토큰과 CSS 변수 * **Variant 스타일**: size, type 등 variant별 스타일 값 * **상태별 스타일**: hover, disabled, focus 등 상태별 스타일 ### 디자인 토큰 (`@featuring-corp/design-tokens`) * **Colors**: 글로벌 색상, 시맨틱 색상, Primary 색상 (Featuring/DataEffect) * **Spacing**: 0px \~ 80px 간격 토큰 * **Radius**: 2px \~ 999px 모서리 반경 * **Elevation**: 그림자 깊이 (2dp \~ 64dp) * **Typography**: 제목, 본문, 캡션 타이포그래피 ### 아이콘 (`@featuring-corp/icons`) * **System Icons**: 333개 아이콘 (Outline/Filled) * **Service Icons**: 26개 서비스 브랜드 아이콘 (Colored/White) ## LLM 최적화 내용 문서는 LLM이 이해하기 쉽도록 다음과 같이 구성되어 있습니다: 1. **명시적 토큰 목록**: 모든 디자인 토큰이 개별적으로 나열됨 2. **정확한 값 포함**: CSS 변수와 실제 값이 함께 표시됨 3. **구조화된 테이블**: Props, 스타일, Variant가 테이블로 정리됨 4. **완전한 import 경로**: 모든 컴포넌트/아이콘의 import 문 포함 5. **사용 예시**: 각 기능의 코드 예시 포함 # Next.js Next.js 프로젝트에서 Featuring Design System을 사용하는 방법을 안내합니다. ## 기본 설정 ### 1. 패키지 설치 npm pnpm yarn bun ```bash npm install @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash pnpm add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash yarn add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ```bash bun add @featuring-corp/design-tokens @featuring-corp/components @featuring-corp/icons ``` ### 2. Next.js 설정 `next.config.ts`에 `transpilePackages` 설정을 추가합니다. ```ts title="next.config.ts" import type { NextConfig } from 'next'; const nextConfig: NextConfig = { transpilePackages: ['@featuring-corp/components'], }; export default nextConfig; ``` ### 3. CSS Preset Import #### App Router (app/) ```tsx title="app/layout.tsx" import '@featuring-corp/components/preset/featuring'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` #### Pages Router (pages/) ```tsx title="pages/_app.tsx" import '@featuring-corp/components/preset/featuring'; import type { AppProps } from 'next/app'; export default function App({ Component, pageProps }: AppProps) { return ; } ``` ### 4. 사용 시작 ```tsx title="app/page.tsx" import { VStack, Typo, Button } from '@featuring-corp/components'; import { IconRocketOutline } from '@featuring-corp/icons'; export default function Page() { return ( }> Hello, Featuring! Launch ); } ``` ## Vanilla Extract와 함께 사용 프로젝트에서 Vanilla Extract를 직접 사용하려면 추가 설정이 필요합니다. ### 1. 추가 패키지 설치 npm pnpm yarn bun ```bash npm install -D @vanilla-extract/css @vanilla-extract/next-plugin ``` ```bash pnpm add -D @vanilla-extract/css @vanilla-extract/next-plugin ``` ```bash yarn add --dev @vanilla-extract/css @vanilla-extract/next-plugin ``` ```bash bun add --dev @vanilla-extract/css @vanilla-extract/next-plugin ``` ### 2. Next.js 설정 업데이트 ```ts title="next.config.ts" import { createVanillaExtractPlugin } from '@vanilla-extract/next-plugin'; import type { NextConfig } from 'next'; const withVanillaExtract = createVanillaExtractPlugin(); const nextConfig: NextConfig = { transpilePackages: ['@featuring-corp/components'], }; export default withVanillaExtract(nextConfig); ``` ### 3. Theme Contract 설정 ```ts title="src/styles/theme.css.ts" import { getGlobalColors, getVarName, global, semantic } from '@featuring-corp/design-tokens'; import { createGlobalTheme, createGlobalThemeContract } from '@vanilla-extract/css'; export const vars = createGlobalThemeContract( { global: { colors: getGlobalColors('featuring'), elevation: global.elevation, spacing: global.spacing, radius: global.radius, typography: global.typography, }, semantic, }, getVarName, ); createGlobalTheme(':root', vars, { global: { colors: getGlobalColors('featuring'), elevation: global.elevation, spacing: global.spacing, radius: global.radius, typography: global.typography, }, semantic, }); ``` ### 4. 스타일 파일에서 사용 ```ts title="src/styles/card.css.ts" import { style } from '@vanilla-extract/css'; import { vars } from './theme.css'; export const card = style({ backgroundColor: vars.semantic.color.background[1], border: `1px solid ${vars.semantic.color.border.default}`, borderRadius: vars.global.radius[200], padding: vars.global.spacing[400], boxShadow: vars.global.elevation[4], }); ``` > **참고**: Vanilla Extract 설정은 선택 사항입니다. `$css` prop과 CSS 변수만으로도 대부분의 스타일링이 가능합니다. # Responsive Design Featuring Design System은 Mobile-First 반응형 디자인을 기본으로 지원합니다. `$css` prop의 레이아웃 속성에서 객체 구문을 사용하여 브레이크포인트별 스타일을 지정할 수 있습니다. ## 브레이크포인트 | 이름 | 최소 너비 | 미디어 쿼리 | | --------- | ------ | ---------------------------- | | `mobile` | 0px | 기본값 (미디어 쿼리 없음) | | `tablet` | 768px | `@media (min-width: 768px)` | | `desktop` | 1024px | `@media (min-width: 1024px)` | | `wide` | 1440px | `@media (min-width: 1440px)` | ## 기본 사용법 레이아웃 속성에 객체를 전달하면 브레이크포인트별로 다른 값을 적용합니다. `mobile`은 기본값이므로 생략할 수 있으며, 지정하지 않은 브레이크포인트는 이전 값을 상속합니다. 화면 크기에 따라 padding이 변합니다 `} /> ## 반응형 레이아웃 패턴 ### 반응형 그리드 화면 크기에 따라 컬럼 수가 자동으로 조절됩니다. 브라우저 창 크기를 조절해 확인해 보세요. {[1,2,3,4,5,6].map(i => ( Card {i} ))} `} /> ### 반응형 방향 전환 모바일에서는 세로로, 태블릿 이상에서는 가로로 배치됩니다. Item 1 Item 2 Item 3 `} /> ### 반응형 타이포그래피 `Typo` 컴포넌트는 `variant` prop에서 직접 반응형 값을 지원합니다. } > 반응형 제목 화면 크기에 따라 텍스트 크기가 변합니다. `} /> ### 반응형 표시/숨김 태블릿 이상에서만 표시 모바일/태블릿에서만 표시 항상 표시 `} /> ## 반응형 컴포넌트 사이즈 `Button`과 `Typo` 컴포넌트는 `size`/`variant` prop에서 반응형 값을 지원합니다. ### 반응형 버튼 사이즈
), default: md, }, indeterminate: { description: 'Indeterminate (mixed) 상태. 부모 체크박스 패턴에서 사용', type: boolean, default: false, }, disabled: { description: '비활성 상태', type: boolean, default: false, }, checked: { description: '체크 상태 (controlled)', type: boolean, default: undefined, }, defaultChecked: { description: '초기 체크 상태 (uncontrolled)', type: boolean, default: undefined, }, onChange: { description: '변경 핸들러', type: {`ChangeEventHandler`}, default: undefined, }, }} /> ### Checkbox.Label 레이블 콘텐츠. 텍스트, 아이콘 등 ReactNode를 받습니다. ReactNode, default: '-', }, size: { description: '레이블 타이포 크기. Root의 size를 override. 반응형 객체 지원', type: (
sm,md,lg
), default: md, }, }} /> ## 스타일 ### Size Variants | Size | Indicator 크기 | SVG 크기 | | ---- | ------------ | ------ | | `sm` | 14px | 16px | | `md` | 16px | 18px | | `lg` | 20px | 22px | ### Color States | State | Color Token | 비고 | | --------------- | --------------------------- | ----------- | | unchecked | `toggle.primary.3` (border) | | | unchecked:hover | `toggle.hover.bg` (fill) | | | checked | `toggle.primary.1` (fill) | | | checked:hover | `toggle.primary.2` (fill) | | | indeterminate | `toggle.primary.1` (fill) | checked와 동일 | | disabled | `toggle.disabled.bg/border` | | | disabled text | `toggle.disabled.text` | | | focus-visible | `focus` (outline stroke) | | ### CheckboxState `className`, `style` 콜백에 전달되는 상태 객체입니다. Root, Input, Label 모두 동일합니다. boolean, }, indeterminate: { description: 'indeterminate (mixed) 상태', type: boolean, }, }} /> ### CheckboxSize sm,md,lg ), }, }} /> # Field import { TypeTable } from 'fumadocs-ui/components/type-table'; ## 개요 `Field`는 **폼 컨트롤(TextInput, Select 등)에 레이블·보조 텍스트·검증 메시지를 연결**하는 컨테이너입니다. * **자동 ID 연결** — `Field.Root`가 고유 `fieldId`, `descriptionId`, `validationId`를 생성하고 자식에 context로 전파 * **접근성 자동화** — `Field.Label`의 `htmlFor`, `Field.Description`·`Field.Message`의 `id`가 자동 설정 * **상태 전파** — `status`, `disabled`, `readOnly`가 context를 통해 하위 컴포넌트(TextInput 등)로 전달 * **4가지 `status`** — `none` (기본), `success`, `warning`, `error` * **`$css` prop** — 모든 서브컴포넌트에서 rainbow-sprinkles 토큰 기반 스타일링 * **`render` prop** — base-ui 기반 다형성 렌더링 ### 접근성 * **WCAG 1.3.1** — `Field.Label`이 `htmlFor`로 입력 요소와 연결 * **WCAG 3.3.1** — `Field.Message`가 `role="alert"` 또는 `aria-live`로 오류 안내 * **WCAG 3.3.2** — `Field.Description`의 `id`가 입력 요소의 `aria-describedby`에 자동 연결 * **WCAG 1.4.3** — `disabled`/`readOnly` 상태는 시각적 색상과 aria 속성 모두 변경 ## Usage ### 기본 사용법 { const [value, setValue] = React.useState(''); return ( 이름 setValue(e.target.value)} /> 실명을 입력해 주세요. ); }`} /> ### 검증 상태 `status`에 따라 `Field.Message`의 색상과 아이콘이 변경됩니다. { const [value, setValue] = React.useState(''); const isError = value.length > 0 && value.length < 3; return ( 닉네임 setValue(e.target.value)} /> {isError && 닉네임은 3자 이상이어야 합니다.} ); }`} /> ### 필수 항목 `Field.Label`의 `required` prop을 사용하면 레이블 뒤에 `*` 인디케이터가 표시됩니다. 이메일 `} /> ### 성공 상태 아이디 사용 가능한 아이디입니다. `} /> ### 경고 상태 비밀번호 비밀번호가 너무 짧습니다. `} /> ### 비활성 상태 사용자 ID 수정할 수 없는 항목입니다. `} /> ### 읽기 전용 가입일 `} /> ### 커스텀 레이아웃 (HStack) `$css`로 레이아웃을 자유롭게 조정할 수 있습니다. 이름 `} /> ## Props > **공통 Props** — `$css`, `render`, `className`, `style`는 모든 서브컴포넌트에서 지원됩니다. > [useRenderComponent 가이드 →](/docs/components/utils/use-render) ### Field.Root 폼 필드의 최상위 컨테이너. `fieldId`, `descriptionId`, `validationId`를 생성하여 context로 공유합니다. none,success,warning,error), default: none, }, disabled: { description: '비활성 상태. context를 통해 하위 컴포넌트로 전파', type: boolean, default: false, }, readOnly: { description: '읽기 전용 상태. context를 통해 하위 컴포넌트로 전파', type: boolean, default: false, }, children: { description: '필드 콘텐츠 (Label, 폼 컨트롤, Description, Message)', type: ReactNode, default: undefined, }, }} /> ### Field.Label 레이블 요소. `htmlFor`가 `Field.Root`의 `fieldId`에 자동 연결됩니다. boolean, default: false, }, children: { description: '레이블 콘텐츠', type: ReactNode, default: '-', }, }} /> ### Field.Description 보조 설명 텍스트. `id`가 자동 설정되어 폼 컨트롤의 `aria-describedby`에 연결됩니다. ReactNode, default: '-', }, }} /> ### Field.Message 검증 메시지 텍스트. `status`에 따라 색상과 아이콘이 변경됩니다. ReactNode, default: '-', }, }} /> ## 스타일 ### Status Variants | Status | Message 색상 | 아이콘 | | --------- | ------------------- | ------------- | | `none` | `text-3` | 없음 | | `success` | `support-success-1` | CheckCircle | | `warning` | `support-warning-1` | WarningCircle | | `error` | `support-error-1` | XCircle | ### FieldState `className`, `style` 콜백에 전달되는 상태 객체입니다. Root, Label, Description, Message 모두 동일합니다. boolean, }, readOnly: { description: '읽기 전용 상태', type: boolean, }, }} /> # Link import { TypeTable } from 'fumadocs-ui/components/type-table'; ## 개요 `Link`는 **텍스트 기반 하이퍼링크** 컴포넌트입니다. * **2가지 `size`** — `sm` (body-1), `md` (body-2) * **3가지 `underline`** — `always` (기본), `hover`, `none` * **visited 상태** — 기본 활성, `visited={false}`로 opt-out 가능 * **외부 링크 안전 처리** — `target="_blank"` 시 자동 `rel="noopener noreferrer"` + 스크린리더 안내 * **`render` prop** — Next.js Link 등 라우터 라이브러리 연동 * **`$css` prop** — rainbow-sprinkles를 통해 디자인 토큰 기반 스타일링 ### 접근성 * **WCAG 1.4.1** — `underline="always"` 기본값으로 색상 외 시각적 구분 제공 * **WCAG 2.4.7** — `focus-visible` 시 2px solid 아웃라인 * **외부 링크** — `target="_blank"` 시 `(새 탭에서 열림)` 스크린리더 텍스트 자동 삽입 * **disabled** — `aria-disabled` + `data-disabled` + 클릭 차단, focus-visible 아웃라인도 제거 ## Usage ### 기본 사용법 링크 텍스트 `} /> ### 크기 Small Medium `} /> ### 밑줄 동작 Always Hover None `} /> ### 아이콘과 함께 더 알아보기 이전 페이지 `} /> ### 외부 링크 `target="_blank"` 설정 시 자동으로 `rel="noopener noreferrer"`가 추가되고, 스크린리더에 "(새 탭에서 열림)" 안내가 삽입됩니다. 외부 사이트 `} /> ### Visited 상태 제어 기본적으로 방문한 링크는 보라색으로 표시됩니다. 대시보드 등 동적 콘텐츠에서는 `visited={false}`로 비활성화할 수 있습니다. Visited ON (기본) Visited OFF `} /> ### 비활성 상태 비활성 링크 비활성 + 아이콘 `} /> ### 인라인 사용 문장 내에서 사용할 때는 `underline="always"`를 권장합니다 (WCAG 1.4.1). 이용약관에 동의하시면{' '} 개인정보 처리방침 을 확인하신 것으로 간주합니다. `} /> ### 라우터 연동 (render prop) `render` prop으로 Next.js Link, React Router 등과 연동할 수 있습니다. ```tsx import NextLink from 'next/link'; } size="md"> About ``` ### $css 커스텀 간격 커스텀 `} /> ## Props > **공통 Props** — `$css`, `render`, `className`, `style`는 모든 서브컴포넌트에서 지원됩니다. > [useRenderComponent 가이드 →](/docs/components/utils/use-render) ### Link.Root sm,md ), default: sm, }, underline: { description: '밑줄 동작. 인라인 링크에는 always 권장 (WCAG 1.4.1)', type: (
always,hover,none
), default: always, }, visited: { description: 'Visited 상태 스타일 활성화. 대시보드 등에서 false로 비활성화', type: boolean, default: true, }, disabled: { description: '비활성 상태. aria-disabled 설정 및 클릭 차단', type: boolean, default: false, }, }} /> ### Link.Text `Typo` 컴포넌트를 래핑하며, `Link.Root`의 `size`에 따라 타이포그래피 variant가 자동 결정됩니다. TypoVariant, default: sm → $body-1, md → $body-2, }, }} /> ### Link.Icon 아이콘 래퍼. `size`에 따라 SVG 크기가 자동 조절됩니다 (sm: 14px, md: 16px). ReactNode, default: '-', }, }} /> ## 스타일 ### Size Variants | Size | Typography | Icon 크기 | | ---- | ---------- | ------- | | `sm` | body-1 | 14px | | `md` | body-2 | 16px | ### Color States | State | Color Token | 비고 | | ------------------------ | ----------------------------- | ------------------------------ | | default / `:link` | `blue[60]` | | | `:hover` | `blue[70]` | | | `:active` | `blue[80]` | | | `:focus-visible` | `blue[60]` + 2px outline | | | `:visited` | `purple[80]` | `visited` prop으로 opt-out 가능 | | `:visited:hover` | `purple[90]` | | | `:visited:active` | `purple[100]` | | | `:visited:focus-visible` | `purple[80]` + purple outline | | | `disabled` | `text[5]` | 모든 pseudo-class 무시, outline 제거 | ### Underline Variants | Underline | 동작 | | --------- | ------------------------ | | `always` | 항상 밑줄 표시 (WCAG 1.4.1 권장) | | `hover` | hover/focus 시에만 밑줄 | | `none` | 밑줄 없음 (네비게이션 컨텍스트용) | ### LinkState `className`, `style` 콜백에 전달되는 상태 객체입니다. boolean, }, visited: { description: 'visited 스타일 활성 여부', type: boolean, }, }} /> ### Data Attributes base-ui 철학에 따라 상태는 `data-*` 속성으로 DOM에 반영됩니다. 소비자 CSS에서 직접 타겟할 수 있습니다. | 속성 | 조건 | 용도 | | --------------- | --------------------- | --------------- | | `data-disabled` | `disabled={true}` | 비활성 스타일 커스텀 | | `data-visited` | `visited={true}` (기본) | visited 스타일 커스텀 | ```css /* 소비자 CSS 예시 */ .my-link[data-disabled] { opacity: 0.5; } .my-link[data-visited]:visited { color: gray; } ``` ### LinkSize sm,md ), }, }} /> ### LinkUnderline always,hover,none ), }, }} /> # Loader import { TypeTable } from 'fumadocs-ui/components/type-table'; ## 개요 `Loader`는 비동기 작업의 **로딩 상태**를 표시하는 스피너 컴포넌트입니다. * **7가지 `size`** — `xxs`, `xs`, `sm`, `md`, `lg`, `xl`, `xxl` * **2가지 `color`** — `primary`, `white` * **`$css` prop** — rainbow-sprinkles를 통해 디자인 토큰 기반 스타일링 `Button`의 `loading` 상태, `StatusBadge.Loader` 등 내부 컴포넌트에서도 사용됩니다. ## Usage ### 기본 사용법 `} /> ### 크기 설정 `} /> ### 색상 설정 `} /> ### $css로 토큰 기반 커스텀 {/* margin 추가 */} {/* 투명도 조절 */} `} /> ## Props > **공통 Props** — `$css`, `render`, `className`, `style`는 모든 서브컴포넌트에서 지원됩니다. > [useRenderComponent 가이드 →](/docs/components/utils/use-render) ### Loader xxs,xs,sm,md,lg,xl, xxl ), default: md, }, color: { description: '로더 색상', type: (
primary,white
), default: primary, }, }} /> ## 스타일 ### Size Variants | Size | Width/Height | Border Width | | ----- | ------------ | ------------ | | `xxs` | 8px | 1.2px | | `xs` | 16px | 2.5px | | `sm` | 24px | 3.8px | | `md` | 40px | 6.45px | | `lg` | 56px | 9px | | `xl` | 72px | 11.5px | | `xxl` | 88px | 14.1px | ### Color Variants | Color | border-color | border-right-color | | --------- | ------------------------------ | ------------------ | | `primary` | `border-default` | `primary[60]` | | `white` | `border-default (40% opacity)` | `white` | ### Animation * **Keyframes**: `spinner` — `rotate(1turn)` * **Duration**: `1s` * **Timing**: `linear` * **Iteration**: `infinite` ### LoaderSize xxs,xs,sm,md,lg,xl, xxl ), }, }} /> ### LoaderColor primary,white ), }, }} /> # Radio import { TypeTable } from 'fumadocs-ui/components/type-table'; ## 개요 `Radio`는 **여러 옵션 중 하나를 선택**하는 폼 컨트롤입니다. * **Compound composition** — `Group`, `Root`, `Input`, `Label`을 자유롭게 배치 * **3가지 `size`** — `sm` (14px), `md` (16px, 기본), `lg` (20px) * **Group 값 관리** — `value`/`onValueChange`로 단일 선택 제어 * **`$css` prop** — 모든 서브컴포넌트에서 rainbow-sprinkles 토큰 기반 스타일링 * **`render` prop** — base-ui 기반 다형성 렌더링 * **네이티브 ``** 기반 — form 제출 호환 ### 접근성 * **WCAG 1.3.1** — `role="radiogroup"` + `