@xonic0506/sonic-ui 0.0.0-stage → 0.2.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.
- package/AI-RULES.md +18 -0
- package/DESIGN.md +652 -0
- package/LICENSE +21 -0
- package/README.md +124 -2
- package/dist/components/alert-dialog.d.ts +16 -0
- package/dist/components/alert-dialog.js +46 -0
- package/dist/components/avatar.d.ts +10 -0
- package/dist/components/avatar.js +27 -0
- package/dist/components/badge.d.ts +8 -0
- package/dist/components/badge.js +77 -0
- package/dist/components/button.d.ts +18 -0
- package/dist/components/button.js +49 -0
- package/dist/components/card.d.ts +24 -0
- package/dist/components/card.js +15 -0
- package/dist/components/checkbox.d.ts +30 -0
- package/dist/components/checkbox.js +48 -0
- package/dist/components/dialog.d.ts +39 -0
- package/dist/components/dialog.js +52 -0
- package/dist/components/empty-state.d.ts +11 -0
- package/dist/components/empty-state.js +17 -0
- package/dist/components/error-state.d.ts +12 -0
- package/dist/components/error-state.js +24 -0
- package/dist/components/field.d.ts +13 -0
- package/dist/components/field.js +33 -0
- package/dist/components/input.d.ts +8 -0
- package/dist/components/input.js +16 -0
- package/dist/components/menu.d.ts +33 -0
- package/dist/components/menu.js +44 -0
- package/dist/components/popover.d.ts +26 -0
- package/dist/components/popover.js +50 -0
- package/dist/components/popup-styles.d.ts +3 -0
- package/dist/components/popup-styles.js +5 -0
- package/dist/components/progress.d.ts +16 -0
- package/dist/components/progress.js +27 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +40 -0
- package/dist/components/select.d.ts +30 -0
- package/dist/components/select.js +66 -0
- package/dist/components/separator.d.ts +7 -0
- package/dist/components/separator.js +14 -0
- package/dist/components/skeleton.d.ts +8 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/state-layout.d.ts +11 -0
- package/dist/components/state-layout.js +31 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.js +24 -0
- package/dist/components/tabs.d.ts +31 -0
- package/dist/components/tabs.js +41 -0
- package/dist/components/text.d.ts +10 -0
- package/dist/components/text.js +39 -0
- package/dist/components/textarea.d.ts +6 -0
- package/dist/components/textarea.js +20 -0
- package/dist/components/toast.d.ts +24 -0
- package/dist/components/toast.js +62 -0
- package/dist/components/tooltip.d.ts +11 -0
- package/dist/components/tooltip.js +19 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +26 -0
- package/dist/lib/cn.d.ts +1 -0
- package/dist/lib/cn.js +19 -0
- package/dist/styles.css +3 -0
- package/dist/tailwind.css +72 -0
- package/lint/design-rules.js +25 -0
- package/package.json +62 -4
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TabsProps {
|
|
3
|
+
value?: string;
|
|
4
|
+
defaultValue?: string;
|
|
5
|
+
onValueChange?: (value: string) => void;
|
|
6
|
+
className?: string;
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
/** 같은 대상의 다른 보기를 오갈 때 써요. 페이지 이동 링크에는 쓰지 않아요 */
|
|
10
|
+
export declare function Tabs({ value, defaultValue, onValueChange, className, children }: TabsProps): React.JSX.Element;
|
|
11
|
+
export interface TabsListProps {
|
|
12
|
+
/** 화살표 키로 이동하면 바로 탭을 바꿀지 (기본 true). false면 화살표로 이동하고 Enter·Space로 바꿔요 */
|
|
13
|
+
activateOnFocus?: boolean;
|
|
14
|
+
/** 끝에서 화살표를 누르면 처음으로 돌아갈지 (기본 true) */
|
|
15
|
+
loopFocus?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
}
|
|
19
|
+
export declare function TabsList({ activateOnFocus, loopFocus, className, children }: TabsListProps): React.JSX.Element;
|
|
20
|
+
export interface TabProps {
|
|
21
|
+
value: string;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
className?: string;
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
export declare const Tab: React.ForwardRefExoticComponent<TabProps & React.RefAttributes<HTMLButtonElement>>;
|
|
27
|
+
export declare function TabsPanel({ value, className, children }: {
|
|
28
|
+
value: string;
|
|
29
|
+
className?: string;
|
|
30
|
+
children?: React.ReactNode;
|
|
31
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import * as t from "react";
|
|
4
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
5
|
+
import { Tabs as i } from "@base-ui/react/tabs";
|
|
6
|
+
//#region src/components/tabs.tsx
|
|
7
|
+
function a({ value: t, defaultValue: r, onValueChange: a, className: o, children: s }) {
|
|
8
|
+
return /* @__PURE__ */ n(i.Root, {
|
|
9
|
+
value: t,
|
|
10
|
+
defaultValue: r,
|
|
11
|
+
onValueChange: a && ((e) => a(e)),
|
|
12
|
+
className: e("sn:flex sn:flex-col sn:gap-4 sn:font-sans", o),
|
|
13
|
+
children: s
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function o({ activateOnFocus: t = !0, loopFocus: a = !0, className: o, children: s }) {
|
|
17
|
+
return /* @__PURE__ */ r(i.List, {
|
|
18
|
+
activateOnFocus: t,
|
|
19
|
+
loopFocus: a,
|
|
20
|
+
className: e("sn:relative sn:flex sn:border-b sn:border-default", o),
|
|
21
|
+
children: [s, /* @__PURE__ */ n(i.Indicator, { className: "sn:absolute sn:bottom-[-1px] sn:left-0 sn:h-0.5 sn:w-(--active-tab-width) sn:translate-x-(--active-tab-left) sn:bg-primary sn:transition-[translate,width] sn:duration-(--sn-duration-normal) sn:ease-standard sn:motion-reduce:transition-none" })]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
var s = t.forwardRef(function({ value: t, disabled: r, className: a, children: o }, s) {
|
|
25
|
+
return /* @__PURE__ */ n(i.Tab, {
|
|
26
|
+
ref: s,
|
|
27
|
+
value: t,
|
|
28
|
+
disabled: r,
|
|
29
|
+
className: e("sn:box-border sn:flex sn:h-8 sn:items-center sn:border-0 sn:bg-transparent sn:px-3 sn:font-sans sn:text-body-strong sn:text-muted sn:cursor-pointer sn:focus-visible:outline-2 sn:focus-visible:-outline-offset-2 sn:focus-visible:outline-focus sn:transition-colors sn:duration-(--sn-duration-fast) sn:hover:not-data-disabled:text-default sn:data-active:text-default sn:data-disabled:cursor-not-allowed sn:data-disabled:opacity-(--sn-opacity-disabled)", a),
|
|
30
|
+
children: o
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
function c({ value: t, className: r, children: a }) {
|
|
34
|
+
return /* @__PURE__ */ n(i.Panel, {
|
|
35
|
+
value: t,
|
|
36
|
+
className: e("sn:text-body sn:text-default sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus", r),
|
|
37
|
+
children: a
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { s as Tab, a as Tabs, o as TabsList, c as TabsPanel };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type TextVariant = 'display' | 'title' | 'body' | 'body-strong' | 'label' | 'caption' | 'mono' | 'mono-caption';
|
|
3
|
+
export interface TextProps extends Omit<React.HTMLAttributes<HTMLElement>, 'className'>, Pick<React.LabelHTMLAttributes<HTMLLabelElement>, 'htmlFor'> {
|
|
4
|
+
variant?: TextVariant;
|
|
5
|
+
tone?: 'default' | 'muted' | 'danger';
|
|
6
|
+
/** 렌더할 태그. 기본값 span */
|
|
7
|
+
as?: 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label' | 'strong' | 'code';
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const Text: React.ForwardRefExoticComponent<TextProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import * as t from "react";
|
|
4
|
+
import { cva as n } from "class-variance-authority";
|
|
5
|
+
//#region src/components/text.tsx
|
|
6
|
+
var r = n("sn:m-0 sn:font-sans", {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
display: "sn:text-display",
|
|
10
|
+
title: "sn:text-title",
|
|
11
|
+
body: "sn:text-body",
|
|
12
|
+
"body-strong": "sn:text-body-strong",
|
|
13
|
+
label: "sn:text-label",
|
|
14
|
+
caption: "sn:text-caption",
|
|
15
|
+
mono: "sn:font-mono sn:text-body sn:tabular-nums",
|
|
16
|
+
"mono-caption": "sn:font-mono sn:text-caption sn:tabular-nums"
|
|
17
|
+
},
|
|
18
|
+
tone: {
|
|
19
|
+
default: "sn:text-default",
|
|
20
|
+
muted: "sn:text-muted",
|
|
21
|
+
danger: "sn:text-danger"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "body",
|
|
26
|
+
tone: "default"
|
|
27
|
+
}
|
|
28
|
+
}), i = t.forwardRef(function({ variant: n, tone: i, as: a = "span", className: o, ...s }, c) {
|
|
29
|
+
return t.createElement(a, {
|
|
30
|
+
ref: c,
|
|
31
|
+
className: e(r({
|
|
32
|
+
variant: n,
|
|
33
|
+
tone: i
|
|
34
|
+
}), o),
|
|
35
|
+
...s
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//#endregion
|
|
39
|
+
export { i as Text };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'className'> {
|
|
3
|
+
invalid?: boolean;
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { inputClass as t } from "./input.js";
|
|
4
|
+
import * as n from "react";
|
|
5
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
6
|
+
import { Input as i } from "@base-ui/react/input";
|
|
7
|
+
//#region src/components/textarea.tsx
|
|
8
|
+
var a = n.forwardRef(function({ invalid: n, className: a, rows: o = 3, ...s }, c) {
|
|
9
|
+
return /* @__PURE__ */ r(i, {
|
|
10
|
+
render: /* @__PURE__ */ r("textarea", {
|
|
11
|
+
ref: c,
|
|
12
|
+
rows: o
|
|
13
|
+
}),
|
|
14
|
+
"aria-invalid": n || void 0,
|
|
15
|
+
className: e(t, "sn:min-h-20 sn:resize-y sn:py-2", a),
|
|
16
|
+
...s
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { a as Textarea };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export type ToastTone = 'neutral' | 'success' | 'warning' | 'danger';
|
|
3
|
+
export interface ToastOptions {
|
|
4
|
+
/** 결과 한 줄 */
|
|
5
|
+
title: React.ReactNode;
|
|
6
|
+
description?: React.ReactNode;
|
|
7
|
+
tone?: ToastTone;
|
|
8
|
+
/** 사라지기까지 ms. 0이면 직접 닫을 때까지 남아요 (기본 5000) */
|
|
9
|
+
timeout?: number;
|
|
10
|
+
}
|
|
11
|
+
/** ToastProvider 안의 클라이언트 컴포넌트에서만 써요 */
|
|
12
|
+
export declare function useToast(): {
|
|
13
|
+
/** 알림을 띄우고 id를 돌려줘요 */
|
|
14
|
+
add: ({ tone, ...options }: ToastOptions) => string;
|
|
15
|
+
/** id가 없으면 모두 닫아요 */
|
|
16
|
+
close: (id?: string) => void;
|
|
17
|
+
};
|
|
18
|
+
export interface ToastProviderProps {
|
|
19
|
+
/** 기본 사라지는 시간 ms (기본 5000) */
|
|
20
|
+
timeout?: number;
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/** 앱 최상단에 한 번 둬요. 알림은 화면 오른쪽 아래에 쌓여요 */
|
|
24
|
+
export declare function ToastProvider({ timeout, children }: ToastProviderProps): React.JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import { Button as t } from "./button.js";
|
|
4
|
+
import * as n from "react";
|
|
5
|
+
import { CircleAlert as r, CircleCheck as i, Info as a, TriangleAlert as o, X as s } from "lucide-react";
|
|
6
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
|
+
import { Toast as u } from "@base-ui/react/toast";
|
|
8
|
+
//#region src/components/toast.tsx
|
|
9
|
+
var d = {
|
|
10
|
+
neutral: /* @__PURE__ */ c(a, { className: "sn:text-muted" }),
|
|
11
|
+
success: /* @__PURE__ */ c(i, { className: "sn:text-success" }),
|
|
12
|
+
warning: /* @__PURE__ */ c(o, { className: "sn:text-warning" }),
|
|
13
|
+
danger: /* @__PURE__ */ c(r, { className: "sn:text-danger" })
|
|
14
|
+
};
|
|
15
|
+
function f() {
|
|
16
|
+
let { add: e, close: t } = u.useToastManager();
|
|
17
|
+
return n.useMemo(() => ({
|
|
18
|
+
add: ({ tone: t = "neutral", ...n }) => e({
|
|
19
|
+
...n,
|
|
20
|
+
type: t,
|
|
21
|
+
priority: t === "danger" ? "high" : "low"
|
|
22
|
+
}),
|
|
23
|
+
close: (e) => t(e)
|
|
24
|
+
}), [e, t]);
|
|
25
|
+
}
|
|
26
|
+
function p({ timeout: e, children: t }) {
|
|
27
|
+
return /* @__PURE__ */ l(u.Provider, {
|
|
28
|
+
timeout: e,
|
|
29
|
+
children: [t, /* @__PURE__ */ c(u.Portal, { children: /* @__PURE__ */ c(u.Viewport, {
|
|
30
|
+
"aria-label": "알림",
|
|
31
|
+
className: "sn:fixed sn:right-4 sn:bottom-4 sn:z-50 sn:flex sn:w-90 sn:max-w-[calc(100vw-2*var(--sn-space-4))] sn:flex-col-reverse sn:gap-2 sn:outline-none",
|
|
32
|
+
children: /* @__PURE__ */ c(m, {})
|
|
33
|
+
}) })]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function m() {
|
|
37
|
+
let { toasts: n } = u.useToastManager();
|
|
38
|
+
return n.map((n) => /* @__PURE__ */ l(u.Root, {
|
|
39
|
+
toast: n,
|
|
40
|
+
className: "sn:box-border sn:flex sn:items-start sn:gap-3 sn:rounded-lg sn:border sn:border-default sn:bg-surface sn:p-3 sn:font-sans sn:text-default sn:transition-[opacity,translate] sn:duration-(--sn-duration-normal) sn:ease-standard sn:focus-visible:outline-2 sn:focus-visible:outline-offset-2 sn:focus-visible:outline-focus sn:data-starting-style:translate-y-2 sn:data-starting-style:opacity-0 sn:data-ending-style:translate-y-2 sn:data-ending-style:opacity-0 sn:data-limited:hidden sn:motion-reduce:transition-none",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ c("span", {
|
|
43
|
+
className: "sn:flex sn:h-7 sn:shrink-0 sn:items-center sn:[&_svg]:size-4",
|
|
44
|
+
children: d[n.type ?? "neutral"]
|
|
45
|
+
}),
|
|
46
|
+
/* @__PURE__ */ l("div", {
|
|
47
|
+
className: "sn:flex sn:min-w-0 sn:flex-1 sn:flex-col sn:gap-1 sn:pt-1",
|
|
48
|
+
children: [/* @__PURE__ */ c(u.Title, { className: "sn:m-0 sn:text-body-strong sn:text-default" }), /* @__PURE__ */ c(u.Description, { className: e("sn:m-0 sn:text-caption sn:text-muted", !n.description && "sn:hidden") })]
|
|
49
|
+
}),
|
|
50
|
+
/* @__PURE__ */ c(u.Close, {
|
|
51
|
+
render: /* @__PURE__ */ c(t, {
|
|
52
|
+
size: "icon-sm",
|
|
53
|
+
variant: "ghost",
|
|
54
|
+
"aria-label": "알림 닫기"
|
|
55
|
+
}),
|
|
56
|
+
children: /* @__PURE__ */ c(s, {})
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
}, n.id));
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { p as ToastProvider, f as useToast };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface TooltipProps {
|
|
3
|
+
/** 보여 줄 짧은 이름. 중요한 정보나 클릭할 요소는 넣지 않아요 */
|
|
4
|
+
content: React.ReactNode;
|
|
5
|
+
side?: 'top' | 'bottom' | 'left' | 'right';
|
|
6
|
+
className?: string;
|
|
7
|
+
/** 대상 요소 하나 (보통 아이콘 버튼) */
|
|
8
|
+
children: React.ReactElement;
|
|
9
|
+
}
|
|
10
|
+
/** 마우스는 600ms 뒤, 키보드 포커스는 바로 보여요 */
|
|
11
|
+
export declare function Tooltip({ content, side, className, children }: TooltipProps): React.JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn as e } from "../lib/cn.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
5
|
+
import { Tooltip as r } from "@base-ui/react/tooltip";
|
|
6
|
+
//#region src/components/tooltip.tsx
|
|
7
|
+
function i({ content: i, side: a = "top", className: o, children: s }) {
|
|
8
|
+
return /* @__PURE__ */ n(r.Root, { children: [/* @__PURE__ */ t(r.Trigger, { render: s }), /* @__PURE__ */ t(r.Portal, { children: /* @__PURE__ */ t(r.Positioner, {
|
|
9
|
+
side: a,
|
|
10
|
+
sideOffset: 4,
|
|
11
|
+
className: "sn:z-50",
|
|
12
|
+
children: /* @__PURE__ */ t(r.Popup, {
|
|
13
|
+
className: e("sn:origin-(--transform-origin) sn:rounded-md sn:bg-inverse sn:px-2 sn:py-1 sn:font-sans sn:text-caption sn:text-inverse sn:transition-[opacity,scale] sn:duration-(--sn-duration-fast) sn:ease-standard sn:data-starting-style:scale-98 sn:data-starting-style:opacity-0 sn:data-ending-style:scale-98 sn:data-ending-style:opacity-0 sn:motion-reduce:transition-none", o),
|
|
14
|
+
children: i
|
|
15
|
+
})
|
|
16
|
+
}) })] });
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { i as Tooltip };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { Button, type ButtonProps } from './components/button';
|
|
2
|
+
export { Input, type InputProps } from './components/input';
|
|
3
|
+
export { Textarea, type TextareaProps } from './components/textarea';
|
|
4
|
+
export { Checkbox, CheckboxGroup, type CheckboxProps, type CheckboxGroupProps } from './components/checkbox';
|
|
5
|
+
export { RadioGroup, Radio, type RadioGroupProps, type RadioProps } from './components/radio-group';
|
|
6
|
+
export { Switch, type SwitchProps } from './components/switch';
|
|
7
|
+
export { Select, type SelectProps, type SelectOption } from './components/select';
|
|
8
|
+
export { Text, type TextProps, type TextVariant } from './components/text';
|
|
9
|
+
export { Card, CardHeader, CardTitle, CardDescription, CardBody, CardFooter, type CardProps } from './components/card';
|
|
10
|
+
export { Avatar, type AvatarProps } from './components/avatar';
|
|
11
|
+
export { Field, type FieldProps } from './components/field';
|
|
12
|
+
export { Skeleton, type SkeletonProps } from './components/skeleton';
|
|
13
|
+
export { EmptyState, type EmptyStateProps } from './components/empty-state';
|
|
14
|
+
export { ErrorState, type ErrorStateProps } from './components/error-state';
|
|
15
|
+
export { Dialog, DialogTrigger, DialogPopup, DialogTitle, DialogDescription, DialogClose, DialogFooter, type DialogProps, type DialogButtonProps, type DialogPopupProps, } from './components/dialog';
|
|
16
|
+
export { AlertDialog, AlertDialogTrigger, AlertDialogPopup, AlertDialogTitle, AlertDialogDescription, AlertDialogClose, AlertDialogFooter, } from './components/alert-dialog';
|
|
17
|
+
export { Menu, MenuTrigger, MenuPopup, MenuItem, MenuSeparator, type MenuProps, type MenuTriggerProps, type MenuPopupProps, type MenuItemProps } from './components/menu';
|
|
18
|
+
export { Popover, PopoverTrigger, PopoverPopup, PopoverTitle, PopoverDescription, PopoverClose, type PopoverProps, type PopoverPopupProps } from './components/popover';
|
|
19
|
+
export { Tooltip, type TooltipProps } from './components/tooltip';
|
|
20
|
+
export { Tabs, TabsList, Tab, TabsPanel, type TabsProps, type TabsListProps, type TabProps } from './components/tabs';
|
|
21
|
+
export { Separator, type SeparatorProps } from './components/separator';
|
|
22
|
+
export { ToastProvider, useToast, type ToastProviderProps, type ToastOptions, type ToastTone } from './components/toast';
|
|
23
|
+
export { Badge, type BadgeProps } from './components/badge';
|
|
24
|
+
export { Progress, type ProgressProps } from './components/progress';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button as e } from "./components/button.js";
|
|
3
|
+
import { Input as t } from "./components/input.js";
|
|
4
|
+
import { Textarea as n } from "./components/textarea.js";
|
|
5
|
+
import { Checkbox as r, CheckboxGroup as i } from "./components/checkbox.js";
|
|
6
|
+
import { Radio as a, RadioGroup as o } from "./components/radio-group.js";
|
|
7
|
+
import { Switch as s } from "./components/switch.js";
|
|
8
|
+
import { Select as c } from "./components/select.js";
|
|
9
|
+
import { Text as l } from "./components/text.js";
|
|
10
|
+
import { Card as u, CardBody as d, CardDescription as f, CardFooter as p, CardHeader as m, CardTitle as h } from "./components/card.js";
|
|
11
|
+
import { Avatar as g } from "./components/avatar.js";
|
|
12
|
+
import { Field as _ } from "./components/field.js";
|
|
13
|
+
import { Skeleton as v } from "./components/skeleton.js";
|
|
14
|
+
import { EmptyState as y } from "./components/empty-state.js";
|
|
15
|
+
import { ErrorState as b } from "./components/error-state.js";
|
|
16
|
+
import { Dialog as x, DialogClose as S, DialogDescription as C, DialogFooter as w, DialogPopup as T, DialogTitle as E, DialogTrigger as D } from "./components/dialog.js";
|
|
17
|
+
import { AlertDialog as O, AlertDialogClose as k, AlertDialogDescription as A, AlertDialogFooter as j, AlertDialogPopup as M, AlertDialogTitle as N, AlertDialogTrigger as P } from "./components/alert-dialog.js";
|
|
18
|
+
import { Menu as F, MenuItem as I, MenuPopup as L, MenuSeparator as R, MenuTrigger as z } from "./components/menu.js";
|
|
19
|
+
import { Popover as B, PopoverClose as V, PopoverDescription as H, PopoverPopup as U, PopoverTitle as W, PopoverTrigger as G } from "./components/popover.js";
|
|
20
|
+
import { Tooltip as K } from "./components/tooltip.js";
|
|
21
|
+
import { Tab as q, Tabs as J, TabsList as Y, TabsPanel as X } from "./components/tabs.js";
|
|
22
|
+
import { Separator as Z } from "./components/separator.js";
|
|
23
|
+
import { ToastProvider as Q, useToast as $ } from "./components/toast.js";
|
|
24
|
+
import { Badge as ee } from "./components/badge.js";
|
|
25
|
+
import { Progress as te } from "./components/progress.js";
|
|
26
|
+
export { O as AlertDialog, k as AlertDialogClose, A as AlertDialogDescription, j as AlertDialogFooter, M as AlertDialogPopup, N as AlertDialogTitle, P as AlertDialogTrigger, g as Avatar, ee as Badge, e as Button, u as Card, d as CardBody, f as CardDescription, p as CardFooter, m as CardHeader, h as CardTitle, r as Checkbox, i as CheckboxGroup, x as Dialog, S as DialogClose, C as DialogDescription, w as DialogFooter, T as DialogPopup, E as DialogTitle, D as DialogTrigger, y as EmptyState, b as ErrorState, _ as Field, t as Input, F as Menu, I as MenuItem, L as MenuPopup, R as MenuSeparator, z as MenuTrigger, B as Popover, V as PopoverClose, H as PopoverDescription, U as PopoverPopup, W as PopoverTitle, G as PopoverTrigger, te as Progress, a as Radio, o as RadioGroup, c as Select, Z as Separator, v as Skeleton, s as Switch, q as Tab, J as Tabs, Y as TabsList, X as TabsPanel, l as Text, n as Textarea, Q as ToastProvider, K as Tooltip, $ as useToast };
|
package/dist/lib/cn.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cn(...inputs: Array<string | false | null | undefined>): string;
|
package/dist/lib/cn.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { extendTailwindMerge as e } from "tailwind-merge";
|
|
3
|
+
//#region src/lib/cn.ts
|
|
4
|
+
var t = e({
|
|
5
|
+
prefix: "sn",
|
|
6
|
+
extend: { classGroups: { "font-size": [{ text: [
|
|
7
|
+
"display",
|
|
8
|
+
"title",
|
|
9
|
+
"body",
|
|
10
|
+
"body-strong",
|
|
11
|
+
"label",
|
|
12
|
+
"caption"
|
|
13
|
+
] }] } }
|
|
14
|
+
});
|
|
15
|
+
function n(...e) {
|
|
16
|
+
return t(e.filter(Boolean).join(" "));
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { n as cn };
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/*! sonic-ui | 전체가 @layer sonic 안에 있어요. 쓰는 쪽: @layer theme, base, sonic, components, utilities; */
|
|
2
|
+
@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@layer sonic{/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
3
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-duration:initial;--tw-ease:initial;--tw-outline-style:solid;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme;@layer utilities{.sn\:absolute{position:absolute}.sn\:fixed{position:fixed}.sn\:relative{position:relative}.sn\:inset-0{inset:0}.sn\:top-1\/2{top:50%}.sn\:right-4{right:calc(var(--sn-space-1) * 4)}.sn\:bottom-4{bottom:calc(var(--sn-space-1) * 4)}.sn\:bottom-\[-1px\]{bottom:-1px}.sn\:left-0{left:0}.sn\:left-1\/2{left:50%}.sn\:z-50{z-index:50}.sn\:m-0{margin:0}.sn\:my-1{margin-block:var(--sn-space-1)}.sn\:-mt-2{margin-top:calc(var(--sn-space-1) * -2)}.sn\:-mt-3{margin-top:calc(var(--sn-space-1) * -3)}.sn\:mt-2{margin-top:calc(var(--sn-space-1) * 2)}.sn\:mb-0{margin-bottom:0}.sn\:box-border{box-sizing:border-box}.sn\:block{display:block}.sn\:flex{display:flex}.sn\:hidden{display:none}.sn\:inline-flex{display:inline-flex}.sn\:size-2{width:calc(var(--sn-space-1) * 2);height:calc(var(--sn-space-1) * 2)}.sn\:size-3{width:calc(var(--sn-space-1) * 3);height:calc(var(--sn-space-1) * 3)}.sn\:size-4{width:calc(var(--sn-space-1) * 4);height:calc(var(--sn-space-1) * 4)}.sn\:size-6{width:calc(var(--sn-space-1) * 6);height:calc(var(--sn-space-1) * 6)}.sn\:size-7{width:calc(var(--sn-space-1) * 7);height:calc(var(--sn-space-1) * 7)}.sn\:size-8{width:calc(var(--sn-space-1) * 8);height:calc(var(--sn-space-1) * 8)}.sn\:size-10{width:calc(var(--sn-space-1) * 10);height:calc(var(--sn-space-1) * 10)}.sn\:size-12{width:calc(var(--sn-space-1) * 12);height:calc(var(--sn-space-1) * 12)}.sn\:size-full{width:100%;height:100%}.sn\:h-0\.5{height:calc(var(--sn-space-1) * .5)}.sn\:h-1{height:var(--sn-space-1)}.sn\:h-3{height:calc(var(--sn-space-1) * 3)}.sn\:h-4{height:calc(var(--sn-space-1) * 4)}.sn\:h-5{height:calc(var(--sn-space-1) * 5)}.sn\:h-7{height:calc(var(--sn-space-1) * 7)}.sn\:h-8{height:calc(var(--sn-space-1) * 8)}.sn\:h-10{height:calc(var(--sn-space-1) * 10)}.sn\:h-16{height:calc(var(--sn-space-1) * 16)}.sn\:h-full{height:100%}.sn\:h-px{height:1px}.sn\:max-h-\(--available-height\){max-height:var(--available-height)}.sn\:min-h-20{min-height:calc(var(--sn-space-1) * 20)}.sn\:w-\(--active-tab-width\){width:var(--active-tab-width)}.sn\:w-7{width:calc(var(--sn-space-1) * 7)}.sn\:w-80{width:calc(var(--sn-space-1) * 80)}.sn\:w-90{width:calc(var(--sn-space-1) * 90)}.sn\:w-110{width:calc(var(--sn-space-1) * 110)}.sn\:w-full{width:100%}.sn\:w-px{width:1px}.sn\:max-w-\[calc\(100vw-2\*var\(--sn-space-4\)\)\]{max-width:calc(100vw - 2 * var(--sn-space-4))}.sn\:min-w-\(--anchor-width\){min-width:var(--anchor-width)}.sn\:min-w-0{min-width:0}.sn\:min-w-48{min-width:calc(var(--sn-space-1) * 48)}.sn\:flex-1{flex:1}.sn\:shrink-0{flex-shrink:0}.sn\:origin-\(--transform-origin\){transform-origin:var(--transform-origin)}.sn\:-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:translate-x-\(--active-tab-left\){--tw-translate-x:var(--active-tab-left);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:animate-\[sn-pulse_var\(--sn-duration-loop\)_ease-in-out_infinite\]{animation:sn-pulse var(--sn-duration-loop) ease-in-out infinite}.sn\:animate-\[sn-spin_var\(--sn-duration-loop\)_linear_infinite\]{animation:sn-spin var(--sn-duration-loop) linear infinite}.sn\:cursor-default{cursor:default}.sn\:cursor-not-allowed{cursor:not-allowed}.sn\:cursor-pointer{cursor:pointer}.sn\:resize-y{resize:vertical}.sn\:flex-col{flex-direction:column}.sn\:flex-col-reverse{flex-direction:column-reverse}.sn\:flex-wrap{flex-wrap:wrap}.sn\:items-center{align-items:center}.sn\:items-start{align-items:flex-start}.sn\:justify-center{justify-content:center}.sn\:justify-end{justify-content:flex-end}.sn\:gap-1{gap:var(--sn-space-1)}.sn\:gap-2{gap:calc(var(--sn-space-1) * 2)}.sn\:gap-3{gap:calc(var(--sn-space-1) * 3)}.sn\:gap-4{gap:calc(var(--sn-space-1) * 4)}.sn\:gap-6{gap:calc(var(--sn-space-1) * 6)}.sn\:gap-10{gap:calc(var(--sn-space-1) * 10)}.sn\:self-stretch{align-self:stretch}.sn\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sn\:overflow-hidden{overflow:hidden}.sn\:overflow-y-auto{overflow-y:auto}.sn\:rounded-full{border-radius:var(--sn-radius-full)}.sn\:rounded-lg{border-radius:var(--sn-radius-lg)}.sn\:rounded-md{border-radius:var(--sn-radius-md)}.sn\:rounded-sm{border-radius:var(--sn-radius-sm)}.sn\:border{border-style:var(--tw-border-style);border-width:1px}.sn\:border-0{border-style:var(--tw-border-style);border-width:0}.sn\:border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.sn\:border-default{border-color:var(--sn-color-border-default)}.sn\:border-strong{border-color:var(--sn-color-border-strong)}.sn\:border-transparent{border-color:#0000}.sn\:bg-\(--sn-color-border-default\){background-color:var(--sn-color-border-default)}.sn\:bg-\(--sn-color-text-on-primary\){background-color:var(--sn-color-text-on-primary)}.sn\:bg-canvas{background-color:var(--sn-color-bg-canvas)}.sn\:bg-danger{background-color:var(--sn-color-bg-danger)}.sn\:bg-danger-subtle{background-color:var(--sn-color-bg-danger-subtle)}.sn\:bg-inverse{background-color:var(--sn-color-bg-inverse)}.sn\:bg-overlay{background-color:var(--sn-color-bg-overlay)}.sn\:bg-primary{background-color:var(--sn-color-bg-primary)}.sn\:bg-subtle{background-color:var(--sn-color-bg-subtle)}.sn\:bg-success{background-color:var(--sn-color-bg-success)}.sn\:bg-success-subtle{background-color:var(--sn-color-bg-success-subtle)}.sn\:bg-surface{background-color:var(--sn-color-bg-surface)}.sn\:bg-transparent{background-color:#0000}.sn\:bg-warning{background-color:var(--sn-color-bg-warning)}.sn\:bg-warning-subtle{background-color:var(--sn-color-bg-warning-subtle)}.sn\:object-cover{object-fit:cover}.sn\:p-0{padding:0}.sn\:p-1{padding:var(--sn-space-1)}.sn\:p-3{padding:calc(var(--sn-space-1) * 3)}.sn\:p-4{padding:calc(var(--sn-space-1) * 4)}.sn\:p-5{padding:calc(var(--sn-space-1) * 5)}.sn\:p-6{padding:calc(var(--sn-space-1) * 6)}.sn\:p-px{padding:1px}.sn\:px-2{padding-inline:calc(var(--sn-space-1) * 2)}.sn\:px-3{padding-inline:calc(var(--sn-space-1) * 3)}.sn\:px-4{padding-inline:calc(var(--sn-space-1) * 4)}.sn\:px-6{padding-inline:calc(var(--sn-space-1) * 6)}.sn\:py-1{padding-block:var(--sn-space-1)}.sn\:py-2{padding-block:calc(var(--sn-space-1) * 2)}.sn\:py-12{padding-block:calc(var(--sn-space-1) * 12)}.sn\:pt-1{padding-top:var(--sn-space-1)}.sn\:text-center{text-align:center}.sn\:text-left{text-align:left}.sn\:align-middle{vertical-align:middle}.sn\:font-mono{font-family:var(--sn-font-mono)}.sn\:font-sans{font-family:var(--sn-font-sans)}.sn\:text-body{font-size:var(--sn-text-body);line-height:var(--tw-leading,var(--sn-text-body--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-body--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-body--font-weight))}.sn\:text-body-strong{font-size:var(--sn-text-body-strong);line-height:var(--tw-leading,var(--sn-text-body-strong--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-body-strong--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-body-strong--font-weight))}.sn\:text-caption{font-size:var(--sn-text-caption);line-height:var(--tw-leading,var(--sn-text-caption--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-caption--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-caption--font-weight))}.sn\:text-display{font-size:var(--sn-text-display);line-height:var(--tw-leading,var(--sn-text-display--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-display--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-display--font-weight))}.sn\:text-label{font-size:var(--sn-text-label);line-height:var(--tw-leading,var(--sn-text-label--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-label--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-label--font-weight))}.sn\:text-title{font-size:var(--sn-text-title);line-height:var(--tw-leading,var(--sn-text-title--line-height));letter-spacing:var(--tw-tracking,var(--sn-text-title--letter-spacing));font-weight:var(--tw-font-weight,var(--sn-text-title--font-weight))}.sn\:whitespace-nowrap{white-space:nowrap}.sn\:text-danger{color:var(--sn-color-text-danger)}.sn\:text-default{color:var(--sn-color-text-default)}.sn\:text-inverse{color:var(--sn-color-text-inverse)}.sn\:text-muted{color:var(--sn-color-text-muted)}.sn\:text-on-danger{color:var(--sn-color-text-on-danger)}.sn\:text-on-primary{color:var(--sn-color-text-on-primary)}.sn\:text-on-success{color:var(--sn-color-text-on-success)}.sn\:text-on-warning{color:var(--sn-color-text-on-warning)}.sn\:text-success{color:var(--sn-color-text-success)}.sn\:text-warning{color:var(--sn-color-text-warning)}.sn\:tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.sn\:opacity-\(--sn-opacity-disabled\){opacity:var(--sn-opacity-disabled)}.sn\:transition-\[opacity\,scale\]{transition-property:opacity,scale;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-\[opacity\,translate\]{transition-property:opacity,translate;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-\[translate\,width\]{transition-property:translate,width;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,ease);transition-duration:var(--tw-duration,0s)}.sn\:duration-\(--sn-duration-fast\){--tw-duration:var(--sn-duration-fast);transition-duration:var(--sn-duration-fast)}.sn\:duration-\(--sn-duration-normal\){--tw-duration:var(--sn-duration-normal);transition-duration:var(--sn-duration-normal)}.sn\:ease-standard{--tw-ease:var(--sn-easing-standard);transition-timing-function:var(--sn-easing-standard)}.sn\:outline-none{--tw-outline-style:none;outline-style:none}.sn\:select-none{-webkit-user-select:none;user-select:none}.sn\:group-data-popup-open\:rotate-180:is(:where(.sn\:group)[data-popup-open] *){rotate:180deg}.sn\:group-data-selected\:border-transparent:is(:where(.sn\:group)[data-selected] *){border-color:#0000}.sn\:group-data-selected\:bg-primary:is(:where(.sn\:group)[data-selected] *){background-color:var(--sn-color-bg-primary)}.sn\:placeholder\:text-muted::placeholder{color:var(--sn-color-text-muted)}@media (hover:hover){.sn\:hover\:not-data-disabled\:border-strong:hover:not([data-disabled]){border-color:var(--sn-color-border-strong)}.sn\:hover\:not-data-disabled\:bg-primary-hover:hover:not([data-disabled]){background-color:var(--sn-color-bg-primary-hover)}.sn\:hover\:not-data-disabled\:bg-subtle:hover:not([data-disabled]){background-color:var(--sn-color-bg-subtle)}.sn\:hover\:not-data-disabled\:text-default:hover:not([data-disabled]){color:var(--sn-color-text-default)}}.sn\:focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.sn\:focus-visible\:-outline-offset-2:focus-visible{outline-offset:calc(2px * -1)}.sn\:focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.sn\:focus-visible\:outline-focus:focus-visible{outline-color:var(--sn-color-border-focus)}.sn\:aria-invalid\:border-danger[aria-invalid=true]{border-color:var(--sn-color-border-danger)}@media (hover:hover){.sn\:hover\:aria-invalid\:border-danger:hover[aria-invalid=true]{border-color:var(--sn-color-border-danger)}}.sn\:aria-invalid\:focus-visible\:outline-danger[aria-invalid=true]:focus-visible{outline-color:var(--sn-color-border-danger)}.sn\:data-active\:text-default[data-active]{color:var(--sn-color-text-default)}.sn\:data-checked\:translate-x-3[data-checked]{--tw-translate-x:calc(var(--sn-space-1) * 3);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:data-checked\:border-transparent[data-checked]{border-color:#0000}.sn\:data-checked\:bg-\(--sn-color-text-on-primary\)[data-checked]{background-color:var(--sn-color-text-on-primary)}.sn\:data-checked\:bg-primary[data-checked]{background-color:var(--sn-color-bg-primary)}.sn\:data-disabled\:cursor-not-allowed[data-disabled]{cursor:not-allowed}.sn\:data-disabled\:opacity-\(--sn-opacity-disabled\)[data-disabled]{opacity:var(--sn-opacity-disabled)}.sn\:data-disabled\:not-data-loading\:cursor-not-allowed[data-disabled]:not([data-loading]){cursor:not-allowed}.sn\:data-disabled\:not-data-loading\:opacity-\(--sn-opacity-disabled\)[data-disabled]:not([data-loading]){opacity:var(--sn-opacity-disabled)}.sn\:data-ending-style\:translate-y-2[data-ending-style]{--tw-translate-y:calc(var(--sn-space-1) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:data-ending-style\:scale-98[data-ending-style]{--tw-scale-x:98%;--tw-scale-y:98%;--tw-scale-z:98%;scale:var(--tw-scale-x) var(--tw-scale-y)}.sn\:data-ending-style\:opacity-0[data-ending-style]{opacity:0}.sn\:data-highlighted\:bg-subtle[data-highlighted]{background-color:var(--sn-color-bg-subtle)}.sn\:data-indeterminate\:border-transparent[data-indeterminate]{border-color:#0000}.sn\:data-indeterminate\:bg-primary[data-indeterminate]{background-color:var(--sn-color-bg-primary)}.sn\:data-invalid\:border-danger[data-invalid]{border-color:var(--sn-color-border-danger)}.sn\:data-invalid\:focus-visible\:outline-danger[data-invalid]:focus-visible{outline-color:var(--sn-color-border-danger)}.sn\:data-limited\:hidden[data-limited]{display:none}.sn\:data-loading\:cursor-progress[data-loading]{cursor:progress}.sn\:data-placeholder\:text-muted[data-placeholder]{color:var(--sn-color-text-muted)}.sn\:data-popup-open\:border-focus[data-popup-open]{border-color:var(--sn-color-border-focus)}.sn\:data-starting-style\:translate-y-2[data-starting-style]{--tw-translate-y:calc(var(--sn-space-1) * 2);translate:var(--tw-translate-x) var(--tw-translate-y)}.sn\:data-starting-style\:scale-98[data-starting-style]{--tw-scale-x:98%;--tw-scale-y:98%;--tw-scale-z:98%;scale:var(--tw-scale-x) var(--tw-scale-y)}.sn\:data-starting-style\:opacity-0[data-starting-style]{opacity:0}@media (prefers-reduced-motion:reduce){.sn\:motion-reduce\:animate-none{animation:none}.sn\:motion-reduce\:transition-none{transition-property:none}}.sn\:\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.sn\:\[\&_svg\]\:size-3 svg{width:calc(var(--sn-space-1) * 3);height:calc(var(--sn-space-1) * 3)}.sn\:\[\&_svg\]\:size-4 svg{width:calc(var(--sn-space-1) * 4);height:calc(var(--sn-space-1) * 4)}.sn\:\[\&_svg\]\:size-5 svg{width:calc(var(--sn-space-1) * 5);height:calc(var(--sn-space-1) * 5)}.sn\:\[\&_svg\]\:size-6 svg{width:calc(var(--sn-space-1) * 6);height:calc(var(--sn-space-1) * 6)}.sn\:\[\&_svg\]\:shrink-0 svg{flex-shrink:0}}:root{--sn-space-0:0px;--sn-space-1:4px;--sn-space-2:8px;--sn-space-3:12px;--sn-space-4:16px;--sn-space-5:20px;--sn-space-6:24px;--sn-space-7:28px;--sn-space-8:32px;--sn-space-10:40px;--sn-space-12:48px;--sn-space-16:64px;--sn-radius-sm:2px;--sn-radius-md:4px;--sn-radius-lg:6px;--sn-radius-full:9999px;--sn-stroke-default:1px;--sn-stroke-focus:2px;--sn-duration-fast:.12s;--sn-duration-normal:.2s;--sn-easing-standard:cubic-bezier(.2, 0, 0, 1);--sn-duration-loop:1s;--sn-opacity-disabled:.45;--sn-font-sans:"IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;--sn-font-mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;--sn-text-display:32px;--sn-text-display--line-height:40px;--sn-text-display--font-weight:600;--sn-text-display--letter-spacing:-.02em;--sn-text-title:20px;--sn-text-title--line-height:28px;--sn-text-title--font-weight:600;--sn-text-title--letter-spacing:-.01em;--sn-text-body:14px;--sn-text-body--line-height:20px;--sn-text-body--font-weight:400;--sn-text-body--letter-spacing:0em;--sn-text-body-strong:14px;--sn-text-body-strong--line-height:20px;--sn-text-body-strong--font-weight:500;--sn-text-body-strong--letter-spacing:0em;--sn-text-label:13px;--sn-text-label--line-height:16px;--sn-text-label--font-weight:500;--sn-text-label--letter-spacing:0em;--sn-text-caption:12px;--sn-text-caption--line-height:16px;--sn-text-caption--font-weight:400;--sn-text-caption--letter-spacing:0em;--sn-text-mono:14px;--sn-text-mono--line-height:20px;--sn-text-mono--font-weight:400;--sn-text-mono--letter-spacing:0em;--sn-text-mono-caption:12px;--sn-text-mono-caption--line-height:16px;--sn-text-mono-caption--font-weight:400;--sn-text-mono-caption--letter-spacing:0em}:root,[data-theme=light]{color-scheme:light;--sn-color-bg-canvas:#f4f6f8;--sn-color-bg-surface:#fff;--sn-color-bg-subtle:#e6eaee;--sn-color-bg-primary:#2340d0;--sn-color-bg-primary-hover:#1b33ae;--sn-color-bg-danger:#c8312b;--sn-color-bg-overlay:#0e121680;--sn-color-text-default:#0e1216;--sn-color-text-muted:#5b6672;--sn-color-text-on-primary:#fff;--sn-color-text-on-danger:#fff;--sn-color-text-danger:#c8312b;--sn-color-border-default:#d5dbe1;--sn-color-border-strong:#8d99a6;--sn-color-border-focus:#2340d0;--sn-color-border-danger:#c8312b;--sn-color-bg-inverse:#0e1216;--sn-color-text-inverse:#fff;--sn-color-bg-success:#16804f;--sn-color-text-on-success:#fff;--sn-color-bg-success-subtle:#eaf7f0;--sn-color-text-success:#0f6640;--sn-color-bg-warning:#e3a43a;--sn-color-text-on-warning:#0e1216;--sn-color-bg-warning-subtle:#fcf5e6;--sn-color-text-warning:#85520a;--sn-color-bg-danger-subtle:#fcedec;--sn-color-border-success:#16804f;--sn-color-border-warning:#a86c12}[data-theme=dark]{color-scheme:dark;--sn-color-bg-canvas:#0e1216;--sn-color-bg-surface:#161b21;--sn-color-bg-subtle:#1e252d;--sn-color-bg-primary:#6e86ff;--sn-color-bg-primary-hover:#8c9fff;--sn-color-bg-danger:#f06a5f;--sn-color-bg-overlay:#0e121680;--sn-color-text-default:#e6eaee;--sn-color-text-muted:#8d99a6;--sn-color-text-on-primary:#0e1216;--sn-color-text-on-danger:#0e1216;--sn-color-text-danger:#f06a5f;--sn-color-border-default:#2a323b;--sn-color-border-strong:#5b6672;--sn-color-border-focus:#6e86ff;--sn-color-border-danger:#f06a5f;--sn-color-bg-inverse:#e6eaee;--sn-color-text-inverse:#0e1216;--sn-color-bg-success:#3db882;--sn-color-text-on-success:#0e1216;--sn-color-bg-success-subtle:#0b2218;--sn-color-text-success:#6bd1a0;--sn-color-bg-warning:#e3a43a;--sn-color-text-on-warning:#0e1216;--sn-color-bg-warning-subtle:#24190a;--sn-color-text-warning:#eec46a;--sn-color-bg-danger-subtle:#2b1311;--sn-color-border-success:#3db882;--sn-color-border-warning:#e3a43a}@keyframes sn-pulse{50%{opacity:.5}}@keyframes sn-spin{to{transform:rotate(360deg)}}}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* 생성물: scripts/build-tokens.mjs. 손으로 수정 금지 (원본: Figma → scripts/figma-tokens.json) */
|
|
2
|
+
/* Tailwind v4 @theme 매핑. sn 토큰을 Tailwind 클래스 이름으로 연결해요 (bg-primary, text-muted, rounded-md, text-body …).
|
|
3
|
+
inline reference: 클래스가 var(--sn-*)를 직접 참조하고, Tailwind 테마 변수는 따로 출력하지 않음 */
|
|
4
|
+
@theme inline reference {
|
|
5
|
+
--spacing: var(--sn-space-1);
|
|
6
|
+
--radius-sm: var(--sn-radius-sm);
|
|
7
|
+
--radius-md: var(--sn-radius-md);
|
|
8
|
+
--radius-lg: var(--sn-radius-lg);
|
|
9
|
+
--radius-full: var(--sn-radius-full);
|
|
10
|
+
--ease-standard: var(--sn-easing-standard);
|
|
11
|
+
--font-sans: var(--sn-font-sans);
|
|
12
|
+
--font-mono: var(--sn-font-mono);
|
|
13
|
+
--text-display: var(--sn-text-display);
|
|
14
|
+
--text-display--line-height: var(--sn-text-display--line-height);
|
|
15
|
+
--text-display--font-weight: var(--sn-text-display--font-weight);
|
|
16
|
+
--text-display--letter-spacing: var(--sn-text-display--letter-spacing);
|
|
17
|
+
--text-title: var(--sn-text-title);
|
|
18
|
+
--text-title--line-height: var(--sn-text-title--line-height);
|
|
19
|
+
--text-title--font-weight: var(--sn-text-title--font-weight);
|
|
20
|
+
--text-title--letter-spacing: var(--sn-text-title--letter-spacing);
|
|
21
|
+
--text-body: var(--sn-text-body);
|
|
22
|
+
--text-body--line-height: var(--sn-text-body--line-height);
|
|
23
|
+
--text-body--font-weight: var(--sn-text-body--font-weight);
|
|
24
|
+
--text-body--letter-spacing: var(--sn-text-body--letter-spacing);
|
|
25
|
+
--text-body-strong: var(--sn-text-body-strong);
|
|
26
|
+
--text-body-strong--line-height: var(--sn-text-body-strong--line-height);
|
|
27
|
+
--text-body-strong--font-weight: var(--sn-text-body-strong--font-weight);
|
|
28
|
+
--text-body-strong--letter-spacing: var(--sn-text-body-strong--letter-spacing);
|
|
29
|
+
--text-label: var(--sn-text-label);
|
|
30
|
+
--text-label--line-height: var(--sn-text-label--line-height);
|
|
31
|
+
--text-label--font-weight: var(--sn-text-label--font-weight);
|
|
32
|
+
--text-label--letter-spacing: var(--sn-text-label--letter-spacing);
|
|
33
|
+
--text-caption: var(--sn-text-caption);
|
|
34
|
+
--text-caption--line-height: var(--sn-text-caption--line-height);
|
|
35
|
+
--text-caption--font-weight: var(--sn-text-caption--font-weight);
|
|
36
|
+
--text-caption--letter-spacing: var(--sn-text-caption--letter-spacing);
|
|
37
|
+
--background-color-canvas: var(--sn-color-bg-canvas);
|
|
38
|
+
--background-color-surface: var(--sn-color-bg-surface);
|
|
39
|
+
--background-color-subtle: var(--sn-color-bg-subtle);
|
|
40
|
+
--background-color-primary: var(--sn-color-bg-primary);
|
|
41
|
+
--background-color-primary-hover: var(--sn-color-bg-primary-hover);
|
|
42
|
+
--background-color-danger: var(--sn-color-bg-danger);
|
|
43
|
+
--background-color-overlay: var(--sn-color-bg-overlay);
|
|
44
|
+
--text-color-default: var(--sn-color-text-default);
|
|
45
|
+
--text-color-muted: var(--sn-color-text-muted);
|
|
46
|
+
--text-color-on-primary: var(--sn-color-text-on-primary);
|
|
47
|
+
--text-color-on-danger: var(--sn-color-text-on-danger);
|
|
48
|
+
--text-color-danger: var(--sn-color-text-danger);
|
|
49
|
+
--border-color-default: var(--sn-color-border-default);
|
|
50
|
+
--outline-color-default: var(--sn-color-border-default);
|
|
51
|
+
--border-color-strong: var(--sn-color-border-strong);
|
|
52
|
+
--outline-color-strong: var(--sn-color-border-strong);
|
|
53
|
+
--border-color-focus: var(--sn-color-border-focus);
|
|
54
|
+
--outline-color-focus: var(--sn-color-border-focus);
|
|
55
|
+
--border-color-danger: var(--sn-color-border-danger);
|
|
56
|
+
--outline-color-danger: var(--sn-color-border-danger);
|
|
57
|
+
--background-color-inverse: var(--sn-color-bg-inverse);
|
|
58
|
+
--text-color-inverse: var(--sn-color-text-inverse);
|
|
59
|
+
--background-color-success: var(--sn-color-bg-success);
|
|
60
|
+
--text-color-on-success: var(--sn-color-text-on-success);
|
|
61
|
+
--background-color-success-subtle: var(--sn-color-bg-success-subtle);
|
|
62
|
+
--text-color-success: var(--sn-color-text-success);
|
|
63
|
+
--background-color-warning: var(--sn-color-bg-warning);
|
|
64
|
+
--text-color-on-warning: var(--sn-color-text-on-warning);
|
|
65
|
+
--background-color-warning-subtle: var(--sn-color-bg-warning-subtle);
|
|
66
|
+
--text-color-warning: var(--sn-color-text-warning);
|
|
67
|
+
--background-color-danger-subtle: var(--sn-color-bg-danger-subtle);
|
|
68
|
+
--border-color-success: var(--sn-color-border-success);
|
|
69
|
+
--outline-color-success: var(--sn-color-border-success);
|
|
70
|
+
--border-color-warning: var(--sn-color-border-warning);
|
|
71
|
+
--outline-color-warning: var(--sn-color-border-warning);
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// DESIGN.md를 지키게 하는 ESLint 규칙 (flat config 조각). 이 저장소에서는 켜지 않고 제공만 함.
|
|
2
|
+
// 쓰는 쪽: import sonicDesign from '@xonic0506/sonic-ui/eslint-design-rules'; export default [..., sonicDesign];
|
|
3
|
+
const className = "JSXAttribute[name.name='className']";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'sonic-ui/design-rules',
|
|
7
|
+
files: ['**/*.{jsx,tsx}'],
|
|
8
|
+
rules: {
|
|
9
|
+
'no-restricted-syntax': [
|
|
10
|
+
'error',
|
|
11
|
+
{
|
|
12
|
+
selector: `${className} Literal[value=/(^|\\s)[\\w:-]*-\\[[^\\]]+\\]/], ${className} TemplateElement[value.raw=/(^|\\s)[\\w:-]*-\\[[^\\]]+\\]/]`,
|
|
13
|
+
message: 'DESIGN.md: 임의의 값 클래스(p-[7px] 등) 대신 토큰 클래스를 쓰세요.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
selector: `${className} Literal[value=/(^|\\s)[\\w:-]*(bg|text|border|outline|ring|fill|stroke)-(red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone)-\\d+/]`,
|
|
17
|
+
message: 'DESIGN.md: Tailwind 기본 팔레트 대신 sn 토큰 색(bg-primary, text-muted …)을 쓰세요.',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
selector: 'Literal[value=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/]',
|
|
21
|
+
message: 'DESIGN.md: hex 색을 직접 쓰지 말고 var(--sn-color-…) 토큰을 쓰세요.',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xonic0506/sonic-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Figma-first personal design system (Base UI + Tailwind v4, prebuilt CSS)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./styles.css": "./dist/styles.css",
|
|
16
|
+
"./tailwind.css": "./dist/tailwind.css",
|
|
17
|
+
"./DESIGN.md": "./DESIGN.md",
|
|
18
|
+
"./AI-RULES.md": "./AI-RULES.md",
|
|
19
|
+
"./eslint-design-rules": "./lint/design-rules.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"lint",
|
|
24
|
+
"DESIGN.md",
|
|
25
|
+
"AI-RULES.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"tokens": "node scripts/build-tokens.mjs",
|
|
29
|
+
"build:css": "node scripts/build-css.mjs",
|
|
30
|
+
"build:js": "vite build && tsc -p tsconfig.build.json",
|
|
31
|
+
"build": "rm -rf dist && npm run tokens && npm run build:js && npm run build:css",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"playground": "vite --config playground/vite.config.ts",
|
|
35
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run check:contrast && npm run build",
|
|
36
|
+
"check:contrast": "node scripts/check-contrast.mjs"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
40
|
+
"react-dom": "^18.2.0 || ^19.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@base-ui/react": "^1.9.0",
|
|
44
|
+
"class-variance-authority": "^0.7.1",
|
|
45
|
+
"lucide-react": "^1.59.0",
|
|
46
|
+
"tailwind-merge": "^3.7.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@tailwindcss/cli": "^4.3.3",
|
|
50
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
51
|
+
"@testing-library/react": "^16.3.3",
|
|
52
|
+
"@testing-library/user-event": "^14.6.8",
|
|
53
|
+
"@types/react": "^19.3.0",
|
|
54
|
+
"@types/react-dom": "^19.3.0",
|
|
55
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
56
|
+
"jsdom": "^30.1.3",
|
|
57
|
+
"react": "^19.3.1",
|
|
58
|
+
"react-dom": "^19.3.1",
|
|
59
|
+
"tailwindcss": "^4.3.3",
|
|
60
|
+
"typescript": "^7.0.2",
|
|
61
|
+
"vite": "^8.3.4",
|
|
62
|
+
"vitest": "^5.0.5"
|
|
63
|
+
}
|
|
64
|
+
}
|