sh-ui-cli 0.80.0 → 0.81.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/data/changelog/versions.json +29 -0
- package/data/registry/react/components/button/index.tailwind.tsx +1 -1
- package/data/registry/react/components/button/styles.css +1 -0
- package/data/registry/react/components/button/styles.css.ts +1 -0
- package/data/registry/react/components/button/styles.module.css +1 -0
- package/data/registry/react/components/combobox/index.tailwind.tsx +1 -1
- package/data/registry/react/components/combobox/styles.css +1 -1
- package/data/registry/react/components/combobox/styles.module.css +1 -1
- package/data/registry/react/components/context-menu/index.tailwind.tsx +1 -1
- package/data/registry/react/components/context-menu/styles.css +1 -1
- package/data/registry/react/components/context-menu/styles.module.css +1 -1
- package/data/registry/react/components/dropdown-menu/index.module.tsx +22 -7
- package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +18 -3
- package/data/registry/react/components/dropdown-menu/index.tsx +24 -7
- package/data/registry/react/components/dropdown-menu/styles.css +1 -1
- package/data/registry/react/components/dropdown-menu/styles.module.css +1 -1
- package/data/registry/react/components/markdown-editor/index.module.tsx +1 -1
- package/data/registry/react/components/markdown-editor/index.tailwind.tsx +1 -1
- package/data/registry/react/components/markdown-editor/index.tsx +1 -1
- package/data/registry/react/components/select/index.tailwind.tsx +1 -1
- package/data/registry/react/components/select/styles.css +1 -1
- package/data/registry/react/components/select/styles.module.css +1 -1
- package/data/registry/react/components/sidebar/index.module.tsx +1 -1
- package/data/registry/react/components/sidebar/index.tailwind.tsx +1 -1
- package/data/registry/react/components/sidebar/index.tsx +1 -1
- package/data/registry/react/tokens-used.json +13 -13
- package/data/tokens/src/primitives.json +3 -3
- package/package.json +1 -1
- package/src/add.mjs +13 -1
- package/src/create/generator.js +34 -1
- package/src/create/templateManifest.js +1 -0
- package/src/create/theme/decode.js +5 -0
- package/src/create/theme/presets.js +44 -0
- package/templates/monorepo/packages/typescript-config/base.json +0 -1
- package/templates/monorepo/packages/ui/ui-core/package.json +2 -1
- package/templates/monorepo/packages/ui/ui-core/sh-ui.config.json +2 -1
- package/templates/monorepo/packages/ui/ui-core/src/styles/.gitkeep +0 -0
- package/templates/nextjs-standalone/_arch/flat/lib/styles/tokens.css +36 -3
- package/templates/nextjs-standalone/_arch/flat/tsconfig.json +0 -1
- package/templates/nextjs-standalone/_arch/fsd/src/shared/styles/tokens.css +36 -3
- package/templates/nextjs-standalone/_arch/fsd/tsconfig.json +0 -1
- package/templates/nextjs-standalone/_arch/mes/src/lib/styles/tokens.css +2 -2
- package/templates/nextjs-standalone/_arch/mes/tsconfig.json +0 -1
- package/templates/ui-app-template/src/styles/globals.css +10 -0
- package/templates/ui-app-template/src/styles/tokens.css +36 -3
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
|
|
4
4
|
"versions": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.81.0",
|
|
7
|
+
"date": "2026-05-13",
|
|
8
|
+
"title": "atlas P0/P1 일괄 — monorepo create_project · registry · 디자인 톤",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**monorepo `create_project` 자동완성 흐름** — (1) ui-core `sh-ui.config.json` 의 `cssFramework` 가 항상 `plain` 으로 emit 되던 결함 수정 (호출 시점 인자가 ui-core/ui-app 양쪽에 반영), (2) ui-app `globals.css` 의 `@source` 에 ui-core 경로 누락 수정 (`../../../../ui-core/src/**/*.{ts,tsx}` 자동 박힘), (3) utility CSS 5종 (`base · focus-ring · animations · breakpoints · z-index`) 을 스캐폴드 시점에 ui-core 에 자동 emit + ui-app `globals.css` 에 sentinel-bounded `@import` 자동 박기. `create → dev` 가 매뉴얼 보정 없이 굴러감.",
|
|
12
|
+
"**registry `.tsx` cross-import 잔존 6건 일괄 제거** — v0.80.1 에서 sidebar/plain 한 개만 고쳤던 누락 후속. sidebar `tailwind/module` + markdown-editor `plain/tailwind/module` + docs 듀얼카피 markdown-editor 의 `../code-editor/index.tsx` / `../popover/index.tsx` 를 확장자 없는 형태로 통일. Next.js + TS 디폴트(`allowImportingTsExtensions: false`) 에서 빌드 깨지던 결함 해소.",
|
|
13
|
+
"**DropdownMenu / Select / Combobox / ContextMenu z-index `--z-dropdown` (200) → `--z-popover` (500)** — Sidebar offcanvas drawer 의 `--z-overlay` (300) 아래로 깔려 안 보이던 결함 수정. 드로어 안에 있는 메뉴/콤보가 정상 위로 뜸. 듀얼 카피본까지 일괄.",
|
|
14
|
+
"**DropdownMenuLabel free-standing 자동 self-wrap** — Base UI 의 `Menu.GroupLabel` 이 `Menu.Group` 부재 시 \"MenuGroupRootContext is missing\" 으로 런타임 throw 하던 마이그레이션 함정 해소. 자체 Context 로 부모 Group 감지, 없으면 Label 이 스스로 `Menu.Group` 으로 wrap. 명시적 Group 안에 두면 중첩 없이 그대로 동작 — shadcn 코드 그대로 옮겨와도 OK.",
|
|
15
|
+
"**디폴트 control 사이즈 다운 (40 → 36)** — `--control-md` 가 데스크탑 SaaS/대시보드 톤에 비해 통통하다는 atlas 측정(`size='sm'` 24건, `size='lg'` 0건) 반영. `--control-sm/md/lg` 를 `2rem / 2.25rem / 2.5rem` (32/36/40) 로. 모바일 터치 점프(44) 는 그대로. 기존 프로젝트의 `tokens.css` 는 미변경 — 신규 프로젝트만 새 디폴트.",
|
|
16
|
+
"**Button `white-space: nowrap`** — 좁은 컨테이너에서 한글 라벨이 줄바꿈되며 버튼 높이 60px+ 로 부풀던 결함 수정. 4 변종 (plain/tailwind/module/vanilla-extract) + 듀얼카피 일괄.",
|
|
17
|
+
"**`noUncheckedIndexedAccess` 디폴트 off** — 스캐폴드의 `typescript-config/base.json` + nextjs-standalone 3 arch 의 tsconfig 에서 제거. registry 컴포넌트들이 index access 결과를 narrowing 없이 쓰는 패턴이라 신규 사용자가 첫 `typecheck` 부터 실패하던 결함 해소.",
|
|
18
|
+
"**`add tokens` 가 ui-core 에 대해 던지는 에러 메시지에 가이드 추가** — `paths.tokens 가 설정에 없습니다` 만 던지던 것을 v0.65 layout 설명 + `cwd` 를 `packages/ui/ui-apps/ui-<name>` 로 바꾸라는 한 줄 안내로 보강."
|
|
19
|
+
],
|
|
20
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.81.0"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"version": "0.80.1",
|
|
24
|
+
"date": "2026-05-12",
|
|
25
|
+
"title": "monorepo create_project P0 fix — paths.styles · sidebar 토큰 · sidebar import",
|
|
26
|
+
"type": "patch",
|
|
27
|
+
"highlights": [
|
|
28
|
+
"**A1/A2: `create_project --structure monorepo` ui-core 가 styles 경로 누락하던 결함 수정** — `sh-ui.config.json` 에 `paths.styles: \"src/styles\"`, `package.json` `exports` 에 `\"./styles/*\": \"./src/styles/*\"` 추가. 이전엔 직후 `add base focus-ring …` 호출이 `paths.styles 가 sh-ui.config.json에 없습니다` 로 즉시 실패.",
|
|
29
|
+
"**A3: 신규 프로젝트 tokens.css 에 sidebar/success/warning/info 토큰 emit** — sidebar 컴포넌트가 요구하는 `--sidebar-bg / --sidebar-fg / --sidebar-border / --sidebar-accent / --sidebar-accent-fg` 가 신규 프로젝트 tokens.css 에 빠져 sidebar 가 `@theme inline` 매핑에서 undefined 로 풀리던 결함 수정. presets(neutral · slate · rose · emerald · violet) light/dark 양쪽에 5+6 토큰 추가, `OPTIONAL_TOKEN_KEYS` 에 sidebar 키 등록. 베이스라인 3개 tokens.css 템플릿도 동기화.",
|
|
30
|
+
"**B1: registry/docs sidebar `.tsx` 확장자 import 제거** — `import { Popover } from \"../popover/index.tsx\"` 가 TS5097 (allowImportingTsExtensions) 로 docs typecheck 실패하던 결함 수정. 듀얼 카피본 양쪽 수정."
|
|
31
|
+
],
|
|
32
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.80.1"
|
|
33
|
+
},
|
|
5
34
|
{
|
|
6
35
|
"version": "0.80.0",
|
|
7
36
|
"date": "2026-05-12",
|
|
@@ -3,7 +3,7 @@ import { cn } from "@SH_UI_UTILS@";
|
|
|
3
3
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
|
|
5
5
|
const buttonVariants = cva(
|
|
6
|
-
"inline-flex items-center justify-center gap-[var(--space-2)] border border-transparent rounded-[var(--radius)] font-medium leading-none cursor-pointer select-none transition-[background-color,color,border-color] duration-[var(--duration-fast)] disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 active:scale-[0.97]",
|
|
6
|
+
"inline-flex items-center justify-center gap-[var(--space-2)] border border-transparent rounded-[var(--radius)] font-medium leading-none whitespace-nowrap cursor-pointer select-none transition-[background-color,color,border-color] duration-[var(--duration-fast)] disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 active:scale-[0.97]",
|
|
7
7
|
{
|
|
8
8
|
variants: {
|
|
9
9
|
variant: {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
border-radius: var(--radius);
|
|
8
8
|
font-weight: var(--weight-medium);
|
|
9
9
|
line-height: 1;
|
|
10
|
+
white-space: nowrap;
|
|
10
11
|
cursor: pointer;
|
|
11
12
|
transition: background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast),
|
|
12
13
|
transform 80ms ease-out, filter 80ms;
|
|
@@ -9,6 +9,7 @@ export const button = style({
|
|
|
9
9
|
borderRadius: "var(--radius)",
|
|
10
10
|
fontWeight: "var(--weight-medium)",
|
|
11
11
|
lineHeight: 1,
|
|
12
|
+
whiteSpace: "nowrap",
|
|
12
13
|
cursor: "pointer",
|
|
13
14
|
transition:
|
|
14
15
|
"background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast), transform 80ms ease-out, filter 80ms",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
border-radius: var(--radius);
|
|
8
8
|
font-weight: var(--weight-medium);
|
|
9
9
|
line-height: 1;
|
|
10
|
+
white-space: nowrap;
|
|
10
11
|
cursor: pointer;
|
|
11
12
|
transition: background-color var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast),
|
|
12
13
|
transform 80ms ease-out, filter 80ms;
|
|
@@ -53,7 +53,7 @@ export const ComboboxContent = React.forwardRef<
|
|
|
53
53
|
return (
|
|
54
54
|
<BaseCombobox.Portal container={container}>
|
|
55
55
|
<BaseCombobox.Positioner
|
|
56
|
-
className="z-[var(--z-
|
|
56
|
+
className="z-[var(--z-popover)] outline-none w-[var(--anchor-width)]"
|
|
57
57
|
sideOffset={sideOffset}
|
|
58
58
|
align="start"
|
|
59
59
|
>
|
|
@@ -29,7 +29,7 @@ export const ContextMenuContent = React.forwardRef<HTMLDivElement, ContextMenuCo
|
|
|
29
29
|
function ContextMenuContent({ className, children, container, ...props }, ref) {
|
|
30
30
|
return (
|
|
31
31
|
<BaseContextMenu.Portal container={container}>
|
|
32
|
-
<BaseContextMenu.Positioner className="outline-none z-[var(--z-
|
|
32
|
+
<BaseContextMenu.Positioner className="outline-none z-[var(--z-popover)]">
|
|
33
33
|
<BaseContextMenu.Popup ref={ref} className={cn(contentClasses, className)} {...props}>
|
|
34
34
|
{children}
|
|
35
35
|
</BaseContextMenu.Popup>
|
|
@@ -149,18 +149,26 @@ export const DropdownMenuRadioItem = React.forwardRef<
|
|
|
149
149
|
);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
/* ───────── Group / Label ─────────
|
|
152
|
+
/* ───────── Group / Label ─────────
|
|
153
|
+
*
|
|
154
|
+
* Base UI Menu.GroupLabel 은 Menu.Group 안에서만 동작 — free-standing 시 throw.
|
|
155
|
+
* DropdownMenuLabel 이 부모 Group 부재 시 자체 Group 으로 self-wrap.
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
const InDmGroupContext = React.createContext(false);
|
|
153
159
|
|
|
154
160
|
export const DropdownMenuGroup = React.forwardRef<
|
|
155
161
|
HTMLDivElement,
|
|
156
162
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
|
|
157
163
|
>(function DropdownMenuGroup({ className, ...props }, ref) {
|
|
158
164
|
return (
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
<InDmGroupContext.Provider value={true}>
|
|
166
|
+
<BaseMenu.Group
|
|
167
|
+
ref={ref}
|
|
168
|
+
className={cn(styles.dm__group, className)}
|
|
169
|
+
{...props}
|
|
170
|
+
/>
|
|
171
|
+
</InDmGroupContext.Provider>
|
|
164
172
|
);
|
|
165
173
|
});
|
|
166
174
|
|
|
@@ -168,13 +176,20 @@ export const DropdownMenuLabel = React.forwardRef<
|
|
|
168
176
|
HTMLDivElement,
|
|
169
177
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
|
|
170
178
|
>(function DropdownMenuLabel({ className, ...props }, ref) {
|
|
171
|
-
|
|
179
|
+
const inGroup = React.useContext(InDmGroupContext);
|
|
180
|
+
const label = (
|
|
172
181
|
<BaseMenu.GroupLabel
|
|
173
182
|
ref={ref}
|
|
174
183
|
className={cn(styles.dm__label, className)}
|
|
175
184
|
{...props}
|
|
176
185
|
/>
|
|
177
186
|
);
|
|
187
|
+
if (inGroup) return label;
|
|
188
|
+
return (
|
|
189
|
+
<InDmGroupContext.Provider value={true}>
|
|
190
|
+
<BaseMenu.Group>{label}</BaseMenu.Group>
|
|
191
|
+
</InDmGroupContext.Provider>
|
|
192
|
+
);
|
|
178
193
|
});
|
|
179
194
|
|
|
180
195
|
/* ───────── Separator ─────────
|
|
@@ -52,7 +52,7 @@ export const DropdownMenuContent = React.forwardRef<HTMLDivElement, DropdownMenu
|
|
|
52
52
|
return (
|
|
53
53
|
<BaseMenu.Portal container={container}>
|
|
54
54
|
<BaseMenu.Positioner
|
|
55
|
-
className="outline-none z-[var(--z-
|
|
55
|
+
className="outline-none z-[var(--z-popover)]"
|
|
56
56
|
side={side}
|
|
57
57
|
align={align}
|
|
58
58
|
sideOffset={sideOffset}
|
|
@@ -120,18 +120,27 @@ export const DropdownMenuRadioItem = React.forwardRef<
|
|
|
120
120
|
);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
+
// Base UI Menu.GroupLabel 은 Menu.Group 안에 있어야만 동작 — free-standing 시 throw.
|
|
124
|
+
// DropdownMenuLabel 이 부모 Group 부재 시 자체 Group 으로 self-wrap 하도록 Context 로 감지.
|
|
125
|
+
const InDmGroupContext = React.createContext(false);
|
|
126
|
+
|
|
123
127
|
export const DropdownMenuGroup = React.forwardRef<
|
|
124
128
|
HTMLDivElement,
|
|
125
129
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
|
|
126
130
|
>(function DropdownMenuGroup({ className, ...props }, ref) {
|
|
127
|
-
return
|
|
131
|
+
return (
|
|
132
|
+
<InDmGroupContext.Provider value={true}>
|
|
133
|
+
<BaseMenu.Group ref={ref} className={cn("p-0", className)} {...props} />
|
|
134
|
+
</InDmGroupContext.Provider>
|
|
135
|
+
);
|
|
128
136
|
});
|
|
129
137
|
|
|
130
138
|
export const DropdownMenuLabel = React.forwardRef<
|
|
131
139
|
HTMLDivElement,
|
|
132
140
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
|
|
133
141
|
>(function DropdownMenuLabel({ className, ...props }, ref) {
|
|
134
|
-
|
|
142
|
+
const inGroup = React.useContext(InDmGroupContext);
|
|
143
|
+
const label = (
|
|
135
144
|
<BaseMenu.GroupLabel
|
|
136
145
|
ref={ref}
|
|
137
146
|
className={cn(
|
|
@@ -141,6 +150,12 @@ export const DropdownMenuLabel = React.forwardRef<
|
|
|
141
150
|
{...props}
|
|
142
151
|
/>
|
|
143
152
|
);
|
|
153
|
+
if (inGroup) return label;
|
|
154
|
+
return (
|
|
155
|
+
<InDmGroupContext.Provider value={true}>
|
|
156
|
+
<BaseMenu.Group>{label}</BaseMenu.Group>
|
|
157
|
+
</InDmGroupContext.Provider>
|
|
158
|
+
);
|
|
144
159
|
});
|
|
145
160
|
|
|
146
161
|
export const DropdownMenuSeparator = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
@@ -149,18 +149,28 @@ export const DropdownMenuRadioItem = React.forwardRef<
|
|
|
149
149
|
);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
/* ───────── Group / Label ─────────
|
|
152
|
+
/* ───────── Group / Label ─────────
|
|
153
|
+
*
|
|
154
|
+
* Base UI Menu.GroupLabel 은 Menu.Group 안에 있어야만 동작 (없으면 런타임 throw —
|
|
155
|
+
* "MenuGroupRootContext is missing"). shadcn 등에선 Label 이 free-standing 으로도
|
|
156
|
+
* 쓰여서 마이그레이션 시 사용자가 잘 찔린다. DropdownMenuLabel 이 부모 Group 부재 시
|
|
157
|
+
* 자체로 Group 으로 self-wrap 하도록 Context 로 감지.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
const InDmGroupContext = React.createContext(false);
|
|
153
161
|
|
|
154
162
|
export const DropdownMenuGroup = React.forwardRef<
|
|
155
163
|
HTMLDivElement,
|
|
156
164
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
|
|
157
165
|
>(function DropdownMenuGroup({ className, ...props }, ref) {
|
|
158
166
|
return (
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
<InDmGroupContext.Provider value={true}>
|
|
168
|
+
<BaseMenu.Group
|
|
169
|
+
ref={ref}
|
|
170
|
+
className={cn("sh-ui-dm__group", className)}
|
|
171
|
+
{...props}
|
|
172
|
+
/>
|
|
173
|
+
</InDmGroupContext.Provider>
|
|
164
174
|
);
|
|
165
175
|
});
|
|
166
176
|
|
|
@@ -168,13 +178,20 @@ export const DropdownMenuLabel = React.forwardRef<
|
|
|
168
178
|
HTMLDivElement,
|
|
169
179
|
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
|
|
170
180
|
>(function DropdownMenuLabel({ className, ...props }, ref) {
|
|
171
|
-
|
|
181
|
+
const inGroup = React.useContext(InDmGroupContext);
|
|
182
|
+
const label = (
|
|
172
183
|
<BaseMenu.GroupLabel
|
|
173
184
|
ref={ref}
|
|
174
185
|
className={cn("sh-ui-dm__label", className)}
|
|
175
186
|
{...props}
|
|
176
187
|
/>
|
|
177
188
|
);
|
|
189
|
+
if (inGroup) return label;
|
|
190
|
+
return (
|
|
191
|
+
<InDmGroupContext.Provider value={true}>
|
|
192
|
+
<BaseMenu.Group>{label}</BaseMenu.Group>
|
|
193
|
+
</InDmGroupContext.Provider>
|
|
194
|
+
);
|
|
178
195
|
});
|
|
179
196
|
|
|
180
197
|
/* ───────── Separator ─────────
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import ReactMarkdown from "react-markdown";
|
|
5
5
|
import remarkGfm from "remark-gfm";
|
|
6
|
-
import { CodeEditor } from "../code-editor
|
|
6
|
+
import { CodeEditor } from "../code-editor";
|
|
7
7
|
import styles from "./styles.module.css";
|
|
8
8
|
|
|
9
9
|
import { cn } from "@SH_UI_UTILS@";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import ReactMarkdown from "react-markdown";
|
|
5
5
|
import remarkGfm from "remark-gfm";
|
|
6
|
-
import { CodeEditor } from "../code-editor
|
|
6
|
+
import { CodeEditor } from "../code-editor";
|
|
7
7
|
|
|
8
8
|
import { cn } from "@SH_UI_UTILS@";
|
|
9
9
|
export interface MarkdownEditorProps {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import ReactMarkdown from "react-markdown";
|
|
5
5
|
import remarkGfm from "remark-gfm";
|
|
6
|
-
import { CodeEditor } from "../code-editor
|
|
6
|
+
import { CodeEditor } from "../code-editor";
|
|
7
7
|
import "./styles.css";
|
|
8
8
|
|
|
9
9
|
import { cn } from "@SH_UI_UTILS@";
|
|
@@ -76,7 +76,7 @@ export const SelectContent = React.forwardRef<
|
|
|
76
76
|
}
|
|
77
77
|
>(({ className, children, container, ...props }, ref) => (
|
|
78
78
|
<BaseSelect.Portal container={container}>
|
|
79
|
-
<BaseSelect.Positioner className="outline-none z-[var(--z-
|
|
79
|
+
<BaseSelect.Positioner className="outline-none z-[var(--z-popover)]" sideOffset={4} align="start">
|
|
80
80
|
<BaseSelect.Popup
|
|
81
81
|
ref={ref}
|
|
82
82
|
className={cn(
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "@SH_UI_UTILS@";
|
|
5
5
|
import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
|
|
6
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../popover
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
|
7
7
|
import styles from "./styles.module.css";
|
|
8
8
|
|
|
9
9
|
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "@SH_UI_UTILS@";
|
|
5
5
|
import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
|
|
6
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../popover
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
|
7
7
|
|
|
8
8
|
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
9
9
|
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "@SH_UI_UTILS@";
|
|
5
5
|
import { ChevronRightIcon, PanelLeftIcon } from "lucide-react";
|
|
6
|
-
import { Popover, PopoverContent, PopoverTrigger } from "../popover
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
|
7
7
|
import "./styles.css";
|
|
8
8
|
|
|
9
9
|
const SIDEBAR_COOKIE_NAME = "sidebar_state";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$description": "컴포넌트별 토큰 의존성 (var(--*) 추출). build-registry-tokens.mjs 가 자동 생성.",
|
|
3
|
-
"$generated": "2026-05-
|
|
3
|
+
"$generated": "2026-05-12T23:53:11.079Z",
|
|
4
4
|
"components": {
|
|
5
5
|
"button": {
|
|
6
6
|
"plain": [
|
|
@@ -600,7 +600,7 @@
|
|
|
600
600
|
"--text-sm",
|
|
601
601
|
"--text-xs",
|
|
602
602
|
"--weight-semibold",
|
|
603
|
-
"--z-
|
|
603
|
+
"--z-popover"
|
|
604
604
|
],
|
|
605
605
|
"tailwind": [
|
|
606
606
|
"--border-width-strong",
|
|
@@ -614,7 +614,7 @@
|
|
|
614
614
|
"--space-3",
|
|
615
615
|
"--text-sm",
|
|
616
616
|
"--text-xs",
|
|
617
|
-
"--z-
|
|
617
|
+
"--z-popover"
|
|
618
618
|
],
|
|
619
619
|
"css-modules": [
|
|
620
620
|
"--background",
|
|
@@ -636,7 +636,7 @@
|
|
|
636
636
|
"--text-sm",
|
|
637
637
|
"--text-xs",
|
|
638
638
|
"--weight-semibold",
|
|
639
|
-
"--z-
|
|
639
|
+
"--z-popover"
|
|
640
640
|
],
|
|
641
641
|
"vanilla-extract": []
|
|
642
642
|
},
|
|
@@ -1034,7 +1034,7 @@
|
|
|
1034
1034
|
"--text-xs",
|
|
1035
1035
|
"--weight-medium",
|
|
1036
1036
|
"--weight-semibold",
|
|
1037
|
-
"--z-
|
|
1037
|
+
"--z-popover"
|
|
1038
1038
|
],
|
|
1039
1039
|
"tailwind": [
|
|
1040
1040
|
"--border-width-strong",
|
|
@@ -1048,7 +1048,7 @@
|
|
|
1048
1048
|
"--space-3",
|
|
1049
1049
|
"--text-sm",
|
|
1050
1050
|
"--text-xs",
|
|
1051
|
-
"--z-
|
|
1051
|
+
"--z-popover"
|
|
1052
1052
|
],
|
|
1053
1053
|
"css-modules": [
|
|
1054
1054
|
"--background",
|
|
@@ -1072,7 +1072,7 @@
|
|
|
1072
1072
|
"--text-xs",
|
|
1073
1073
|
"--weight-medium",
|
|
1074
1074
|
"--weight-semibold",
|
|
1075
|
-
"--z-
|
|
1075
|
+
"--z-popover"
|
|
1076
1076
|
],
|
|
1077
1077
|
"vanilla-extract": []
|
|
1078
1078
|
},
|
|
@@ -1202,7 +1202,7 @@
|
|
|
1202
1202
|
"--text-sm",
|
|
1203
1203
|
"--text-xs",
|
|
1204
1204
|
"--weight-semibold",
|
|
1205
|
-
"--z-
|
|
1205
|
+
"--z-popover"
|
|
1206
1206
|
],
|
|
1207
1207
|
"tailwind": [
|
|
1208
1208
|
"--border-width-strong",
|
|
@@ -1213,7 +1213,7 @@
|
|
|
1213
1213
|
"--space-2",
|
|
1214
1214
|
"--text-sm",
|
|
1215
1215
|
"--text-xs",
|
|
1216
|
-
"--z-
|
|
1216
|
+
"--z-popover"
|
|
1217
1217
|
],
|
|
1218
1218
|
"css-modules": [
|
|
1219
1219
|
"--background",
|
|
@@ -1230,7 +1230,7 @@
|
|
|
1230
1230
|
"--text-sm",
|
|
1231
1231
|
"--text-xs",
|
|
1232
1232
|
"--weight-semibold",
|
|
1233
|
-
"--z-
|
|
1233
|
+
"--z-popover"
|
|
1234
1234
|
],
|
|
1235
1235
|
"vanilla-extract": []
|
|
1236
1236
|
},
|
|
@@ -1248,7 +1248,7 @@
|
|
|
1248
1248
|
"--text-sm",
|
|
1249
1249
|
"--text-xs",
|
|
1250
1250
|
"--weight-semibold",
|
|
1251
|
-
"--z-
|
|
1251
|
+
"--z-popover"
|
|
1252
1252
|
],
|
|
1253
1253
|
"tailwind": [
|
|
1254
1254
|
"--opacity-disabled",
|
|
@@ -1258,7 +1258,7 @@
|
|
|
1258
1258
|
"--space-2",
|
|
1259
1259
|
"--text-sm",
|
|
1260
1260
|
"--text-xs",
|
|
1261
|
-
"--z-
|
|
1261
|
+
"--z-popover"
|
|
1262
1262
|
],
|
|
1263
1263
|
"css-modules": [
|
|
1264
1264
|
"--background",
|
|
@@ -1273,7 +1273,7 @@
|
|
|
1273
1273
|
"--text-sm",
|
|
1274
1274
|
"--text-xs",
|
|
1275
1275
|
"--weight-semibold",
|
|
1276
|
-
"--z-
|
|
1276
|
+
"--z-popover"
|
|
1277
1277
|
],
|
|
1278
1278
|
"vanilla-extract": []
|
|
1279
1279
|
},
|
|
@@ -113,9 +113,9 @@
|
|
|
113
113
|
},
|
|
114
114
|
|
|
115
115
|
"controlHeight": {
|
|
116
|
-
"sm": { "$value": "2rem",
|
|
117
|
-
"md": { "$value": "2.
|
|
118
|
-
"lg": { "$value": "
|
|
116
|
+
"sm": { "$value": "2rem", "$type": "dimension" },
|
|
117
|
+
"md": { "$value": "2.25rem", "$type": "dimension" },
|
|
118
|
+
"lg": { "$value": "2.5rem", "$type": "dimension" }
|
|
119
119
|
},
|
|
120
120
|
|
|
121
121
|
"borderWidth": {
|
package/package.json
CHANGED
package/src/add.mjs
CHANGED
|
@@ -180,7 +180,19 @@ async function writeOrDiff({ dest, content, cwd, diffMode, summary, conflictReso
|
|
|
180
180
|
/** 특수 컴포넌트: 설정으로 토큰 파일 생성 */
|
|
181
181
|
async function addTokens(config, cwd, diffMode, summary, conflictResolver) {
|
|
182
182
|
const destRel = config.paths?.tokens;
|
|
183
|
-
if (!destRel)
|
|
183
|
+
if (!destRel) {
|
|
184
|
+
// v0.65+ monorepo: ui-core 는 tokens-only 마커가 아니라 컴포넌트-only.
|
|
185
|
+
// tokens 는 ui-apps/ui-<name>/ 의 역할. cwd 가 ui-core 면 안내를 명확히.
|
|
186
|
+
const cwdLower = cwd.replace(/\\/g, '/').toLowerCase();
|
|
187
|
+
if (cwdLower.endsWith('/packages/ui/ui-core')) {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"paths.tokens 가 설정에 없습니다.\n" +
|
|
190
|
+
" ui-core 는 컴포넌트 단일 SoT 라 tokens 를 보관하지 않습니다 (v0.65+ layout).\n" +
|
|
191
|
+
" tokens 는 ui-app 의 역할 — cwd 를 packages/ui/ui-apps/ui-<name>/ 로 바꾸세요.",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
throw new Error("paths.tokens 가 설정에 없습니다.");
|
|
195
|
+
}
|
|
184
196
|
const dest = resolve(cwd, destRel);
|
|
185
197
|
|
|
186
198
|
// theme.base === 'custom' 이면 토큰 빌더가 color.custom.X 스케일을 못 찾아 throw 한다 —
|
package/src/create/generator.js
CHANGED
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
buildDartEasesBlock,
|
|
105
105
|
buildDartGradientsBlock,
|
|
106
106
|
} from './theme/inject.js';
|
|
107
|
-
import { getTemplatesRoot } from '../paths.mjs';
|
|
107
|
+
import { getTemplatesRoot, getRegistryRoot } from '../paths.mjs';
|
|
108
108
|
import {
|
|
109
109
|
CSS_FRAMEWORK_DEFAULT,
|
|
110
110
|
CSS_FRAMEWORKS_SUPPORTED,
|
|
@@ -714,6 +714,39 @@ async function generateMonorepo(targetDir, projectName, plugins, { yes = false,
|
|
|
714
714
|
const uiAppDir = path.join(targetDir, 'packages', 'ui', 'ui-apps', `ui-${appName}`);
|
|
715
715
|
await injectCssTheme(uiAppDir, theme);
|
|
716
716
|
await patchShUiConfig(path.join(uiAppDir, 'sh-ui.config.json'), css, themeBase);
|
|
717
|
+
|
|
718
|
+
// ui-core sh-ui.config.json 의 cssFramework 도 동일하게 맞춘다 (v0.81.0+).
|
|
719
|
+
// 안 그러면 후속 add_component 호출이 plain 변종으로 fallback.
|
|
720
|
+
const uiCoreConfigPath = path.join(targetDir, 'packages', 'ui', 'ui-core', 'sh-ui.config.json');
|
|
721
|
+
await patchShUiConfig(uiCoreConfigPath, css, themeBase);
|
|
722
|
+
|
|
723
|
+
// ui-core 에 utility CSS 5종 자동 설치 (v0.81.0+).
|
|
724
|
+
// ui-app/globals.css 가 이 5개 파일을 @import 하므로 안 깔리면 빌드 깨짐.
|
|
725
|
+
// create → dev 사이에 사용자가 매뉴얼 `add base focus-ring …` 호출 안 해도 굴러가게 함.
|
|
726
|
+
await prefetchUiCoreUtilityCss(path.join(targetDir, 'packages', 'ui', 'ui-core'));
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* v0.81.0+ — monorepo create_project 직후 ui-core 에 베이스 유틸 CSS 를 자동 emit.
|
|
731
|
+
*
|
|
732
|
+
* ui-app-template/src/styles/globals.css 가 `@workspace/ui-core/styles/{base,focus-ring,
|
|
733
|
+
* animations,breakpoints,z-index}.css` 를 sentinel 블록 안에서 @import 하므로 이 파일들이
|
|
734
|
+
* 존재해야 Tailwind v4 빌드가 깨지지 않는다. 외부 의존성 없는 5개 CSS 파일이라 registry
|
|
735
|
+
* 라우팅 거치지 않고 직접 카피 — add.mjs 의 framework 매칭 / placeholder 치환 로직이
|
|
736
|
+
* 필요 없다 (모두 plain CSS, 토큰 변수만 참조).
|
|
737
|
+
*/
|
|
738
|
+
async function prefetchUiCoreUtilityCss(uiCoreDir) {
|
|
739
|
+
const stylesDir = path.join(uiCoreDir, 'src', 'styles');
|
|
740
|
+
await fs.ensureDir(stylesDir);
|
|
741
|
+
const registryRoot = getRegistryRoot('react');
|
|
742
|
+
const names = ['base', 'focus-ring', 'animations', 'breakpoints', 'z-index'];
|
|
743
|
+
for (const name of names) {
|
|
744
|
+
const src = path.join(registryRoot, 'components', name, `${name}.css`);
|
|
745
|
+
const dest = path.join(stylesDir, `${name}.css`);
|
|
746
|
+
if (await fs.pathExists(src) && !(await fs.pathExists(dest))) {
|
|
747
|
+
await fs.copy(src, dest);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
717
750
|
}
|
|
718
751
|
|
|
719
752
|
async function generateApp(targetDir, appName, port, plugins, arch, css = 'tailwind') {
|
|
@@ -41,6 +41,7 @@ export const TEMPLATE_MANIFEST = {
|
|
|
41
41
|
"packages/ui/ui-core/src/components/.gitkeep",
|
|
42
42
|
"packages/ui/ui-core/src/hooks/.gitkeep",
|
|
43
43
|
"packages/ui/ui-core/src/lib/utils.ts",
|
|
44
|
+
"packages/ui/ui-core/src/styles/.gitkeep",
|
|
44
45
|
"packages/ui/ui-core/tsconfig.json",
|
|
45
46
|
"pnpm-workspace.yaml",
|
|
46
47
|
"tsconfig.json",
|
|
@@ -19,6 +19,11 @@ const OPTIONAL_TOKEN_KEYS = [
|
|
|
19
19
|
'info', 'info-foreground',
|
|
20
20
|
'danger-hover',
|
|
21
21
|
'ring',
|
|
22
|
+
// v0.80.1+ — sidebar cascade 색. 5개 모두 light/dark 양쪽에 정의되어야 emit.
|
|
23
|
+
// sidebar 컴포넌트의 styles.css 가 .sh-ui-sidebar-wrapper 에 var(--background-subtle) 등으로
|
|
24
|
+
// fallback 을 두지만, Tailwind @theme inline 의 --color-sidebar-* 가 :root 에서 해석되도록
|
|
25
|
+
// tokens.css 에도 끌어올린다.
|
|
26
|
+
'sidebar-bg', 'sidebar-fg', 'sidebar-border', 'sidebar-accent', 'sidebar-accent-fg',
|
|
22
27
|
];
|
|
23
28
|
|
|
24
29
|
/**
|
|
@@ -24,6 +24,17 @@ const NEUTRAL_LIGHT = {
|
|
|
24
24
|
'danger-foreground': '#FFFFFF',
|
|
25
25
|
'danger-hover': '#B91C1C',
|
|
26
26
|
'ring': '#A3A3A3',
|
|
27
|
+
'success': '#16A34A',
|
|
28
|
+
'success-foreground': '#FFFFFF',
|
|
29
|
+
'warning': '#D97706',
|
|
30
|
+
'warning-foreground': '#FFFFFF',
|
|
31
|
+
'info': '#2563EB',
|
|
32
|
+
'info-foreground': '#FFFFFF',
|
|
33
|
+
'sidebar-bg': '#FAFAFA',
|
|
34
|
+
'sidebar-fg': '#0A0A0A',
|
|
35
|
+
'sidebar-border': '#E5E5E5',
|
|
36
|
+
'sidebar-accent': '#F5F5F5',
|
|
37
|
+
'sidebar-accent-fg': '#0A0A0A',
|
|
27
38
|
};
|
|
28
39
|
|
|
29
40
|
const NEUTRAL_DARK = {
|
|
@@ -44,6 +55,17 @@ const NEUTRAL_DARK = {
|
|
|
44
55
|
'danger-foreground': '#FFFFFF',
|
|
45
56
|
'danger-hover': '#EF4444',
|
|
46
57
|
'ring': '#737373',
|
|
58
|
+
'success': '#22C55E',
|
|
59
|
+
'success-foreground': '#052E16',
|
|
60
|
+
'warning': '#F59E0B',
|
|
61
|
+
'warning-foreground': '#451A03',
|
|
62
|
+
'info': '#3B82F6',
|
|
63
|
+
'info-foreground': '#172554',
|
|
64
|
+
'sidebar-bg': '#171717',
|
|
65
|
+
'sidebar-fg': '#FAFAFA',
|
|
66
|
+
'sidebar-border': '#262626',
|
|
67
|
+
'sidebar-accent': '#262626',
|
|
68
|
+
'sidebar-accent-fg': '#FAFAFA',
|
|
47
69
|
};
|
|
48
70
|
|
|
49
71
|
export const THEME_PRESETS = {
|
|
@@ -73,6 +95,17 @@ export const THEME_PRESETS = {
|
|
|
73
95
|
'danger-foreground': '#FFFFFF',
|
|
74
96
|
'danger-hover': '#B91C1C',
|
|
75
97
|
'ring': '#94A3B8',
|
|
98
|
+
'success': '#16A34A',
|
|
99
|
+
'success-foreground': '#FFFFFF',
|
|
100
|
+
'warning': '#D97706',
|
|
101
|
+
'warning-foreground': '#FFFFFF',
|
|
102
|
+
'info': '#2563EB',
|
|
103
|
+
'info-foreground': '#FFFFFF',
|
|
104
|
+
'sidebar-bg': '#F8FAFC',
|
|
105
|
+
'sidebar-fg': '#0F172A',
|
|
106
|
+
'sidebar-border': '#E2E8F0',
|
|
107
|
+
'sidebar-accent': '#F1F5F9',
|
|
108
|
+
'sidebar-accent-fg': '#0F172A',
|
|
76
109
|
},
|
|
77
110
|
dark: {
|
|
78
111
|
'background': '#0F172A',
|
|
@@ -92,6 +125,17 @@ export const THEME_PRESETS = {
|
|
|
92
125
|
'danger-foreground': '#450A0A',
|
|
93
126
|
'danger-hover': '#FCA5A5',
|
|
94
127
|
'ring': '#64748B',
|
|
128
|
+
'success': '#22C55E',
|
|
129
|
+
'success-foreground': '#052E16',
|
|
130
|
+
'warning': '#F59E0B',
|
|
131
|
+
'warning-foreground': '#451A03',
|
|
132
|
+
'info': '#60A5FA',
|
|
133
|
+
'info-foreground': '#172554',
|
|
134
|
+
'sidebar-bg': '#1E293B',
|
|
135
|
+
'sidebar-fg': '#F1F5F9',
|
|
136
|
+
'sidebar-border': '#334155',
|
|
137
|
+
'sidebar-accent': '#334155',
|
|
138
|
+
'sidebar-accent-fg': '#F1F5F9',
|
|
95
139
|
},
|
|
96
140
|
radius: 0.375,
|
|
97
141
|
// 정보 밀도 ↑ — 본문 14px 부터, 컨트롤 36px (대시보드/관리자 인상)
|
|
File without changes
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
--danger: #DC2626;
|
|
21
21
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
22
22
|
--danger-foreground: #FFFFFF;
|
|
23
|
+
--success: #16A34A;
|
|
24
|
+
--success-foreground: #FFFFFF;
|
|
25
|
+
--warning: #D97706;
|
|
26
|
+
--warning-foreground: #FFFFFF;
|
|
27
|
+
--info: #2563EB;
|
|
28
|
+
--info-foreground: #FFFFFF;
|
|
29
|
+
--sidebar-bg: #FAFAFA;
|
|
30
|
+
--sidebar-fg: #0A0A0A;
|
|
31
|
+
--sidebar-border: #E5E5E5;
|
|
32
|
+
--sidebar-accent: #F5F5F5;
|
|
33
|
+
--sidebar-accent-fg: #0A0A0A;
|
|
23
34
|
}
|
|
24
35
|
@media (prefers-color-scheme: dark) {
|
|
25
36
|
:root:not(.light):not(.dark) {
|
|
@@ -37,8 +48,19 @@
|
|
|
37
48
|
--primary-foreground: #171717;
|
|
38
49
|
--primary-hover: #E5E5E5;
|
|
39
50
|
--danger: #DC2626;
|
|
40
|
-
|
|
51
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
41
52
|
--danger-foreground: #FFFFFF;
|
|
53
|
+
--success: #22C55E;
|
|
54
|
+
--success-foreground: #052E16;
|
|
55
|
+
--warning: #F59E0B;
|
|
56
|
+
--warning-foreground: #451A03;
|
|
57
|
+
--info: #3B82F6;
|
|
58
|
+
--info-foreground: #172554;
|
|
59
|
+
--sidebar-bg: #171717;
|
|
60
|
+
--sidebar-fg: #FAFAFA;
|
|
61
|
+
--sidebar-border: #262626;
|
|
62
|
+
--sidebar-accent: #262626;
|
|
63
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
.dark {
|
|
@@ -58,6 +80,17 @@
|
|
|
58
80
|
--danger: #DC2626;
|
|
59
81
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
60
82
|
--danger-foreground: #FFFFFF;
|
|
83
|
+
--success: #22C55E;
|
|
84
|
+
--success-foreground: #052E16;
|
|
85
|
+
--warning: #F59E0B;
|
|
86
|
+
--warning-foreground: #451A03;
|
|
87
|
+
--info: #3B82F6;
|
|
88
|
+
--info-foreground: #172554;
|
|
89
|
+
--sidebar-bg: #171717;
|
|
90
|
+
--sidebar-fg: #FAFAFA;
|
|
91
|
+
--sidebar-border: #262626;
|
|
92
|
+
--sidebar-accent: #262626;
|
|
93
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
61
94
|
}
|
|
62
95
|
/* sh-ui:theme-colors-end */
|
|
63
96
|
|
|
@@ -112,8 +145,8 @@
|
|
|
112
145
|
/* sh-ui:theme-ease-end */
|
|
113
146
|
/* sh-ui:theme-control-start */
|
|
114
147
|
--control-sm: 2rem;
|
|
115
|
-
--control-md: 2.
|
|
116
|
-
--control-lg:
|
|
148
|
+
--control-md: 2.25rem;
|
|
149
|
+
--control-lg: 2.5rem;
|
|
117
150
|
/* sh-ui:theme-control-end */
|
|
118
151
|
/* sh-ui:theme-border-width-start */
|
|
119
152
|
--border-width: 1px;
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
--danger: #DC2626;
|
|
21
21
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
22
22
|
--danger-foreground: #FFFFFF;
|
|
23
|
+
--success: #16A34A;
|
|
24
|
+
--success-foreground: #FFFFFF;
|
|
25
|
+
--warning: #D97706;
|
|
26
|
+
--warning-foreground: #FFFFFF;
|
|
27
|
+
--info: #2563EB;
|
|
28
|
+
--info-foreground: #FFFFFF;
|
|
29
|
+
--sidebar-bg: #FAFAFA;
|
|
30
|
+
--sidebar-fg: #0A0A0A;
|
|
31
|
+
--sidebar-border: #E5E5E5;
|
|
32
|
+
--sidebar-accent: #F5F5F5;
|
|
33
|
+
--sidebar-accent-fg: #0A0A0A;
|
|
23
34
|
}
|
|
24
35
|
@media (prefers-color-scheme: dark) {
|
|
25
36
|
:root:not(.light):not(.dark) {
|
|
@@ -37,8 +48,19 @@
|
|
|
37
48
|
--primary-foreground: #171717;
|
|
38
49
|
--primary-hover: #E5E5E5;
|
|
39
50
|
--danger: #DC2626;
|
|
40
|
-
|
|
51
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
41
52
|
--danger-foreground: #FFFFFF;
|
|
53
|
+
--success: #22C55E;
|
|
54
|
+
--success-foreground: #052E16;
|
|
55
|
+
--warning: #F59E0B;
|
|
56
|
+
--warning-foreground: #451A03;
|
|
57
|
+
--info: #3B82F6;
|
|
58
|
+
--info-foreground: #172554;
|
|
59
|
+
--sidebar-bg: #171717;
|
|
60
|
+
--sidebar-fg: #FAFAFA;
|
|
61
|
+
--sidebar-border: #262626;
|
|
62
|
+
--sidebar-accent: #262626;
|
|
63
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
.dark {
|
|
@@ -58,6 +80,17 @@
|
|
|
58
80
|
--danger: #DC2626;
|
|
59
81
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
60
82
|
--danger-foreground: #FFFFFF;
|
|
83
|
+
--success: #22C55E;
|
|
84
|
+
--success-foreground: #052E16;
|
|
85
|
+
--warning: #F59E0B;
|
|
86
|
+
--warning-foreground: #451A03;
|
|
87
|
+
--info: #3B82F6;
|
|
88
|
+
--info-foreground: #172554;
|
|
89
|
+
--sidebar-bg: #171717;
|
|
90
|
+
--sidebar-fg: #FAFAFA;
|
|
91
|
+
--sidebar-border: #262626;
|
|
92
|
+
--sidebar-accent: #262626;
|
|
93
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
61
94
|
}
|
|
62
95
|
/* sh-ui:theme-colors-end */
|
|
63
96
|
|
|
@@ -112,8 +145,8 @@
|
|
|
112
145
|
/* sh-ui:theme-ease-end */
|
|
113
146
|
/* sh-ui:theme-control-start */
|
|
114
147
|
--control-sm: 2rem;
|
|
115
|
-
--control-md: 2.
|
|
116
|
-
--control-lg:
|
|
148
|
+
--control-md: 2.25rem;
|
|
149
|
+
--control-lg: 2.5rem;
|
|
117
150
|
/* sh-ui:theme-control-end */
|
|
118
151
|
/* sh-ui:theme-border-width-start */
|
|
119
152
|
--border-width: 1px;
|
|
@@ -112,8 +112,8 @@
|
|
|
112
112
|
/* sh-ui:theme-ease-end */
|
|
113
113
|
/* sh-ui:theme-control-start */
|
|
114
114
|
--control-sm: 2rem;
|
|
115
|
-
--control-md: 2.
|
|
116
|
-
--control-lg:
|
|
115
|
+
--control-md: 2.25rem;
|
|
116
|
+
--control-lg: 2.5rem;
|
|
117
117
|
/* sh-ui:theme-control-end */
|
|
118
118
|
/* sh-ui:theme-border-width-start */
|
|
119
119
|
--border-width: 1px;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
@import 'tailwindcss';
|
|
2
2
|
|
|
3
3
|
@source "../../../../../../apps/**/*.{ts,tsx}";
|
|
4
|
+
@source "../../../../ui-core/src/**/*.{ts,tsx}";
|
|
4
5
|
@source "../**/*.{ts,tsx}";
|
|
5
6
|
|
|
6
7
|
@import './tokens.css';
|
|
7
8
|
|
|
9
|
+
/* sh-ui:utility-imports-start — v0.81+ create_project 가 자동 설치하는 베이스 유틸 CSS.
|
|
10
|
+
* 이 블록 안의 줄들은 add_component 가 멱등하게 관리. 직접 편집은 가능하지만 sentinel 은 유지. */
|
|
11
|
+
@import '@workspace/ui-core/styles/base.css';
|
|
12
|
+
@import '@workspace/ui-core/styles/focus-ring.css';
|
|
13
|
+
@import '@workspace/ui-core/styles/animations.css';
|
|
14
|
+
@import '@workspace/ui-core/styles/breakpoints.css';
|
|
15
|
+
@import '@workspace/ui-core/styles/z-index.css';
|
|
16
|
+
/* sh-ui:utility-imports-end */
|
|
17
|
+
|
|
8
18
|
@custom-variant dark (&:is(.dark *));
|
|
9
19
|
|
|
10
20
|
/* Tailwind v4 — 토큰 CSS 변수를 utility 클래스로 노출.
|
|
@@ -20,6 +20,17 @@
|
|
|
20
20
|
--danger: #DC2626;
|
|
21
21
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
22
22
|
--danger-foreground: #FFFFFF;
|
|
23
|
+
--success: #16A34A;
|
|
24
|
+
--success-foreground: #FFFFFF;
|
|
25
|
+
--warning: #D97706;
|
|
26
|
+
--warning-foreground: #FFFFFF;
|
|
27
|
+
--info: #2563EB;
|
|
28
|
+
--info-foreground: #FFFFFF;
|
|
29
|
+
--sidebar-bg: #FAFAFA;
|
|
30
|
+
--sidebar-fg: #0A0A0A;
|
|
31
|
+
--sidebar-border: #E5E5E5;
|
|
32
|
+
--sidebar-accent: #F5F5F5;
|
|
33
|
+
--sidebar-accent-fg: #0A0A0A;
|
|
23
34
|
}
|
|
24
35
|
@media (prefers-color-scheme: dark) {
|
|
25
36
|
:root:not(.light):not(.dark) {
|
|
@@ -37,8 +48,19 @@
|
|
|
37
48
|
--primary-foreground: #171717;
|
|
38
49
|
--primary-hover: #E5E5E5;
|
|
39
50
|
--danger: #DC2626;
|
|
40
|
-
|
|
51
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
41
52
|
--danger-foreground: #FFFFFF;
|
|
53
|
+
--success: #22C55E;
|
|
54
|
+
--success-foreground: #052E16;
|
|
55
|
+
--warning: #F59E0B;
|
|
56
|
+
--warning-foreground: #451A03;
|
|
57
|
+
--info: #3B82F6;
|
|
58
|
+
--info-foreground: #172554;
|
|
59
|
+
--sidebar-bg: #171717;
|
|
60
|
+
--sidebar-fg: #FAFAFA;
|
|
61
|
+
--sidebar-border: #262626;
|
|
62
|
+
--sidebar-accent: #262626;
|
|
63
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
.dark {
|
|
@@ -58,6 +80,17 @@
|
|
|
58
80
|
--danger: #DC2626;
|
|
59
81
|
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
60
82
|
--danger-foreground: #FFFFFF;
|
|
83
|
+
--success: #22C55E;
|
|
84
|
+
--success-foreground: #052E16;
|
|
85
|
+
--warning: #F59E0B;
|
|
86
|
+
--warning-foreground: #451A03;
|
|
87
|
+
--info: #3B82F6;
|
|
88
|
+
--info-foreground: #172554;
|
|
89
|
+
--sidebar-bg: #171717;
|
|
90
|
+
--sidebar-fg: #FAFAFA;
|
|
91
|
+
--sidebar-border: #262626;
|
|
92
|
+
--sidebar-accent: #262626;
|
|
93
|
+
--sidebar-accent-fg: #FAFAFA;
|
|
61
94
|
}
|
|
62
95
|
/* sh-ui:theme-colors-end */
|
|
63
96
|
|
|
@@ -112,8 +145,8 @@
|
|
|
112
145
|
/* sh-ui:theme-ease-end */
|
|
113
146
|
/* sh-ui:theme-control-start */
|
|
114
147
|
--control-sm: 2rem;
|
|
115
|
-
--control-md: 2.
|
|
116
|
-
--control-lg:
|
|
148
|
+
--control-md: 2.25rem;
|
|
149
|
+
--control-lg: 2.5rem;
|
|
117
150
|
/* sh-ui:theme-control-end */
|
|
118
151
|
/* sh-ui:theme-border-width-start */
|
|
119
152
|
--border-width: 1px;
|