@uniai-fe/uds-primitives 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +63 -0
  2. package/package.json +85 -0
  3. package/src/components/alternate/hooks/index.ts +4 -0
  4. package/src/components/alternate/img/.gitkeep +0 -0
  5. package/src/components/alternate/index.scss +1 -0
  6. package/src/components/alternate/index.tsx +4 -0
  7. package/src/components/alternate/markup/index.tsx +4 -0
  8. package/src/components/alternate/styles/index.scss +3 -0
  9. package/src/components/alternate/types/index.ts +4 -0
  10. package/src/components/alternate/utils/index.ts +4 -0
  11. package/src/components/badge/hooks/index.ts +4 -0
  12. package/src/components/badge/img/.gitkeep +0 -0
  13. package/src/components/badge/index.scss +1 -0
  14. package/src/components/badge/index.tsx +6 -0
  15. package/src/components/badge/markup/Badge.tsx +51 -0
  16. package/src/components/badge/markup/index.tsx +1 -0
  17. package/src/components/badge/styles/index.scss +189 -0
  18. package/src/components/badge/types/index.ts +55 -0
  19. package/src/components/badge/utils/index.ts +21 -0
  20. package/src/components/button/hooks/index.ts +4 -0
  21. package/src/components/button/img/.gitkeep +0 -0
  22. package/src/components/button/index.scss +1 -0
  23. package/src/components/button/index.tsx +6 -0
  24. package/src/components/button/markup/Button.tsx +175 -0
  25. package/src/components/button/markup/index.tsx +1 -0
  26. package/src/components/button/styles/index.scss +847 -0
  27. package/src/components/button/types/index.ts +79 -0
  28. package/src/components/button/utils/index.ts +58 -0
  29. package/src/components/calendar/hooks/index.ts +4 -0
  30. package/src/components/calendar/img/.gitkeep +0 -0
  31. package/src/components/calendar/index.scss +1 -0
  32. package/src/components/calendar/index.tsx +4 -0
  33. package/src/components/calendar/markup/index.tsx +4 -0
  34. package/src/components/calendar/styles/index.scss +3 -0
  35. package/src/components/calendar/types/index.ts +4 -0
  36. package/src/components/calendar/utils/index.ts +4 -0
  37. package/src/components/checkbox/hooks/index.ts +4 -0
  38. package/src/components/checkbox/img/.gitkeep +0 -0
  39. package/src/components/checkbox/img/check-large.svg +3 -0
  40. package/src/components/checkbox/img/check-medium.svg +3 -0
  41. package/src/components/checkbox/img/check.svg +3 -0
  42. package/src/components/checkbox/index.scss +1 -0
  43. package/src/components/checkbox/index.tsx +4 -0
  44. package/src/components/checkbox/markup/Checkbox.tsx +127 -0
  45. package/src/components/checkbox/markup/index.ts +1 -0
  46. package/src/components/checkbox/styles/index.scss +164 -0
  47. package/src/components/checkbox/types/checkbox.ts +21 -0
  48. package/src/components/checkbox/types/index.ts +1 -0
  49. package/src/components/chip/hooks/index.ts +4 -0
  50. package/src/components/chip/img/.gitkeep +0 -0
  51. package/src/components/chip/img/remove.svg +3 -0
  52. package/src/components/chip/index.scss +1 -0
  53. package/src/components/chip/index.tsx +6 -0
  54. package/src/components/chip/markup/Chip.tsx +103 -0
  55. package/src/components/chip/markup/index.tsx +1 -0
  56. package/src/components/chip/styles/index.scss +140 -0
  57. package/src/components/chip/types/index.ts +52 -0
  58. package/src/components/chip/utils/index.ts +36 -0
  59. package/src/components/dialog/hooks/index.ts +4 -0
  60. package/src/components/dialog/img/.gitkeep +0 -0
  61. package/src/components/dialog/index.scss +1 -0
  62. package/src/components/dialog/index.tsx +3 -0
  63. package/src/components/dialog/markup/confirm-dialog.tsx +316 -0
  64. package/src/components/dialog/markup/index.tsx +4 -0
  65. package/src/components/dialog/markup/notice-dialog.tsx +191 -0
  66. package/src/components/dialog/styles/base.scss +153 -0
  67. package/src/components/dialog/styles/confirm.scss +58 -0
  68. package/src/components/dialog/styles/index.scss +3 -0
  69. package/src/components/dialog/styles/notice.scss +65 -0
  70. package/src/components/dialog/types/index.ts +70 -0
  71. package/src/components/dialog/utils/index.ts +4 -0
  72. package/src/components/drawer/hooks/index.ts +113 -0
  73. package/src/components/drawer/img/.gitkeep +0 -0
  74. package/src/components/drawer/img/close.svg +3 -0
  75. package/src/components/drawer/index.scss +1 -0
  76. package/src/components/drawer/index.tsx +3 -0
  77. package/src/components/drawer/markup/drawer.tsx +421 -0
  78. package/src/components/drawer/markup/index.tsx +3 -0
  79. package/src/components/drawer/styles/index.scss +232 -0
  80. package/src/components/drawer/types/index.ts +51 -0
  81. package/src/components/drawer/utils/context.ts +15 -0
  82. package/src/components/drawer/utils/index.tsx +77 -0
  83. package/src/components/dropdown/hooks/index.ts +4 -0
  84. package/src/components/dropdown/img/.gitkeep +0 -0
  85. package/src/components/dropdown/index.scss +1 -0
  86. package/src/components/dropdown/index.tsx +4 -0
  87. package/src/components/dropdown/markup/index.tsx +4 -0
  88. package/src/components/dropdown/styles/index.scss +3 -0
  89. package/src/components/dropdown/types/index.ts +4 -0
  90. package/src/components/dropdown/utils/index.ts +4 -0
  91. package/src/components/input/hooks/index.ts +4 -0
  92. package/src/components/input/img/.gitkeep +0 -0
  93. package/src/components/input/img/check-correct.svg +3 -0
  94. package/src/components/input/img/check-default.svg +3 -0
  95. package/src/components/input/img/check-incorrect.svg +3 -0
  96. package/src/components/input/img/error.svg +5 -0
  97. package/src/components/input/img/hide-off.svg +4 -0
  98. package/src/components/input/img/hide-on.svg +6 -0
  99. package/src/components/input/img/reset.svg +3 -0
  100. package/src/components/input/img/search.svg +4 -0
  101. package/src/components/input/img/success.svg +3 -0
  102. package/src/components/input/index.scss +1 -0
  103. package/src/components/input/index.tsx +6 -0
  104. package/src/components/input/markup/index.tsx +1 -0
  105. package/src/components/input/markup/text/Base.tsx +311 -0
  106. package/src/components/input/markup/text/Identification.tsx +145 -0
  107. package/src/components/input/markup/text/Password.tsx +71 -0
  108. package/src/components/input/markup/text/Phone.tsx +115 -0
  109. package/src/components/input/markup/text/Search.tsx +35 -0
  110. package/src/components/input/markup/text/index.ts +10 -0
  111. package/src/components/input/styles/index.scss +375 -0
  112. package/src/components/input/types/index.ts +56 -0
  113. package/src/components/input/utils/index.ts +54 -0
  114. package/src/components/label/hooks/index.ts +4 -0
  115. package/src/components/label/img/.gitkeep +0 -0
  116. package/src/components/label/index.scss +1 -0
  117. package/src/components/label/index.tsx +4 -0
  118. package/src/components/label/markup/index.tsx +4 -0
  119. package/src/components/label/styles/index.scss +3 -0
  120. package/src/components/label/types/index.ts +4 -0
  121. package/src/components/label/utils/index.ts +4 -0
  122. package/src/components/navigation/hooks/index.ts +4 -0
  123. package/src/components/navigation/img/.gitkeep +0 -0
  124. package/src/components/navigation/index.scss +1 -0
  125. package/src/components/navigation/index.tsx +8 -0
  126. package/src/components/navigation/markup/index.tsx +2 -0
  127. package/src/components/navigation/markup/mobile/BottomNavigation.tsx +127 -0
  128. package/src/components/navigation/markup/mobile/index.ts +1 -0
  129. package/src/components/navigation/markup/web/index.ts +4 -0
  130. package/src/components/navigation/styles/index.scss +133 -0
  131. package/src/components/navigation/types/index.ts +38 -0
  132. package/src/components/navigation/utils/index.ts +23 -0
  133. package/src/components/pagination/hooks/index.ts +4 -0
  134. package/src/components/pagination/img/.gitkeep +0 -0
  135. package/src/components/pagination/index.scss +1 -0
  136. package/src/components/pagination/index.tsx +6 -0
  137. package/src/components/pagination/markup/Carousel.tsx +76 -0
  138. package/src/components/pagination/markup/Count.tsx +54 -0
  139. package/src/components/pagination/markup/Pagination.tsx +83 -0
  140. package/src/components/pagination/markup/index.tsx +3 -0
  141. package/src/components/pagination/styles/index.scss +155 -0
  142. package/src/components/pagination/types/index.ts +68 -0
  143. package/src/components/pagination/utils/index.ts +58 -0
  144. package/src/components/radio/hooks/index.ts +4 -0
  145. package/src/components/radio/img/.gitkeep +0 -0
  146. package/src/components/radio/index.scss +1 -0
  147. package/src/components/radio/index.tsx +7 -0
  148. package/src/components/radio/markup/Radio.tsx +121 -0
  149. package/src/components/radio/markup/RadioCard.tsx +68 -0
  150. package/src/components/radio/markup/RadioCardGroup.tsx +75 -0
  151. package/src/components/radio/markup/index.tsx +3 -0
  152. package/src/components/radio/styles/index.scss +252 -0
  153. package/src/components/radio/types/index.ts +1 -0
  154. package/src/components/radio/types/radio.ts +63 -0
  155. package/src/components/radio/utils/index.ts +4 -0
  156. package/src/components/scrollbar/hooks/index.ts +4 -0
  157. package/src/components/scrollbar/img/.gitkeep +0 -0
  158. package/src/components/scrollbar/index.scss +1 -0
  159. package/src/components/scrollbar/index.tsx +4 -0
  160. package/src/components/scrollbar/markup/index.tsx +4 -0
  161. package/src/components/scrollbar/styles/index.scss +3 -0
  162. package/src/components/scrollbar/types/index.ts +4 -0
  163. package/src/components/scrollbar/utils/index.ts +4 -0
  164. package/src/components/segmented-control/index.scss +1 -0
  165. package/src/components/segmented-control/index.tsx +7 -0
  166. package/src/components/segmented-control/markup/SegmentedControl.tsx +117 -0
  167. package/src/components/segmented-control/markup/index.ts +1 -0
  168. package/src/components/segmented-control/styles/index.scss +113 -0
  169. package/src/components/segmented-control/types/index.ts +22 -0
  170. package/src/components/select/hooks/index.ts +4 -0
  171. package/src/components/select/img/.gitkeep +0 -0
  172. package/src/components/select/index.scss +1 -0
  173. package/src/components/select/index.tsx +4 -0
  174. package/src/components/select/markup/index.tsx +4 -0
  175. package/src/components/select/styles/index.scss +3 -0
  176. package/src/components/select/types/index.ts +4 -0
  177. package/src/components/select/utils/index.ts +4 -0
  178. package/src/components/spinner/hooks/index.ts +4 -0
  179. package/src/components/spinner/img/.gitkeep +0 -0
  180. package/src/components/spinner/index.scss +1 -0
  181. package/src/components/spinner/index.tsx +4 -0
  182. package/src/components/spinner/markup/index.tsx +4 -0
  183. package/src/components/spinner/styles/index.scss +3 -0
  184. package/src/components/spinner/types/index.ts +4 -0
  185. package/src/components/spinner/utils/index.ts +4 -0
  186. package/src/components/tab/hooks/index.ts +4 -0
  187. package/src/components/tab/img/.gitkeep +0 -0
  188. package/src/components/tab/index.scss +1 -0
  189. package/src/components/tab/index.tsx +6 -0
  190. package/src/components/tab/markup/TabContent.tsx +29 -0
  191. package/src/components/tab/markup/TabList.tsx +60 -0
  192. package/src/components/tab/markup/TabRoot.tsx +74 -0
  193. package/src/components/tab/markup/TabTrigger.tsx +47 -0
  194. package/src/components/tab/markup/index.tsx +4 -0
  195. package/src/components/tab/styles/index.scss +182 -0
  196. package/src/components/tab/types/index.ts +46 -0
  197. package/src/components/tab/utils/index.ts +5 -0
  198. package/src/components/tab/utils/tab-context.ts +20 -0
  199. package/src/components/table/hooks/index.ts +4 -0
  200. package/src/components/table/img/.gitkeep +0 -0
  201. package/src/components/table/index.scss +1 -0
  202. package/src/components/table/index.tsx +4 -0
  203. package/src/components/table/markup/index.tsx +4 -0
  204. package/src/components/table/styles/index.scss +3 -0
  205. package/src/components/table/types/index.ts +4 -0
  206. package/src/components/table/utils/index.ts +4 -0
  207. package/src/hooks/index.ts +4 -0
  208. package/src/img/.gitkeep +0 -0
  209. package/src/index.scss +3 -0
  210. package/src/index.tsx +26 -0
  211. package/src/init/dayjs.ts +14 -0
  212. package/src/theme/ThemeProvider.tsx +25 -0
  213. package/src/theme/config.ts +29 -0
  214. package/src/theme/index.ts +3 -0
  215. package/src/theme/overrides.scss +215 -0
  216. package/src/types/index.ts +4 -0
  217. package/src/utils/index.ts +4 -0
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @uniai-fe/uds-primitives
2
+
3
+ `@uniai-fe/uds-foundation` 토큰 위에 Radix UI 컴포넌트를 얇게 감싼 **기초 UI 컴포넌트 컬렉션**입니다. Next.js 등 React 런타임에서 바로 import해 버튼·입력·네비게이션 등 공통 요소를 일관된 스타일로 사용할 수 있습니다.
4
+
5
+ ## 설치
6
+
7
+ ```bash
8
+ pnpm add @uniai-fe/uds-foundation @uniai-fe/uds-primitives
9
+ ```
10
+
11
+ ```ts
12
+ // next.config.ts
13
+ const nextConfig = {
14
+ transpilePackages: ["@uniai-fe/uds-foundation", "@uniai-fe/uds-primitives"],
15
+ };
16
+ export default nextConfig;
17
+ ```
18
+
19
+ 앱 루트에서 foundation CSS를 1회 import 하면 모든 primitives가 토큰을 공유합니다.
20
+
21
+ ```ts
22
+ import "@uniai-fe/uds-foundation/css";
23
+ ```
24
+
25
+ ## 사용 예시
26
+
27
+ ```tsx
28
+ import { Button } from "@uniai-fe/uds-primitives";
29
+
30
+ export default function Page() {
31
+ return <Button intent="primary">확인</Button>;
32
+ }
33
+ ```
34
+
35
+ 모든 컴포넌트는 `className`/`data-*` 기반으로 override가 가능하며, 내부 슬롯(prefix/suffix/iconSlot 등)은 `CONTEXT-*.md`에 정의된 API를 따릅니다.
36
+
37
+ ## 구조
38
+
39
+ ```plaintext
40
+ src/components/{category}/
41
+ markup/ // 컴포넌트 구현
42
+ types/ // 외부 노출 타입
43
+ styles/ // SCSS (foundation 토큰 기반)
44
+ hooks/ // 카테고리 전용 훅
45
+ ```
46
+
47
+ - 배럴(`components/{category}/index.tsx`)은 항상 `import "./index.scss"`를 포함합니다.
48
+ - 스타일은 foundation CSS 변수만 사용하며, Radix 기본 클래스 대신 `.button[data-intent=...]` 형태로 분기합니다.
49
+
50
+ ## 스크립트
51
+
52
+ - `pnpm module:lint`
53
+ - `pnpm module:typecheck`
54
+ - `pnpm module:build`
55
+
56
+ 루트에서는 `pnpm --filter @uniai-fe/uds-primitives <command>`로 실행할 수 있습니다.
57
+
58
+ ## 문서
59
+
60
+ - `CONTEXT.md` 및 `CONTEXT-*.md`: 각 컴포넌트의 상태/진행/디자인 근거
61
+ - `RADIX-SIZE-GUIDE.md`: primitives 사이즈 체계와 Radix 매핑 규칙
62
+
63
+ 필요한 컨텍스트를 확인한 뒤 컴포넌트를 import해 사용하면 됩니다.
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@uniai-fe/uds-primitives",
3
+ "version": "0.0.1",
4
+ "description": "UNIAI Design System; Primitives Components Package",
5
+ "type": "module",
6
+ "private": false,
7
+ "sideEffects": false,
8
+ "license": "MIT",
9
+ "homepage": "https://www.uniai.co.kr/",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "packageManager": "pnpm@10.25.0",
14
+ "engines": {
15
+ "node": ">=24",
16
+ "pnpm": ">=10"
17
+ },
18
+ "author": {
19
+ "name": "GraffitoRyu",
20
+ "email": "yth4135@naver.com",
21
+ "url": "https://github.com/GraffitoRyu"
22
+ },
23
+ "files": [
24
+ "src"
25
+ ],
26
+ "main": "./src/index.tsx",
27
+ "module": "./src/index.tsx",
28
+ "exports": {
29
+ ".": "./src/index.tsx"
30
+ },
31
+ "scripts": {
32
+ "format": "prettier --write .",
33
+ "format:check": "prettier --check .",
34
+ "lint": "eslint . --max-warnings=0",
35
+ "typecheck": "tsc --project tsconfig.build.json --noEmit",
36
+ "build": "pnpm format && pnpm typecheck",
37
+ "dev": "tsc --project tsconfig.build.json --watch --noEmit",
38
+ "module:lint": "pnpm lint",
39
+ "module:typecheck": "pnpm typecheck",
40
+ "module:build": "pnpm build",
41
+ "design-primitives:build": "pnpm run build",
42
+ "design-primitives:dev": "pnpm run dev"
43
+ },
44
+ "peerDependencies": {
45
+ "@uniai-fe/uds-foundation": "workspace:^0.0.1",
46
+ "@radix-ui/react-alert-dialog": "^1.1.15",
47
+ "@radix-ui/react-dialog": "^1.1.15",
48
+ "@radix-ui/react-visually-hidden": "^1.2.4",
49
+ "react": ">= 19",
50
+ "react-dom": ">= 19",
51
+ "react-hook-form": ">= 7"
52
+ },
53
+ "dependencies": {
54
+ "@mantine/core": "^8.3.9",
55
+ "@mantine/dates": "^8.3.9",
56
+ "@mantine/hooks": "^8.3.9",
57
+ "@radix-ui/react-checkbox": "^1.3.3",
58
+ "@radix-ui/react-tabs": "^1.1.3",
59
+ "@radix-ui/react-toggle-group": "^1.1.3",
60
+ "@radix-ui/react-primitive": "^2.1.4",
61
+ "@radix-ui/react-radio-group": "^1.3.8",
62
+ "@radix-ui/react-slot": "^1.2.4",
63
+ "@radix-ui/themes": "^3.2.1",
64
+ "@uniai-fe/util-functions": "workspace:*",
65
+ "clsx": "^2.1.1",
66
+ "dayjs": "^1.11.19"
67
+ },
68
+ "devDependencies": {
69
+ "@radix-ui/react-alert-dialog": "^1.1.15",
70
+ "@radix-ui/react-dialog": "^1.1.15",
71
+ "@radix-ui/react-visually-hidden": "^1.2.4",
72
+ "@svgr/webpack": "^8.1.0",
73
+ "@types/node": "^24.10.2",
74
+ "@types/react": "^19.2.7",
75
+ "@types/react-dom": "^19.2.3",
76
+ "@uniai-fe/eslint-config": "workspace:*",
77
+ "@uniai-fe/next-devkit": "workspace:*",
78
+ "@uniai-fe/tsconfig": "workspace:*",
79
+ "eslint": "^9.39.1",
80
+ "prettier": "^3.7.4",
81
+ "react-hook-form": "^7.68.0",
82
+ "sass": "^1.95.0",
83
+ "typescript": "~5.9.3"
84
+ }
85
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(alternate): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * alternate 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
+ */
4
+ export * from "./markup";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(alternate): SOT 및 사용자 제약에 따라 컴포넌트를 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ /* TODO(alternate): 스타일을 SOT 토큰 값으로 정의한다. */
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(alternate): variant/slot 타입 정의를 작성한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(alternate): 토큰 매핑과 클래스명 유틸을 구현한다.
3
+ */
4
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(badge): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * badge 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
+ */
4
+ import "./index.scss";
5
+
6
+ export * from "./markup";
@@ -0,0 +1,51 @@
1
+ import { forwardRef } from "react";
2
+ import type { BadgeProps } from "../types";
3
+ import { composeBadgeClassName } from "../utils";
4
+
5
+ /**
6
+ * Badge 컴포넌트; size/style/intent 축을 data attribute로 노출한다.
7
+ */
8
+ const Badge = forwardRef<HTMLElementTagNameMap["figure"], BadgeProps>(
9
+ (
10
+ {
11
+ children,
12
+ size = "xsmall",
13
+ style = "fill",
14
+ intent = "primary",
15
+ tone = "primary",
16
+ className,
17
+ inlineStyle,
18
+ ...restProps
19
+ },
20
+ ref,
21
+ ) => {
22
+ const combinedClassName = composeBadgeClassName({ size, style, className });
23
+ // dot 스타일은 실제 원형 포인트 요소를 별도로 렌더링한다.
24
+ const renderDot =
25
+ style === "dot" ? (
26
+ <span className="badge-dot" aria-hidden="true" />
27
+ ) : null;
28
+ const hasLabel = Boolean(children);
29
+
30
+ return (
31
+ <figure
32
+ {...restProps}
33
+ ref={ref}
34
+ className={combinedClassName}
35
+ style={inlineStyle}
36
+ data-size={size}
37
+ data-style={style}
38
+ data-intent={style === "dot" ? undefined : intent}
39
+ data-tone={style === "dot" ? tone : undefined}
40
+ data-has-label={hasLabel ? "true" : undefined}
41
+ >
42
+ {renderDot}
43
+ {children ? <span className="badge-label">{children}</span> : null}
44
+ </figure>
45
+ );
46
+ },
47
+ );
48
+
49
+ Badge.displayName = "Badge";
50
+
51
+ export { Badge };
@@ -0,0 +1 @@
1
+ export { Badge } from "./Badge";
@@ -0,0 +1,189 @@
1
+ @use "@uniai-fe/uds-foundation/css";
2
+
3
+ /* Badge 토큰을 foundation 변수에 맞춰 한 번 더 래핑해 override 여지를 둔다. */
4
+ :where(.radix-themes, .theme-root, :root) {
5
+ --theme-badge-height-xsmall: var(--theme-size-small-1, 20px);
6
+ --theme-badge-height-small: var(--theme-size-small-2, 24px);
7
+ --theme-badge-padding-inline-xsmall: var(--spacing-padding-3, 6px);
8
+ --theme-badge-padding-inline-small: var(--spacing-padding-4, 8px);
9
+ --theme-badge-radius: var(--theme-radius-medium-1, 6px);
10
+ --theme-badge-font-family: var(--font-caption-medium-family, inherit);
11
+ --theme-badge-font-size: var(--font-caption-medium-size, 11px);
12
+ --theme-badge-font-weight: var(--font-caption-medium-weight, 400);
13
+ --theme-badge-line-height: var(--font-caption-medium-line-height, 1.5);
14
+ --theme-badge-letter-spacing: var(--font-caption-medium-letter-spacing, 0);
15
+ --theme-badge-dot-size: var(--spacing-gap-3, 8px);
16
+ }
17
+
18
+ .badge {
19
+ --badge-height: var(--theme-badge-height-xsmall);
20
+ --badge-padding-inline: var(--theme-badge-padding-inline-xsmall);
21
+ --badge-gap: var(--spacing-gap-1, 4px);
22
+ --badge-fill-bg-color: var(--color-primary-default, #1a6aff);
23
+ --badge-fill-label-color: var(--color-common-100, #ffffff);
24
+ --badge-outline-border-color: var(--color-primary-default, #1a6aff);
25
+ --badge-outline-label-color: var(--color-primary-default, #1a6aff);
26
+ --badge-dot-color: var(--color-primary-default, #1a6aff);
27
+ --badge-dot-size: var(--theme-badge-dot-size);
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ gap: var(--badge-gap);
32
+ height: var(--badge-height);
33
+ padding-inline: var(--badge-padding-inline);
34
+ padding-block: 0;
35
+ border-radius: var(--theme-badge-radius);
36
+ border: 0;
37
+ font-family: var(--theme-badge-font-family);
38
+ font-size: var(--theme-badge-font-size);
39
+ font-weight: var(--theme-badge-font-weight);
40
+ line-height: 1;
41
+ letter-spacing: var(--theme-badge-letter-spacing);
42
+ white-space: nowrap;
43
+ box-sizing: border-box;
44
+ margin: 0;
45
+ width: fit-content;
46
+ }
47
+
48
+ .badge:where([data-size="small"]) {
49
+ --badge-height: var(--theme-badge-height-small);
50
+ --badge-padding-inline: var(--theme-badge-padding-inline-small);
51
+ }
52
+
53
+ .badge:where([data-style="fill"]) {
54
+ background-color: var(--badge-fill-bg-color);
55
+ color: var(--badge-fill-label-color);
56
+ border-color: transparent;
57
+ }
58
+
59
+ .badge:where([data-style="outlined"]) {
60
+ background-color: var(--color-bg-surface-static-white, #ffffff);
61
+ border-width: 1px;
62
+ border-style: solid;
63
+ border-color: var(--badge-outline-border-color);
64
+ color: var(--badge-outline-label-color);
65
+ }
66
+
67
+ .badge:where([data-style="dot"]) {
68
+ padding: 0;
69
+ border-width: 0;
70
+ width: var(--badge-dot-size);
71
+ height: var(--badge-dot-size);
72
+ min-width: 0;
73
+ border-radius: var(--badge-dot-size);
74
+ background-color: transparent;
75
+ }
76
+
77
+ .badge-dot {
78
+ width: var(--badge-dot-size);
79
+ height: var(--badge-dot-size);
80
+ border-radius: 999px;
81
+ background-color: var(--badge-dot-color);
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .badge-label {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ color: inherit;
89
+ line-height: 1;
90
+ }
91
+
92
+ .badge:where([data-style="dot"]) .badge-label {
93
+ font-size: 0;
94
+ line-height: 0;
95
+ height: 0;
96
+ width: 0;
97
+ overflow: hidden;
98
+ }
99
+
100
+ /* fill intent 별 배경/라벨 컬러 정의 */
101
+ .badge:where([data-style="fill"][data-intent="primary"]) {
102
+ --badge-fill-bg-color: var(--color-primary-default, #1a6aff);
103
+ --badge-fill-label-color: var(--color-common-100, #ffffff);
104
+ }
105
+
106
+ .badge:where([data-style="fill"][data-intent="secondary"]) {
107
+ --badge-fill-bg-color: var(--color-secondary-default, #e5eeff);
108
+ --badge-fill-label-color: var(--color-primary-default, #1a6aff);
109
+ }
110
+
111
+ .badge:where([data-style="fill"][data-intent="teritary"]) {
112
+ --badge-fill-bg-color: var(--color-cool-gray-10, #18191b);
113
+ --badge-fill-label-color: var(--color-common-100, #ffffff);
114
+ }
115
+
116
+ .badge:where([data-style="fill"][data-intent="gray"]) {
117
+ --badge-fill-bg-color: var(--color-bg-surface-neutral, #f2f2f2);
118
+ --badge-fill-label-color: var(--color-label-neutral, #797e86);
119
+ }
120
+
121
+ .badge:where([data-style="fill"][data-intent="green"]) {
122
+ --badge-fill-bg-color: var(--color-green-95, #e9fcef);
123
+ --badge-fill-label-color: var(--color-green-45, #1dc956);
124
+ }
125
+
126
+ .badge:where([data-style="fill"][data-intent="yellow"]) {
127
+ --badge-fill-bg-color: var(--color-yellow-95, #fefae7);
128
+ --badge-fill-label-color: var(--color-yellow-45, #dab80b);
129
+ }
130
+
131
+ .badge:where([data-style="fill"][data-intent="orange"]) {
132
+ --badge-fill-bg-color: var(--color-orange-95, #ffeee5);
133
+ --badge-fill-label-color: var(--color-orange-55, #ff661a);
134
+ }
135
+
136
+ .badge:where([data-style="fill"][data-intent="red"]) {
137
+ --badge-fill-bg-color: var(--color-red-95, #fde8e7);
138
+ --badge-fill-label-color: var(--color-red-55, #f43625);
139
+ }
140
+
141
+ /* outlined intent 별 border/label 컬러 정의 */
142
+ .badge:where([data-style="outlined"][data-intent="primary"]) {
143
+ --badge-outline-border-color: var(--color-primary-default, #1a6aff);
144
+ --badge-outline-label-color: var(--color-primary-default, #1a6aff);
145
+ }
146
+
147
+ .badge:where([data-style="outlined"][data-intent="secondary"]) {
148
+ --badge-outline-border-color: var(--color-border-standard-blue, #ccdeff);
149
+ --badge-outline-label-color: var(--color-primary-strong, #0050e5);
150
+ }
151
+
152
+ .badge:where([data-style="outlined"][data-intent="teritary"]) {
153
+ --badge-outline-border-color: var(--color-border-standard-heavy, #313235);
154
+ --badge-outline-label-color: var(--color-label-strong, #18191b);
155
+ }
156
+
157
+ .badge:where([data-style="outlined"][data-intent="gray"]) {
158
+ --badge-outline-border-color: var(--color-border-standard-assistive, #e4e5e7);
159
+ --badge-outline-label-color: var(--color-label-standard, #3d3f43);
160
+ }
161
+
162
+ .badge:where([data-style="outlined"][data-intent="green"]) {
163
+ --badge-outline-border-color: var(--color-green-45, #1dc956);
164
+ --badge-outline-label-color: var(--color-green-45, #1dc956);
165
+ }
166
+
167
+ .badge:where([data-style="outlined"][data-intent="yellow"]) {
168
+ --badge-outline-border-color: var(--color-yellow-60, #f5d63d);
169
+ --badge-outline-label-color: var(--color-yellow-45, #dab80b);
170
+ }
171
+
172
+ .badge:where([data-style="outlined"][data-intent="orange"]) {
173
+ --badge-outline-border-color: var(--color-orange-70, #ff9966);
174
+ --badge-outline-label-color: var(--color-orange-55, #ff661a);
175
+ }
176
+
177
+ .badge:where([data-style="outlined"][data-intent="red"]) {
178
+ --badge-outline-border-color: var(--color-red-70, #f7796e);
179
+ --badge-outline-label-color: var(--color-red-55, #f43625);
180
+ }
181
+
182
+ /* dot tone 별 포인트 컬러 */
183
+ .badge:where([data-style="dot"][data-tone="primary"]) {
184
+ --badge-dot-color: var(--color-primary-default, #1a6aff);
185
+ }
186
+
187
+ .badge:where([data-style="dot"][data-tone="feedback"]) {
188
+ --badge-dot-color: var(--color-red-55, #f43625);
189
+ }
@@ -0,0 +1,55 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from "react";
2
+
3
+ export const BADGE_SIZES = ["xsmall", "small"] as const;
4
+ export const BADGE_STYLES = ["fill", "outlined", "dot"] as const;
5
+ export const BADGE_INTENTS = [
6
+ "primary",
7
+ "secondary",
8
+ "teritary",
9
+ "gray",
10
+ "green",
11
+ "yellow",
12
+ "orange",
13
+ "red",
14
+ ] as const;
15
+ export const BADGE_TONES = ["primary", "feedback"] as const;
16
+
17
+ export type BadgeSize = (typeof BADGE_SIZES)[number];
18
+ export type BadgeStyle = (typeof BADGE_STYLES)[number];
19
+ export type BadgeIntent = (typeof BADGE_INTENTS)[number];
20
+ export type BadgeTone = (typeof BADGE_TONES)[number];
21
+
22
+ type NativeFigureProps = Omit<ComponentPropsWithoutRef<"figure">, "style">;
23
+
24
+ export interface BadgeProps extends Omit<NativeFigureProps, "children"> {
25
+ /**
26
+ * 표시할 라벨. 생략하면 dot 스타일만 렌더링된다.
27
+ */
28
+ children?: ReactNode;
29
+ /**
30
+ * Figma size 축 (xsmall/small)
31
+ */
32
+ size?: BadgeSize;
33
+ /**
34
+ * style 축 (fill/outlined/dot)
35
+ */
36
+ style?: BadgeStyle;
37
+ /**
38
+ * fill/outlined 변형의 intent 축. dot 스타일에는 tone을 사용한다.
39
+ */
40
+ intent?: BadgeIntent;
41
+ /**
42
+ * dot 변형 전용 tone 축(primary/feedback).
43
+ */
44
+ tone?: BadgeTone;
45
+ /**
46
+ * 원본 figure 요소에 적용할 인라인 스타일.
47
+ */
48
+ inlineStyle?: ComponentPropsWithoutRef<"figure">["style"];
49
+ }
50
+
51
+ export interface BadgeClassNameOptions {
52
+ size: BadgeSize;
53
+ style: BadgeStyle;
54
+ className?: string;
55
+ }
@@ -0,0 +1,21 @@
1
+ import clsx from "clsx";
2
+ import type { BadgeClassNameOptions } from "../types";
3
+
4
+ const BADGE_CLASSNAME = "badge";
5
+
6
+ /**
7
+ * Badge 데이터 축을 className 구조로 묶어서 Story/override 시 일관되게 쓴다.
8
+ */
9
+ const composeBadgeClassName = ({
10
+ size,
11
+ style,
12
+ className,
13
+ }: BadgeClassNameOptions) =>
14
+ clsx(
15
+ BADGE_CLASSNAME,
16
+ `${BADGE_CLASSNAME}--size-${size}`,
17
+ `${BADGE_CLASSNAME}--style-${style}`,
18
+ className,
19
+ );
20
+
21
+ export { BADGE_CLASSNAME, composeBadgeClassName };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * TODO(button): 접근성/상태 계산 hook을 정의한다.
3
+ */
4
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * button 카테고리 배럴 placeholder: 실제 구현은 markup/ 하위에 추가한다.
3
+ */
4
+ import "./index.scss";
5
+
6
+ export * from "./markup";