@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
@@ -1,43 +0,0 @@
1
- # CopyButton
2
-
3
- ```
4
- 받기 npx @pantoia/ui add copy-button
5
- 층 derived
6
- 파일 copy-button/index.tsx · copy-button/AGENTS.md
7
- ```
8
-
9
- ## 부위
10
-
11
- | 이름 | props |
12
- |---|---|
13
- | `CopyButton` | `CopyButtonProps` |
14
-
15
- ## 도우미
16
-
17
- `useCopy(resetAfter = 2000)`
18
-
19
- ## props
20
-
21
- ### `CopyState`
22
-
23
- `'idle' | 'copied' | 'failed'`
24
-
25
- ### `CopyButtonProps`
26
-
27
- | 이름 | 타입 | 필수 |
28
- |---|---|---|
29
- | `value` | `string` | 예 |
30
- | `color` | `'primary' \| 'neutral' \| 'danger'` | |
31
- | `emphasis` | `'solid' \| 'weak' \| 'outline' \| 'ghost'` | |
32
- | `size` | `'sm' \| 'md' \| 'lg'` | |
33
- | `shape` | `'rect' \| 'square' \| 'pill' \| 'circle'` | |
34
- | `onCopied` | `(value: string) => void` | |
35
- | `onFailed` | `() => void` | |
36
- | `disabled` | `boolean` | |
37
- | `className` | `string` | |
38
- | `aria-label` | `string` | |
39
- | `children` | `ReactNode` | |
40
-
41
- ## 함께 받는 것
42
-
43
- `button`
@@ -1,119 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useRef, useState, type ReactNode } from 'react'
4
-
5
- import { Button } from '../button'
6
-
7
- export type CopyState = 'idle' | 'copied' | 'failed'
8
-
9
- function writeLegacy(value: string): boolean {
10
- const ta = document.createElement('textarea')
11
- ta.value = value
12
- ta.setAttribute('readonly', '')
13
- ta.style.cssText = 'position:fixed;top:0;left:0;opacity:0;pointer-events:none'
14
- document.body.appendChild(ta)
15
- ta.select()
16
-
17
- let ok: boolean
18
- try { ok = document.execCommand('copy') }
19
- catch { ok = false }
20
-
21
- ta.remove()
22
- return ok
23
- }
24
-
25
- async function write(value: string): Promise<boolean> {
26
- try {
27
- await navigator.clipboard.writeText(value)
28
- return true
29
- }
30
- catch {
31
- return writeLegacy(value)
32
- }
33
- }
34
-
35
- export function useCopy(resetAfter = 2000) {
36
- const [state, setState] = useState<CopyState>('idle')
37
- const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)
38
-
39
- useEffect(() => () => clearTimeout(timer.current), [])
40
-
41
- const copy = async (value: string) => {
42
- clearTimeout(timer.current)
43
- const next: CopyState = await write(value) ? 'copied' : 'failed'
44
- setState(next)
45
- timer.current = setTimeout(() => setState('idle'), resetAfter)
46
- return next
47
- }
48
-
49
- return { state, copy }
50
- }
51
-
52
- export interface CopyButtonProps {
53
- value: string
54
- color?: 'primary' | 'neutral' | 'danger'
55
- emphasis?: 'solid' | 'weak' | 'outline' | 'ghost'
56
- size?: 'sm' | 'md' | 'lg'
57
- shape?: 'rect' | 'square' | 'pill' | 'circle'
58
- onCopied?: (value: string) => void
59
- onFailed?: () => void
60
- disabled?: boolean
61
- className?: string
62
- 'aria-label'?: string
63
- children?: ReactNode
64
- }
65
-
66
- export function CopyButton({
67
- value,
68
- color = 'neutral',
69
- emphasis = 'outline',
70
- size = 'md',
71
- shape = 'rect',
72
- onCopied,
73
- onFailed,
74
- children,
75
- ...rest
76
- }: CopyButtonProps) {
77
- const { state, copy } = useCopy()
78
-
79
- const run = async () => {
80
- const result = await copy(value)
81
- if (result === 'copied') onCopied?.(value)
82
- else onFailed?.()
83
- }
84
-
85
- return (
86
- <Button {...rest} color={color} emphasis={emphasis} size={size} shape={shape} onClick={run}>
87
- <svg
88
- viewBox="0 0 24 24"
89
- fill="none"
90
- stroke="currentColor"
91
- strokeWidth="2"
92
- strokeLinecap="round"
93
- strokeLinejoin="round"
94
- aria-hidden="true"
95
- className="shrink-0"
96
- >
97
- {state === 'idle' && (
98
- <g>
99
- <rect x="8" y="8" width="14" height="14" rx="2" />
100
- <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
101
- </g>
102
- )}
103
-
104
- {state === 'copied' && (
105
- <path className="animate-draw motion-reduce:animate-none" d="M4 12l5 5L20 6" />
106
- )}
107
-
108
- {state === 'failed' && (
109
- <g className="animate-enter motion-reduce:animate-none">
110
- <path d="M18 6 6 18" />
111
- <path d="m6 6 12 12" />
112
- </g>
113
- )}
114
- </svg>
115
-
116
- {children}
117
- </Button>
118
- )
119
- }
package/press.css DELETED
@@ -1,37 +0,0 @@
1
- :root, .light { --pressed-dim: rgba(0,23,51,0.12); }
2
- .dark { --pressed-dim: rgba(209,209,253,0.14); }
3
-
4
- :root { --scale-press: 0.97; }
5
-
6
- @media (prefers-reduced-motion: reduce) {
7
- :root { --scale-press: 1; }
8
- }
9
-
10
- @utility press {
11
- position: relative;
12
-
13
- &::after {
14
- content: "";
15
- position: absolute;
16
- inset: 0;
17
- border-radius: inherit;
18
- background: var(--pressed-dim);
19
- opacity: 0;
20
- pointer-events: none;
21
- transition: opacity var(--dur-state) var(--ease-state);
22
- }
23
-
24
- &:active:not(:disabled)::after,
25
- &[data-pressed]:not(:disabled)::after {
26
- opacity: 1;
27
- transition-duration: 0s;
28
- }
29
- }
30
-
31
- @utility press-shrink {
32
- &:active:not(:disabled),
33
- &[data-pressed]:not(:disabled) {
34
- scale: var(--scale-press);
35
- transition-duration: 0s;
36
- }
37
- }
package/styles.css DELETED
@@ -1,347 +0,0 @@
1
- @import "pretendard/dist/web/variable/pretendardvariable-dynamic-subset.css";
2
- @import "./press.css";
3
-
4
- @custom-variant dark (&:where(.dark, .dark *):not(:where(.light, .light *)));
5
-
6
- @custom-variant current (&[aria-current="page"]);
7
-
8
- @custom-variant invalid (&[aria-invalid="true"]);
9
-
10
- @custom-variant invalid-within (&:has([aria-invalid="true"]));
11
-
12
- @custom-variant in-invalid (&:where([aria-invalid="true"] *));
13
-
14
- @custom-variant described (&:has([data-description]));
15
-
16
- @custom-variant checked (&:where([data-state="checked"], [data-state="indeterminate"]));
17
- @custom-variant open (&[data-state="open"]);
18
- @custom-variant closed (&[data-state="closed"]);
19
- @custom-variant on (&[data-state="on"]);
20
- @custom-variant selected (&[data-selected]:not([data-selected="false"]));
21
- @custom-variant unset (&[data-placeholder]);
22
-
23
- @custom-variant menu-gutter (.menu:has(.menu-lead) &:not(:has(.menu-lead)));
24
-
25
- :root, .light {
26
- --gray-50:#f9fafb; --gray-100:#f2f4f6; --gray-200:#e5e8eb; --gray-300:#d1d6db;
27
- --gray-400:#b0b8c1; --gray-500:#8b95a1; --gray-600:#6b7684; --gray-700:#4e5968;
28
- --gray-800:#333d4b; --gray-900:#191f28;
29
-
30
- --purple-50:#f5f1fd; --purple-100:#e4d6ff; --purple-200:#ceb0ff; --purple-300:#bb8ffa;
31
- --purple-400:#aa74f2; --purple-500:#9c63e4; --purple-600:#8750c9; --purple-700:#7543b0;
32
- --purple-800:#633997; --purple-900:#54317f;
33
-
34
- --blue-50:#e8f3ff; --blue-100:#c9e2ff; --blue-200:#90c2ff; --blue-300:#64a8ff;
35
- --blue-400:#4593fc; --blue-500:#3182f6; --blue-600:#2272eb; --blue-700:#1b64da;
36
- --blue-800:#1957c2; --blue-900:#194aa6;
37
-
38
- --green-50:#f0faf6; --green-100:#aeefd5; --green-200:#76e4b8; --green-300:#3fd599;
39
- --green-400:#15c47e; --green-500:#03b26c; --green-600:#02a262; --green-700:#029359;
40
- --green-800:#028450; --green-900:#027648;
41
-
42
- --orange-50:#fff3e0; --orange-100:#ffe0b0; --orange-200:#ffcd80; --orange-300:#ffbd51;
43
- --orange-400:#ffa927; --orange-500:#fe9800; --orange-600:#fb8800; --orange-700:#f57800;
44
- --orange-800:#ed6700; --orange-900:#e45600;
45
-
46
- --red-50:#ffeeee; --red-100:#ffd4d6; --red-200:#feafb4; --red-300:#fb8890;
47
- --red-400:#f66570; --red-500:#f04452; --red-600:#e42939; --red-700:#d22030;
48
- --red-800:#bc1b2a; --red-900:#a51926;
49
- }
50
-
51
- .dark {
52
- --gray-50:#202027; --gray-100:#2c2c35; --gray-200:#3c3c47; --gray-300:#4d4d59;
53
- --gray-400:#62626d; --gray-500:#7e7e87; --gray-600:#9e9ea4; --gray-700:#c3c3c6;
54
- --gray-800:#e4e4e5; --gray-900:#ffffff;
55
-
56
- --purple-50:#362949; --purple-100:#462e66; --purple-200:#583682; --purple-300:#6b3fa0;
57
- --purple-400:#8451c4; --purple-500:#9c63e4; --purple-600:#ad75f7; --purple-700:#c094ff;
58
- --purple-800:#d6baff; --purple-900:#eadeff;
59
-
60
- --blue-50:#202c4d; --blue-100:#23386a; --blue-200:#25478c; --blue-300:#265ab3;
61
- --blue-400:#2970d9; --blue-500:#3485fa; --blue-600:#449bff; --blue-700:#61b0ff;
62
- --blue-800:#8fcdff; --blue-900:#c8e7ff;
63
-
64
- --green-50:#153729; --green-100:#135338; --green-200:#136d47; --green-300:#138a59;
65
- --green-400:#13a065; --green-500:#16bb76; --green-600:#26cf88; --green-700:#4ee4a6;
66
- --green-800:#82f6c5; --green-900:#ccffea;
67
-
68
- --orange-50:#3d2500; --orange-100:#563200; --orange-200:#804600; --orange-300:#a85f00;
69
- --orange-400:#cf7200; --orange-500:#f18600; --orange-600:#fd9528; --orange-700:#ffa861;
70
- --orange-800:#ffc39e; --orange-900:#ffe4d6;
71
-
72
- --red-50:#3c2020; --red-100:#562025; --red-200:#7a242d; --red-300:#9e2733;
73
- --red-400:#ca2f3d; --red-500:#f04251; --red-600:#fa616d; --red-700:#fe818b;
74
- --red-800:#ffa8ad; --red-900:#ffd1d3;
75
- }
76
-
77
- @theme {
78
- --color-*: initial;
79
- --text-*: initial;
80
- --leading-*: initial;
81
- --font-weight-*: initial;
82
- }
83
-
84
- @theme {
85
- --font-weight-normal: 400;
86
- --font-weight-medium: 500;
87
- --font-weight-bold: 700;
88
-
89
- --font-sans: "Pretendard Variable", "Pretendard", -apple-system, system-ui, sans-serif;
90
- --font-mono: "JetBrains Mono", "Pretendard Variable", "Pretendard", ui-monospace, monospace;
91
-
92
- --text-display: 28px; --text-display--line-height: 38px; --text-display--font-weight: 700;
93
- --text-title: 24px; --text-title--line-height: 32px; --text-title--font-weight: 700;
94
- --text-section: 20px; --text-section--line-height: 27px; --text-section--font-weight: 700;
95
- --text-subsection: 16px; --text-subsection--line-height: 22px; --text-subsection--font-weight: 700;
96
- --text-label: 14px; --text-label--line-height: 19px; --text-label--font-weight: 500;
97
- --text-caption: 13px; --text-caption--line-height: 18px; --text-caption--font-weight: 400;
98
- --text-micro: 11px; --text-micro--line-height: 15px; --text-micro--font-weight: 500;
99
-
100
- --text-t10: 10px; --text-t10--line-height: 14px; --text-t10--font-weight: 400;
101
- --text-t11: 11px; --text-t11--line-height: 15px; --text-t11--font-weight: 400;
102
- --text-t12: 12px; --text-t12--line-height: 16px; --text-t12--font-weight: 400;
103
- --text-t13: 13px; --text-t13--line-height: 18px; --text-t13--font-weight: 400;
104
- --text-t14: 14px; --text-t14--line-height: 19px; --text-t14--font-weight: 400;
105
- --text-t16: 16px; --text-t16--line-height: 22px; --text-t16--font-weight: 400;
106
- --text-t18: 18px; --text-t18--line-height: 24px; --text-t18--font-weight: 400;
107
- --text-t20: 20px; --text-t20--line-height: 27px; --text-t20--font-weight: 400;
108
- --text-t24: 24px; --text-t24--line-height: 32px; --text-t24--font-weight: 400;
109
- --text-t28: 28px; --text-t28--line-height: 38px; --text-t28--font-weight: 400;
110
- --text-t32: 32px; --text-t32--line-height: 43px; --text-t32--font-weight: 400;
111
- --text-t40: 40px; --text-t40--line-height: 54px; --text-t40--font-weight: 400;
112
- --text-t48: 48px; --text-t48--line-height: 65px; --text-t48--font-weight: 400;
113
- --text-t56: 56px; --text-t56--line-height: 76px; --text-t56--font-weight: 400;
114
- --text-t64: 64px; --text-t64--line-height: 86px; --text-t64--font-weight: 400;
115
- }
116
-
117
- @theme inline {
118
- --text-prose: var(--ui-prose);
119
- --text-prose--line-height: var(--ui-prose-lh);
120
- --text-prose--font-weight: 400;
121
-
122
- --text-value: var(--ui-value);
123
- --text-value--line-height: var(--ui-value-lh);
124
- --text-value--font-weight: 400;
125
- }
126
-
127
- :root {
128
- --ui-prose: 16px; --ui-prose-lh: 24px;
129
- --ui-value: 16px; --ui-value-lh: 22px;
130
-
131
- @variant sm { --ui-prose: 14px; --ui-prose-lh: 22px; }
132
- @variant sm { --ui-value: 14px; --ui-value-lh: 19px; }
133
- }
134
-
135
- @font-face {
136
- font-family: "JetBrains Mono";
137
- src: url("@fontsource/jetbrains-mono/files/jetbrains-mono-latin-400-normal.woff2") format("woff2");
138
- font-weight: 400;
139
- font-display: swap;
140
- /* [TODO] size-adjust 를 지운다 — 시각 기준 이미지가 서고 나서 */
141
- size-adjust: 96%;
142
- unicode-range: U+0000-00FF, U+2000-206F;
143
- }
144
-
145
- @layer base {
146
- :root { color-scheme: light; }
147
- .dark { color-scheme: dark; }
148
-
149
- body {
150
- background-color: var(--color-canvas);
151
- color: var(--color-fg);
152
- font-family: var(--font-sans);
153
- }
154
-
155
- code, pre, kbd, samp { font-family: var(--font-mono); }
156
- }
157
-
158
- @theme inline {
159
- --color-white:#ffffff;
160
- --color-black:#000000;
161
-
162
- --color-canvas: var(--ui-canvas);
163
- --color-well: var(--ui-well);
164
- --color-panel: var(--ui-panel);
165
- --color-float: var(--ui-float);
166
- --color-disabled: var(--gray-200);
167
-
168
- --color-hover: var(--ui-hover);
169
- --color-selected: var(--ui-selected);
170
- --color-selected-hover: var(--ui-selected-hover);
171
-
172
- --color-control-on: var(--gray-900);
173
- --color-control-on-hover: var(--gray-800);
174
- --color-control-on-disabled: var(--gray-600);
175
- --color-fg-on-control: var(--gray-50);
176
-
177
- --color-fg: var(--gray-900);
178
- --color-fg-muted: var(--gray-700);
179
- --color-fg-subtle: var(--gray-500);
180
- --color-fg-placeholder: var(--gray-400);
181
- --color-fg-disabled: var(--gray-600);
182
- --color-fg-on-solid:#ffffff;
183
-
184
- --color-line: var(--gray-200);
185
- --color-line-strong: var(--gray-300);
186
- --color-ring: var(--blue-500);
187
-
188
- --color-neutral-solid: var(--gray-900);
189
- --color-neutral-solid-hover: var(--gray-800);
190
-
191
- --color-primary-solid: var(--purple-500);
192
- --color-primary-solid-hover: var(--purple-600);
193
- --color-primary-weak: var(--purple-50);
194
- --color-primary-on-weak: var(--purple-700);
195
- --color-primary-stroke: var(--purple-200);
196
-
197
- --color-info: var(--blue-500);
198
- --color-info-weak: var(--blue-50);
199
- --color-info-on-weak: var(--blue-700);
200
- --color-info-stroke: var(--blue-200);
201
-
202
- --color-success: var(--green-500);
203
- --color-success-weak: var(--green-50);
204
- --color-success-on-weak: var(--green-700);
205
- --color-success-stroke: var(--green-200);
206
-
207
- --color-warning: var(--orange-500);
208
- --color-warning-weak: var(--orange-50);
209
- --color-warning-on-weak: var(--orange-700);
210
- --color-warning-stroke: var(--orange-200);
211
-
212
- --color-danger: var(--red-500);
213
- --color-danger-weak: var(--red-50);
214
- --color-danger-on-weak: var(--red-700);
215
- --color-danger-stroke: var(--red-200);
216
- --color-danger-solid: var(--red-500);
217
- --color-danger-solid-hover: var(--red-600);
218
- }
219
-
220
- :root, .light {
221
- --ui-canvas:#ffffff;
222
- --ui-well:#f2f4f6;
223
- --ui-panel:#ffffff;
224
- --ui-float:#ffffff;
225
- --ui-hover: rgba(0,23,51,0.02);
226
- --ui-selected: rgba(2,32,71,0.05);
227
- --ui-selected-hover: rgba(0,27,55,0.1);
228
- }
229
-
230
- .dark {
231
- --ui-canvas:#17171c;
232
- --ui-well:#101013;
233
- --ui-panel:#202027;
234
- --ui-float:#2c2c35;
235
- --ui-hover: rgba(209,209,253,0.05);
236
- --ui-selected: rgba(217,217,255,0.11);
237
- --ui-selected-hover: rgba(222,222,255,0.19);
238
- }
239
-
240
- @theme {
241
- --spacing-tight: 4px;
242
- --spacing-stack: 16px;
243
- --spacing-block: 32px;
244
- --spacing-section: 48px;
245
- --spacing-gutter: 16px;
246
-
247
- --container-app: 1280px;
248
- }
249
-
250
- @theme {
251
- --radius-*: initial;
252
-
253
- --radius-mark: 4px;
254
- --radius-well: 8px;
255
- --radius-panel: 12px;
256
- --radius-float: 16px;
257
-
258
- --radius-r2: 2px; --radius-r4: 4px; --radius-r6: 6px;
259
- --radius-r8: 8px; --radius-r10: 10px; --radius-r12: 12px;
260
- --radius-r14: 14px; --radius-r16: 16px; --radius-r20: 20px;
261
- --radius-r24: 24px;
262
- }
263
-
264
- @theme {
265
- --ease-*: initial;
266
-
267
- --ease-state: cubic-bezier(.35, 0, .65, 1);
268
- --ease-enter: cubic-bezier(0, 0, .15, 1);
269
- --ease-exit: cubic-bezier(.35, 0, 1, 1);
270
- }
271
-
272
- :root {
273
- --dur-state: 150ms;
274
- --dur-enter: 200ms;
275
- --dur-exit: 150ms;
276
- --enter-scale: 0.95;
277
- --draw-len: 24;
278
- }
279
-
280
- @utility transition-state {
281
- transition-property: color, background-color, box-shadow, scale;
282
- transition-duration: var(--dur-state);
283
- transition-timing-function: var(--ease-state);
284
- }
285
-
286
- @keyframes enter { from { opacity: 0; scale: var(--enter-scale); } }
287
- @keyframes exit { to { opacity: 0; scale: var(--enter-scale); } }
288
-
289
- @utility animate-enter { animation: enter var(--dur-enter) var(--ease-enter); }
290
- @utility animate-exit { animation: exit var(--dur-exit) var(--ease-exit); }
291
-
292
- @keyframes draw { from { stroke-dashoffset: var(--draw-len); } }
293
-
294
- @utility animate-draw {
295
- stroke-dasharray: var(--draw-len);
296
- animation: draw var(--dur-enter) var(--ease-enter);
297
- }
298
-
299
- @keyframes fade-in { from { opacity: 0; } }
300
- @keyframes fade-out { to { opacity: 0; } }
301
-
302
- @utility animate-fade-in { animation: fade-in var(--dur-enter) var(--ease-enter); }
303
- @utility animate-fade-out { animation: fade-out var(--dur-exit) var(--ease-exit); }
304
-
305
- @media (prefers-reduced-motion: reduce) {
306
- :root { --enter-scale: 1; }
307
- }
308
-
309
- @theme {
310
- --shadow-*: initial;
311
-
312
- --shadow-s1: 0 1px 4px 0 var(--shade-1);
313
- --shadow-s2: 0 2px 10px 0 var(--shade-2);
314
- --shadow-s3: 0 4px 16px 0 var(--shade-3);
315
- }
316
-
317
- :root, .light { --shade-1:#00000014; --shade-2:#0000001a; --shade-3:#0000001f; }
318
- .dark { --shade-1:#00000080; --shade-2:#000000ad; --shade-3:#000000cc; }
319
-
320
- @theme inline {
321
- --color-overlay: var(--ui-overlay);
322
- }
323
- :root, .light { --ui-overlay: rgba(0,0,0,0.2); }
324
- .dark { --ui-overlay: rgba(0,0,0,0.56); }
325
-
326
- @utility z-sticky { z-index: 10; }
327
- @utility z-overlay { z-index: 50; }
328
- @utility z-toast { z-index: 60; }
329
-
330
- :root { --header-h: 56px; }
331
-
332
- @utility side-rail {
333
- position: sticky;
334
- top: var(--header-h);
335
- max-height: calc(100dvh - var(--header-h));
336
- overflow-y: auto;
337
- }
338
-
339
- @utility scroll-slim {
340
- scrollbar-width: thin;
341
- scrollbar-color: var(--color-line-strong) transparent;
342
- }
343
-
344
- @utility scroll-hidden {
345
- scrollbar-width: none;
346
- &::-webkit-scrollbar { display: none; }
347
- }