@pantoia/ui 0.1.0 → 0.3.0

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 (68) hide show
  1. package/accordion/AGENTS.md +5 -5
  2. package/accordion/index.tsx +21 -11
  3. package/alert-dialog/AGENTS.md +7 -7
  4. package/alert-dialog/index.tsx +20 -10
  5. package/badge/AGENTS.md +5 -5
  6. package/badge/index.tsx +8 -8
  7. package/button/AGENTS.md +7 -7
  8. package/button/index.tsx +34 -34
  9. package/callout/AGENTS.md +4 -9
  10. package/callout/index.tsx +42 -22
  11. package/checkbox/AGENTS.md +5 -5
  12. package/checkbox/index.tsx +19 -16
  13. package/combobox/AGENTS.md +13 -13
  14. package/combobox/index.tsx +90 -42
  15. package/command-palette/AGENTS.md +3 -6
  16. package/command-palette/index.tsx +7 -3
  17. package/dialog/AGENTS.md +6 -6
  18. package/dialog/index.tsx +11 -5
  19. package/dist/index.js +200 -43
  20. package/drawer/AGENTS.md +5 -5
  21. package/drawer/index.tsx +8 -4
  22. package/dropdown-menu/AGENTS.md +12 -12
  23. package/dropdown-menu/index.tsx +27 -12
  24. package/field/AGENTS.md +8 -12
  25. package/field/index.tsx +39 -13
  26. package/input/AGENTS.md +4 -4
  27. package/input/index.tsx +5 -5
  28. package/kbd/AGENTS.md +1 -1
  29. package/kbd/index.tsx +6 -2
  30. package/label/AGENTS.md +1 -1
  31. package/label/index.tsx +3 -1
  32. package/number-field/AGENTS.md +6 -10
  33. package/number-field/index.tsx +26 -16
  34. package/package.json +13 -15
  35. package/palette.css +114 -0
  36. package/popover/AGENTS.md +4 -4
  37. package/popover/index.tsx +14 -4
  38. package/radio-group/AGENTS.md +8 -8
  39. package/radio-group/index.tsx +20 -16
  40. package/registry.json +9 -71
  41. package/select/AGENTS.md +10 -10
  42. package/select/index.tsx +46 -21
  43. package/switch/AGENTS.md +4 -4
  44. package/switch/index.tsx +13 -13
  45. package/table/AGENTS.md +9 -9
  46. package/table/index.tsx +16 -6
  47. package/tabs/AGENTS.md +5 -4
  48. package/tabs/index.module.css +2 -0
  49. package/tabs/index.tsx +22 -11
  50. package/tailwind.css +3 -0
  51. package/textarea/AGENTS.md +4 -4
  52. package/textarea/index.tsx +5 -5
  53. package/toast/AGENTS.md +5 -13
  54. package/toast/index.tsx +27 -33
  55. package/toggle/AGENTS.md +4 -4
  56. package/toggle/index.tsx +8 -7
  57. package/tokens.css +272 -0
  58. package/tooltip/AGENTS.md +2 -2
  59. package/tooltip/index.tsx +14 -4
  60. package/tv.ts +0 -1
  61. package/utilities.css +119 -0
  62. package/code-block/AGENTS.md +0 -60
  63. package/code-block/index.module.css +0 -46
  64. package/code-block/index.tsx +0 -127
  65. package/copy-button/AGENTS.md +0 -43
  66. package/copy-button/index.tsx +0 -119
  67. package/press.css +0 -37
  68. package/styles.css +0 -347
package/field/index.tsx CHANGED
@@ -1,6 +1,5 @@
1
1
  'use client'
2
2
 
3
- import { CircleAlert, CircleCheck, LoaderCircle } from 'lucide-react'
4
3
  import { createContext, use, useId, type ReactNode } from 'react'
5
4
 
6
5
  import { Label } from '../label'
@@ -66,6 +65,8 @@ export function Field({ invalid = false, layout, className, children }: FieldPro
66
65
  )
67
66
  }
68
67
 
68
+ const fieldLabel = tv({ base: 'group-has-disabled:text-fg-disabled' })
69
+
69
70
  export function FieldLabel({ className, children }: { className?: string, children?: ReactNode }) {
70
71
  const ctx = use(Context)
71
72
 
@@ -73,21 +74,25 @@ export function FieldLabel({ className, children }: { className?: string, childr
73
74
  <Label
74
75
  id={ctx?.labelId}
75
76
  htmlFor={ctx?.id}
76
- className={tv({ base: 'group-has-disabled:text-fg-disabled' })({ className })}
77
+ className={fieldLabel({ className })}
77
78
  >
78
79
  {children}
79
80
  </Label>
80
81
  )
81
82
  }
82
83
 
84
+ const fieldHelp = tv({
85
+ base: 'basis-full text-caption text-fg-subtle'
86
+ + ' group-has-disabled:text-fg-disabled',
87
+ })
88
+
83
89
  export function FieldHelp({ className, children }: { className?: string, children?: ReactNode }) {
84
90
  const ctx = use(Context)
85
91
 
86
92
  return (
87
93
  <p
88
94
  id={ctx?.messageId}
89
- className={tv({ base: 'basis-full text-caption text-fg-subtle'
90
- + ' group-has-disabled:text-fg-disabled' })({ className })}
95
+ className={fieldHelp({ className })}
91
96
  >
92
97
  {children}
93
98
  </p>
@@ -100,6 +105,11 @@ export interface FieldErrorProps {
100
105
  children?: ReactNode
101
106
  }
102
107
 
108
+ const fieldError = tv({
109
+ base: 'basis-full text-caption text-fg-danger'
110
+ + ' flex items-start gap-tight',
111
+ })
112
+
103
113
  export function FieldError({ items, className, children }: FieldErrorProps) {
104
114
  const ctx = use(Context)
105
115
  const list = items == null ? [] : Array.isArray(items) ? items : [items]
@@ -107,10 +117,13 @@ export function FieldError({ items, className, children }: FieldErrorProps) {
107
117
  return (
108
118
  <p
109
119
  id={ctx?.messageId}
110
- className={tv({ base: 'basis-full text-caption text-danger'
111
- + ' flex items-start gap-tight' })({ className })}
120
+ className={fieldError({ className })}
112
121
  >
113
- <CircleAlert className="mt-0.5 size-4 shrink-0" />
122
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="mt-0.5 size-4 shrink-0">
123
+ <circle cx="12" cy="12" r="10" />
124
+ <line x1="12" x2="12" y1="8" y2="12" />
125
+ <line x1="12" x2="12.01" y1="16" y2="16" />
126
+ </svg>
114
127
  <span className="flex min-w-0 flex-col gap-0.5">
115
128
  {children ?? list.map(t => <span key={t}>{t}</span>)}
116
129
  </span>
@@ -118,31 +131,44 @@ export function FieldError({ items, className, children }: FieldErrorProps) {
118
131
  )
119
132
  }
120
133
 
134
+ const fieldPending = tv({
135
+ base: 'basis-full text-caption text-fg-subtle'
136
+ + ' flex items-start gap-tight',
137
+ })
138
+
121
139
  export function FieldPending({ className, children }: { className?: string, children?: ReactNode }) {
122
140
  const ctx = use(Context)
123
141
 
124
142
  return (
125
143
  <p
126
144
  id={ctx?.messageId}
127
- className={tv({ base: 'basis-full text-caption text-fg-subtle'
128
- + ' flex items-start gap-tight' })({ className })}
145
+ className={fieldPending({ className })}
129
146
  >
130
- <LoaderCircle className="mt-0.5 size-4 shrink-0 animate-spin motion-reduce:animate-none" />
147
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="mt-0.5 size-4 shrink-0 animate-spin motion-reduce:animate-none">
148
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
149
+ </svg>
131
150
  <span className="min-w-0">{children}</span>
132
151
  </p>
133
152
  )
134
153
  }
135
154
 
155
+ const fieldSuccess = tv({
156
+ base: 'basis-full text-caption text-fg-success'
157
+ + ' flex items-start gap-tight',
158
+ })
159
+
136
160
  export function FieldSuccess({ className, children }: { className?: string, children?: ReactNode }) {
137
161
  const ctx = use(Context)
138
162
 
139
163
  return (
140
164
  <p
141
165
  id={ctx?.messageId}
142
- className={tv({ base: 'basis-full text-caption text-success'
143
- + ' flex items-start gap-tight' })({ className })}
166
+ className={fieldSuccess({ className })}
144
167
  >
145
- <CircleCheck className="mt-0.5 size-4 shrink-0" />
168
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="mt-0.5 size-4 shrink-0">
169
+ <circle cx="12" cy="12" r="10" />
170
+ <path d="m16 9-5.5 5.5L8 12" />
171
+ </svg>
146
172
  <span className="min-w-0">{children}</span>
147
173
  </p>
148
174
  )
package/input/AGENTS.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  받기 npx @pantoia/ui add input
5
5
  층 ui
6
- 파일 input/index.tsx · input/AGENTS.md
6
+ 파일 input/index.tsx, input/AGENTS.md
7
7
  ```
8
8
 
9
9
  ## 부위
@@ -23,12 +23,12 @@
23
23
  | `surface` | `'bare' \| 'well'` | |
24
24
  | `size` | `'sm' \| 'md' \| 'lg'` | |
25
25
 
26
- ## 변형 — `input`
26
+ ## 변형 - `input`
27
27
 
28
28
  | 축 | 값 | 기본 |
29
29
  |---|---|---|
30
- | `surface` | `bare` · `well` | `bare` |
31
- | `size` | `sm` · `md` · `lg` | `md` |
30
+ | `surface` | `bare`, `well` | `bare` |
31
+ | `size` | `sm`, `md`, `lg` | `md` |
32
32
 
33
33
  ## 함께 받는 것
34
34
 
package/input/index.tsx CHANGED
@@ -5,16 +5,16 @@ import { tv } from '../tv'
5
5
  export const input = tv({
6
6
  base: 'w-full rounded-r8 text-value text-fg'
7
7
  + ' placeholder:text-fg-placeholder'
8
- + ' invalid:ring-2 invalid:ring-danger'
8
+ + ' invalid:ring-2 invalid:ring-line-danger'
9
9
  + ' read-only:text-fg-muted'
10
10
  + ' transition-state'
11
- + ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
12
- + ' disabled:bg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',
11
+ + ' focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus-ring'
12
+ + ' disabled:bg-bg-neutral-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',
13
13
 
14
14
  variants: {
15
15
  surface: {
16
- bare: 'bg-transparent ring-1 ring-inset ring-line-strong read-only:bg-well',
17
- well: 'bg-well',
16
+ bare: 'bg-transparent ring-1 ring-inset ring-line-strong read-only:bg-bg-well',
17
+ well: 'bg-bg-well',
18
18
  },
19
19
  size: {
20
20
  sm: 'h-8 px-3',
package/kbd/AGENTS.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  받기 npx @pantoia/ui add kbd
5
5
  층 ui
6
- 파일 kbd/index.tsx · kbd/AGENTS.md
6
+ 파일 kbd/index.tsx, kbd/AGENTS.md
7
7
  ```
8
8
 
9
9
  ## 부위
package/kbd/index.tsx CHANGED
@@ -2,12 +2,16 @@ import type { HTMLAttributes } from 'react'
2
2
 
3
3
  import { tv } from '../tv'
4
4
 
5
+ const kbd = tv({
6
+ base: 'bg-bg-well text-fg-muted text-micro rounded-r4 inline-flex items-center'
7
+ + ' border border-line px-1.5 py-0.5 font-mono',
8
+ })
9
+
5
10
  export function Kbd({ className, ...rest }: HTMLAttributes<HTMLElement>) {
6
11
  return (
7
12
  <kbd
8
13
  {...rest}
9
- className={tv({ base: 'bg-well text-fg-muted text-micro rounded-mark inline-flex items-center'
10
- + ' border border-line px-1.5 py-0.5 font-mono' })({ className })}
14
+ className={kbd({ className })}
11
15
  />
12
16
  )
13
17
  }
package/label/AGENTS.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  받기 npx @pantoia/ui add label
5
5
  층 ui
6
- 파일 label/index.tsx · label/AGENTS.md
6
+ 파일 label/index.tsx, label/AGENTS.md
7
7
  ```
8
8
 
9
9
  ## 부위
package/label/index.tsx CHANGED
@@ -3,11 +3,13 @@ import type { ComponentProps } from 'react'
3
3
 
4
4
  import { tv } from '../tv'
5
5
 
6
+ const labelText = tv({ base: 'text-label text-fg' })
7
+
6
8
  export function Label({ className, ...rest }: ComponentProps<typeof LabelPrimitive.Root>) {
7
9
  return (
8
10
  <LabelPrimitive.Root
9
11
  {...rest}
10
- className={tv({ base: 'text-label text-fg' })({ className })}
12
+ className={labelText({ className })}
11
13
  />
12
14
  )
13
15
  }
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  받기 npx @pantoia/ui add number-field
5
5
  층 ui
6
- 파일 number-field/index.tsx · number-field/AGENTS.md
6
+ 파일 number-field/index.tsx, number-field/AGENTS.md
7
7
  ```
8
8
 
9
9
  ## 부위
@@ -34,20 +34,16 @@
34
34
  | `readOnly` | `boolean` | |
35
35
  | `className` | `string` | |
36
36
 
37
- ## 변형 — `numberField`
37
+ ## 변형 - `numberField`
38
38
 
39
- 슬롯 `root` · `step` · `input`
39
+ 슬롯 `root`, `step`, `input`
40
40
 
41
41
  | 축 | 값 | 기본 |
42
42
  |---|---|---|
43
- | `surface` | `bare` · `well` | `bare` |
44
- | `size` | `sm` · `md` · `lg` | `md` |
45
- | `readonly` | `true` | — |
43
+ | `surface` | `bare`, `well` | `bare` |
44
+ | `size` | `sm`, `md`, `lg` | `md` |
45
+ | `readonly` | `true` | - |
46
46
 
47
47
  ## 함께 받는 것
48
48
 
49
49
  `tv`
50
-
51
- ## npm 의존
52
-
53
- `lucide-react`
@@ -1,6 +1,5 @@
1
1
  'use client'
2
2
 
3
- import { Minus, Plus } from 'lucide-react'
4
3
  import { useEffect, useRef, useState, type InputHTMLAttributes, type PointerEvent as ReactPointerEvent, type KeyboardEvent as ReactKeyboardEvent } from 'react'
5
4
 
6
5
  import { tv } from '../tv'
@@ -9,13 +8,14 @@ export const numberField = tv({
9
8
  slots: {
10
9
  root: 'flex items-center rounded-r8'
11
10
  + ' transition-state'
12
- + ' invalid-within:ring-2 invalid-within:ring-danger'
13
- + ' has-disabled:bg-disabled has-disabled:cursor-not-allowed'
11
+ + ' invalid-within:ring-2 invalid-within:ring-line-danger'
12
+ + ' has-disabled:bg-bg-neutral-disabled has-disabled:cursor-not-allowed'
14
13
  + ' has-focus-visible:outline-2 has-focus-visible:outline-offset-2'
15
- + ' has-focus-visible:outline-ring',
14
+ + ' has-focus-visible:outline-line-focus-ring',
16
15
  step: 'text-fg-muted grid shrink-0 place-items-center rounded-r8'
17
- + ' transition-state press press-shrink enabled:hover:bg-hover'
18
- + ' disabled:text-fg-disabled disabled:cursor-not-allowed',
16
+ + ' transition-state press press-shrink enabled:not-aria-disabled:hover:bg-bg-neutral-hover'
17
+ + ' disabled:text-fg-disabled disabled:cursor-not-allowed'
18
+ + ' aria-disabled:text-fg-disabled aria-disabled:cursor-not-allowed',
19
19
  input: 'text-value text-fg w-full min-w-0 bg-transparent text-center outline-none'
20
20
  + ' disabled:text-fg-disabled disabled:cursor-not-allowed',
21
21
  },
@@ -23,7 +23,7 @@ export const numberField = tv({
23
23
  variants: {
24
24
  surface: {
25
25
  bare: { root: 'bg-transparent ring-1 ring-inset ring-line-strong' },
26
- well: { root: 'bg-well' },
26
+ well: { root: 'bg-bg-well' },
27
27
  },
28
28
  size: {
29
29
  sm: { root: 'h-8', step: 'size-8' },
@@ -31,7 +31,7 @@ export const numberField = tv({
31
31
  lg: { root: 'h-10', step: 'size-10', input: 'text-t16' },
32
32
  },
33
33
  readonly: {
34
- true: { root: 'bg-well', input: 'text-fg-muted' },
34
+ true: { root: 'bg-bg-well', input: 'text-fg-muted' },
35
35
  },
36
36
  },
37
37
 
@@ -55,8 +55,11 @@ function snap(n: number, min: number, step: number) {
55
55
  function useHold(step: () => void, blocked: boolean) {
56
56
  const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
57
57
  const beat = useRef<ReturnType<typeof setInterval> | undefined>(undefined)
58
+ const latest = useRef(step)
58
59
  const [pressed, setPressed] = useState(false)
59
60
 
61
+ useEffect(() => { latest.current = step })
62
+
60
63
  const stop = () => {
61
64
  clearTimeout(timer.current)
62
65
  clearInterval(beat.current)
@@ -68,12 +71,12 @@ function useHold(step: () => void, blocked: boolean) {
68
71
  const onPointerDown = (e: ReactPointerEvent<HTMLButtonElement>) => {
69
72
  if (e.button !== 0 || blocked) return
70
73
 
71
- step()
74
+ latest.current()
72
75
  setPressed(true)
73
76
  e.currentTarget.setPointerCapture(e.pointerId)
74
77
 
75
78
  timer.current = setTimeout(() => {
76
- beat.current = setInterval(step, HOLD_EVERY)
79
+ beat.current = setInterval(() => { latest.current() }, HOLD_EVERY)
77
80
  }, HOLD_DELAY)
78
81
  }
79
82
 
@@ -132,8 +135,8 @@ export function NumberField(props: NumberFieldProps) {
132
135
  const atMin = current !== undefined && current <= min
133
136
  const atMax = current !== undefined && current >= max
134
137
 
135
- const down = useHold(() => { setTyping(null); set(from - step) }, blocked)
136
- const up = useHold(() => { setTyping(null); set(from + step) }, blocked)
138
+ const down = useHold(() => { setTyping(null); set(from - step) }, blocked || atMin)
139
+ const up = useHold(() => { setTyping(null); set(from + step) }, blocked || atMax)
137
140
 
138
141
  const onKeyDown = (e: ReactKeyboardEvent<HTMLInputElement>) => {
139
142
  if (blocked) return
@@ -180,7 +183,8 @@ export function NumberField(props: NumberFieldProps) {
180
183
  <button
181
184
  type="button"
182
185
  aria-label={decrementLabel}
183
- disabled={blocked || atMin}
186
+ disabled={blocked}
187
+ aria-disabled={atMin || undefined}
184
188
  data-pressed={down.pressed ? '' : undefined}
185
189
  className={ui.step()}
186
190
  onPointerDown={down.onPointerDown}
@@ -188,7 +192,9 @@ export function NumberField(props: NumberFieldProps) {
188
192
  onPointerCancel={down.onPointerCancel}
189
193
  onLostPointerCapture={down.onLostPointerCapture}
190
194
  >
191
- <Minus className="size-4" />
195
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
196
+ <path d="M5 12h14" />
197
+ </svg>
192
198
  </button>
193
199
 
194
200
  <input
@@ -212,7 +218,8 @@ export function NumberField(props: NumberFieldProps) {
212
218
  <button
213
219
  type="button"
214
220
  aria-label={incrementLabel}
215
- disabled={blocked || atMax}
221
+ disabled={blocked}
222
+ aria-disabled={atMax || undefined}
216
223
  data-pressed={up.pressed ? '' : undefined}
217
224
  className={ui.step()}
218
225
  onPointerDown={up.onPointerDown}
@@ -220,7 +227,10 @@ export function NumberField(props: NumberFieldProps) {
220
227
  onPointerCancel={up.onPointerCancel}
221
228
  onLostPointerCapture={up.onLostPointerCapture}
222
229
  >
223
- <Plus className="size-4" />
230
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className="size-4">
231
+ <path d="M5 12h14" />
232
+ <path d="M12 5v14" />
233
+ </svg>
224
234
  </button>
225
235
  </div>
226
236
  )
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pantoia/ui",
3
- "version": "0.1.0",
4
- "description": "Pantoia 디자인 시스템 — 소스를 복사해 가는 컴포넌트 스물여덟",
3
+ "version": "0.3.0",
4
+ "description": "Pantoia Design System",
5
5
  "homepage": "https://design.pantoia.com",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,14 +20,18 @@
20
20
  "dist",
21
21
  "tv.ts",
22
22
  "test-utils.ts",
23
- "styles.css",
24
- "press.css",
23
+ "tailwind.css",
24
+ "palette.css",
25
+ "tokens.css",
26
+ "utilities.css",
25
27
  "registry.json"
26
28
  ],
27
29
  "exports": {
28
30
  "./tv": "./tv.ts",
29
- "./styles.css": "./styles.css",
30
- "./press.css": "./press.css",
31
+ "./tailwind.css": "./tailwind.css",
32
+ "./palette.css": "./palette.css",
33
+ "./tokens.css": "./tokens.css",
34
+ "./utilities.css": "./utilities.css",
31
35
  "./registry.json": "./registry.json",
32
36
  "./test-utils": "./test-utils.ts",
33
37
  "./*": "./*/index.tsx"
@@ -39,17 +43,13 @@
39
43
  "access": "public"
40
44
  },
41
45
  "engines": {
42
- "node": ">=24"
46
+ "node": ">=20.11"
43
47
  },
44
48
  "scripts": {
45
49
  "prepack": "tsc cli/index.ts --ignoreConfig --outDir dist --target es2022 --module esnext --moduleResolution bundler --skipLibCheck --types node",
46
- "lint": "eslint . --max-warnings 0",
47
50
  "typecheck": "tsc --noEmit"
48
51
  },
49
52
  "dependencies": {
50
- "@fontsource/jetbrains-mono": "5.3.0",
51
- "lucide-react": "^1.39.0",
52
- "pretendard": "1.3.9",
53
53
  "radix-ui": "^1.6.7",
54
54
  "sonner": "^2.0.8",
55
55
  "tailwind-variants": "^3.3.1"
@@ -57,17 +57,15 @@
57
57
  "peerDependencies": {
58
58
  "react": "^19",
59
59
  "react-dom": "^19",
60
- "tailwindcss": "^4.3.3"
60
+ "tailwindcss": "^4.0.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@pantoia/eslint-config": "*",
64
63
  "@pantoia/typescript-config": "*",
65
64
  "@types/node": "26.4.0",
66
65
  "@types/react": "19.2.18",
67
66
  "@types/react-dom": "19.2.5",
68
- "eslint": "10.9.1",
69
67
  "react": "19.2.8",
70
68
  "react-dom": "19.2.8",
71
- "typescript": "6.0.3"
69
+ "typescript": "7.0.2"
72
70
  }
73
71
  }
package/palette.css ADDED
@@ -0,0 +1,114 @@
1
+ :root, .light {
2
+ --pds-gray-50:#f9fafb; --pds-gray-100:#f2f4f6; --pds-gray-200:#e5e8eb; --pds-gray-300:#d1d6db;
3
+ --pds-gray-400:#b0b8c1; --pds-gray-500:#8b95a1; --pds-gray-600:#6b7684; --pds-gray-700:#4e5968;
4
+ --pds-gray-800:#333d4b; --pds-gray-900:#191f28;
5
+
6
+ --pds-brand-50:#f5f1fd; --pds-brand-100:#e4d6ff; --pds-brand-200:#ceb0ff; --pds-brand-300:#bb8ffa;
7
+ --pds-brand-400:#aa74f2; --pds-brand-500:#9c63e4; --pds-brand-600:#8750c9; --pds-brand-700:#7543b0;
8
+ --pds-brand-800:#633997; --pds-brand-900:#54317f;
9
+
10
+ --pds-blue-50:#e8f3ff; --pds-blue-100:#c9e2ff; --pds-blue-200:#90c2ff; --pds-blue-300:#64a8ff;
11
+ --pds-blue-400:#4593fc; --pds-blue-500:#3182f6; --pds-blue-600:#2272eb; --pds-blue-700:#1b64da;
12
+ --pds-blue-800:#1957c2; --pds-blue-900:#194aa6;
13
+
14
+ --pds-green-50:#f0faf6; --pds-green-100:#aeefd5; --pds-green-200:#76e4b8; --pds-green-300:#3fd599;
15
+ --pds-green-400:#15c47e; --pds-green-500:#03b26c; --pds-green-600:#02a262; --pds-green-700:#029359;
16
+ --pds-green-800:#028450; --pds-green-900:#027648;
17
+
18
+ --pds-orange-50:#fff3e0; --pds-orange-100:#ffe0b0; --pds-orange-200:#ffcd80; --pds-orange-300:#ffbd51;
19
+ --pds-orange-400:#ffa927; --pds-orange-500:#fe9800; --pds-orange-600:#fb8800; --pds-orange-700:#f57800;
20
+ --pds-orange-800:#ed6700; --pds-orange-900:#e45600;
21
+
22
+ --pds-red-50:#ffeeee; --pds-red-100:#ffd4d6; --pds-red-200:#feafb4; --pds-red-300:#fb8890;
23
+ --pds-red-400:#f66570; --pds-red-500:#f04452; --pds-red-600:#e42939; --pds-red-700:#d22030;
24
+ --pds-red-800:#bc1b2a; --pds-red-900:#a51926;
25
+ }
26
+
27
+ .dark {
28
+ --pds-gray-50:#202027; --pds-gray-100:#2c2c35; --pds-gray-200:#3c3c47; --pds-gray-300:#4d4d59;
29
+ --pds-gray-400:#62626d; --pds-gray-500:#7e7e87; --pds-gray-600:#9e9ea4; --pds-gray-700:#c3c3c6;
30
+ --pds-gray-800:#e4e4e5; --pds-gray-900:#ffffff;
31
+
32
+ --pds-brand-50:#362949; --pds-brand-100:#462e66; --pds-brand-200:#583682; --pds-brand-300:#6b3fa0;
33
+ --pds-brand-400:#8451c4; --pds-brand-500:#9c63e4; --pds-brand-600:#ad75f7; --pds-brand-700:#c094ff;
34
+ --pds-brand-800:#d6baff; --pds-brand-900:#eadeff;
35
+
36
+ --pds-blue-50:#202c4d; --pds-blue-100:#23386a; --pds-blue-200:#25478c; --pds-blue-300:#265ab3;
37
+ --pds-blue-400:#2970d9; --pds-blue-500:#3485fa; --pds-blue-600:#449bff; --pds-blue-700:#61b0ff;
38
+ --pds-blue-800:#8fcdff; --pds-blue-900:#c8e7ff;
39
+
40
+ --pds-green-50:#153729; --pds-green-100:#135338; --pds-green-200:#136d47; --pds-green-300:#138a59;
41
+ --pds-green-400:#13a065; --pds-green-500:#16bb76; --pds-green-600:#26cf88; --pds-green-700:#4ee4a6;
42
+ --pds-green-800:#82f6c5; --pds-green-900:#ccffea;
43
+
44
+ --pds-orange-50:#3d2500; --pds-orange-100:#563200; --pds-orange-200:#804600; --pds-orange-300:#a85f00;
45
+ --pds-orange-400:#cf7200; --pds-orange-500:#f18600; --pds-orange-600:#fd9528; --pds-orange-700:#ffa861;
46
+ --pds-orange-800:#ffc39e; --pds-orange-900:#ffe4d6;
47
+
48
+ --pds-red-50:#3c2020; --pds-red-100:#562025; --pds-red-200:#7a242d; --pds-red-300:#9e2733;
49
+ --pds-red-400:#ca2f3d; --pds-red-500:#f04251; --pds-red-600:#fa616d; --pds-red-700:#fe818b;
50
+ --pds-red-800:#ffa8ad; --pds-red-900:#ffd1d3;
51
+ }
52
+
53
+ @theme inline {
54
+ --color-pds-gray-50: var(--pds-gray-50);
55
+ --color-pds-gray-100: var(--pds-gray-100);
56
+ --color-pds-gray-200: var(--pds-gray-200);
57
+ --color-pds-gray-300: var(--pds-gray-300);
58
+ --color-pds-gray-400: var(--pds-gray-400);
59
+ --color-pds-gray-500: var(--pds-gray-500);
60
+ --color-pds-gray-600: var(--pds-gray-600);
61
+ --color-pds-gray-700: var(--pds-gray-700);
62
+ --color-pds-gray-800: var(--pds-gray-800);
63
+ --color-pds-gray-900: var(--pds-gray-900);
64
+ --color-pds-brand-50: var(--pds-brand-50);
65
+ --color-pds-brand-100: var(--pds-brand-100);
66
+ --color-pds-brand-200: var(--pds-brand-200);
67
+ --color-pds-brand-300: var(--pds-brand-300);
68
+ --color-pds-brand-400: var(--pds-brand-400);
69
+ --color-pds-brand-500: var(--pds-brand-500);
70
+ --color-pds-brand-600: var(--pds-brand-600);
71
+ --color-pds-brand-700: var(--pds-brand-700);
72
+ --color-pds-brand-800: var(--pds-brand-800);
73
+ --color-pds-brand-900: var(--pds-brand-900);
74
+ --color-pds-blue-50: var(--pds-blue-50);
75
+ --color-pds-blue-100: var(--pds-blue-100);
76
+ --color-pds-blue-200: var(--pds-blue-200);
77
+ --color-pds-blue-300: var(--pds-blue-300);
78
+ --color-pds-blue-400: var(--pds-blue-400);
79
+ --color-pds-blue-500: var(--pds-blue-500);
80
+ --color-pds-blue-600: var(--pds-blue-600);
81
+ --color-pds-blue-700: var(--pds-blue-700);
82
+ --color-pds-blue-800: var(--pds-blue-800);
83
+ --color-pds-blue-900: var(--pds-blue-900);
84
+ --color-pds-green-50: var(--pds-green-50);
85
+ --color-pds-green-100: var(--pds-green-100);
86
+ --color-pds-green-200: var(--pds-green-200);
87
+ --color-pds-green-300: var(--pds-green-300);
88
+ --color-pds-green-400: var(--pds-green-400);
89
+ --color-pds-green-500: var(--pds-green-500);
90
+ --color-pds-green-600: var(--pds-green-600);
91
+ --color-pds-green-700: var(--pds-green-700);
92
+ --color-pds-green-800: var(--pds-green-800);
93
+ --color-pds-green-900: var(--pds-green-900);
94
+ --color-pds-orange-50: var(--pds-orange-50);
95
+ --color-pds-orange-100: var(--pds-orange-100);
96
+ --color-pds-orange-200: var(--pds-orange-200);
97
+ --color-pds-orange-300: var(--pds-orange-300);
98
+ --color-pds-orange-400: var(--pds-orange-400);
99
+ --color-pds-orange-500: var(--pds-orange-500);
100
+ --color-pds-orange-600: var(--pds-orange-600);
101
+ --color-pds-orange-700: var(--pds-orange-700);
102
+ --color-pds-orange-800: var(--pds-orange-800);
103
+ --color-pds-orange-900: var(--pds-orange-900);
104
+ --color-pds-red-50: var(--pds-red-50);
105
+ --color-pds-red-100: var(--pds-red-100);
106
+ --color-pds-red-200: var(--pds-red-200);
107
+ --color-pds-red-300: var(--pds-red-300);
108
+ --color-pds-red-400: var(--pds-red-400);
109
+ --color-pds-red-500: var(--pds-red-500);
110
+ --color-pds-red-600: var(--pds-red-600);
111
+ --color-pds-red-700: var(--pds-red-700);
112
+ --color-pds-red-800: var(--pds-red-800);
113
+ --color-pds-red-900: var(--pds-red-900);
114
+ }
package/popover/AGENTS.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  받기 npx @pantoia/ui add popover
5
5
  층 ui
6
- 파일 popover/index.tsx · popover/AGENTS.md
6
+ 파일 popover/index.tsx, popover/AGENTS.md
7
7
  ```
8
8
 
9
9
  ## 부위
@@ -11,9 +11,9 @@
11
11
  | 이름 | props |
12
12
  |---|---|
13
13
  | `Popover` | `PopoverProps` |
14
- | `PopoverTrigger` | — |
15
- | `PopoverAnchor` | — |
16
- | `PopoverClose` | — |
14
+ | `PopoverTrigger` | - |
15
+ | `PopoverAnchor` | - |
16
+ | `PopoverClose` | - |
17
17
  | `PopoverContent` | `PopoverContentProps` |
18
18
 
19
19
  ### `PopoverTrigger`
package/popover/index.tsx CHANGED
@@ -38,6 +38,12 @@ export interface PopoverContentProps extends Omit<ComponentProps<typeof PopoverP
38
38
  arrow?: boolean
39
39
  }
40
40
 
41
+ const popoverContent = tv({
42
+ base: 'float text-fg rounded-r16 z-overlay p-4'
43
+ + ' open:animate-enter closed:animate-exit'
44
+ + ' focus-visible:outline-none',
45
+ })
46
+
41
47
  export function PopoverContent({
42
48
  side = 'bottom',
43
49
  align = 'center',
@@ -54,12 +60,16 @@ export function PopoverContent({
54
60
  align={align}
55
61
  sideOffset={8}
56
62
  arrowPadding={16}
57
- className={tv({ base: 'bg-float text-fg rounded-float shadow-s3 z-overlay p-4'
58
- + ' open:animate-enter closed:animate-exit'
59
- + ' focus-visible:outline-none' })({ className })}
63
+ className={popoverContent({ className })}
60
64
  >
61
65
  {children}
62
- {arrow && <PopoverPrimitive.Arrow className="fill-float" width={12} height={6} />}
66
+ {arrow && (
67
+ <PopoverPrimitive.Arrow asChild width={12} height={6}>
68
+ <svg viewBox="0 0 12 6" preserveAspectRatio="none" className="fill-bg-panel -translate-y-px">
69
+ <path d="M0 0L4.58579 4.58579C5.36683 5.36683 6.63316 5.36684 7.41421 4.58579L12 0" className="stroke-line stroke-1" />
70
+ </svg>
71
+ </PopoverPrimitive.Arrow>
72
+ )}
63
73
  </PopoverPrimitive.Content>
64
74
  </PopoverPrimitive.Portal>
65
75
  )