sh-ui-cli 0.85.1 → 0.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/changelog/versions.json +24 -0
- package/package.json +1 -1
- package/src/constants.js +1 -1
- package/src/create/architectures/archSchema.js +1 -1
- package/src/create/architectures/flat.js +1 -1
- package/src/create/architectures/fsd.js +1 -1
- package/src/create/describeTemplate.js +21 -2
- package/src/create/generator.js +86 -3
- package/src/create/templateManifest.js +49 -0
- package/src/mcp.mjs +4 -4
- package/templates/nextjs-standalone/package.json +1 -0
- package/templates/vite-standalone/CLAUDE.md +7 -0
- package/templates/vite-standalone/README.md +23 -0
- package/templates/vite-standalone/_arch/flat/sh-ui.config.json +22 -0
- package/templates/vite-standalone/_arch/flat/src/App.tsx +13 -0
- package/templates/vite-standalone/_arch/flat/src/components/layouts/RootLayout.tsx +5 -0
- package/templates/vite-standalone/_arch/flat/src/components/providers/GlobalProvider/index.tsx +13 -0
- package/templates/vite-standalone/_arch/flat/src/components/providers/index.tsx +1 -0
- package/templates/vite-standalone/_arch/flat/src/components/providers/theme/ThemeProvider.tsx +59 -0
- package/templates/vite-standalone/_arch/flat/src/lib/api/queryClient.ts +12 -0
- package/templates/vite-standalone/_arch/flat/src/lib/hooks/useTheme.ts +8 -0
- package/templates/vite-standalone/_arch/flat/src/lib/styles/globals.css +35 -0
- package/templates/vite-standalone/_arch/flat/src/lib/styles/tokens.css +203 -0
- package/templates/vite-standalone/_arch/flat/src/lib/utils/utils.ts +6 -0
- package/templates/vite-standalone/_arch/flat/src/main.tsx +10 -0
- package/templates/vite-standalone/_arch/flat/tsconfig.app.json +23 -0
- package/templates/vite-standalone/_arch/fsd/sh-ui.config.json +22 -0
- package/templates/vite-standalone/_arch/fsd/src/App.tsx +13 -0
- package/templates/vite-standalone/_arch/fsd/src/app/layouts/RootLayout.tsx +5 -0
- package/templates/vite-standalone/_arch/fsd/src/app/providers/GlobalProvider/index.tsx +13 -0
- package/templates/vite-standalone/_arch/fsd/src/app/providers/theme/ThemeProvider.tsx +59 -0
- package/templates/vite-standalone/_arch/fsd/src/main.tsx +10 -0
- package/templates/vite-standalone/_arch/fsd/src/shared/api/queryClient.ts +12 -0
- package/templates/vite-standalone/_arch/fsd/src/shared/hooks/useTheme.ts +8 -0
- package/templates/vite-standalone/_arch/fsd/src/shared/lib/utils.ts +6 -0
- package/templates/vite-standalone/_arch/fsd/src/shared/styles/globals.css +35 -0
- package/templates/vite-standalone/_arch/fsd/src/shared/styles/tokens.css +203 -0
- package/templates/vite-standalone/_arch/fsd/tsconfig.app.json +22 -0
- package/templates/vite-standalone/eslint.config.js +34 -0
- package/templates/vite-standalone/gitignore +8 -0
- package/templates/vite-standalone/index.html +22 -0
- package/templates/vite-standalone/package.json +64 -0
- package/templates/vite-standalone/src/App.tsx +5 -0
- package/templates/vite-standalone/src/Home.tsx +7 -0
- package/templates/vite-standalone/src/main.tsx +9 -0
- package/templates/vite-standalone/tsconfig.json +7 -0
- package/templates/vite-standalone/tsconfig.node.json +12 -0
- package/templates/vite-standalone/vite.config.ts +11 -0
- package/templates/vite-standalone/vitest.config.ts +13 -0
- package/templates/vite-standalone/vitest.setup.ts +1 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/* Generated by @sh-ui/tokens — do not edit directly */
|
|
2
|
+
/* base=neutral radius=md mode=light-dark */
|
|
3
|
+
|
|
4
|
+
/* sh-ui:theme-colors-start */
|
|
5
|
+
:root {
|
|
6
|
+
--background: #FFFFFF;
|
|
7
|
+
--background-subtle: #FAFAFA;
|
|
8
|
+
--background-muted: #F5F5F5;
|
|
9
|
+
--background-inverse: #0A0A0A;
|
|
10
|
+
--foreground: #0A0A0A;
|
|
11
|
+
--foreground-muted: #525252;
|
|
12
|
+
--foreground-subtle: #A3A3A3;
|
|
13
|
+
--foreground-inverse: #FFFFFF;
|
|
14
|
+
--border: #E5E5E5;
|
|
15
|
+
--border-strong: #D4D4D4;
|
|
16
|
+
--primary: #171717;
|
|
17
|
+
--primary-foreground: #FAFAFA;
|
|
18
|
+
--primary-hover: #262626;
|
|
19
|
+
--ring: color-mix(in srgb, var(--primary) 50%, transparent);
|
|
20
|
+
--danger: #DC2626;
|
|
21
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
34
|
+
}
|
|
35
|
+
@media (prefers-color-scheme: dark) {
|
|
36
|
+
:root:not(.light):not(.dark) {
|
|
37
|
+
--background: #0A0A0A;
|
|
38
|
+
--background-subtle: #171717;
|
|
39
|
+
--background-muted: #262626;
|
|
40
|
+
--background-inverse: #FFFFFF;
|
|
41
|
+
--foreground: #FAFAFA;
|
|
42
|
+
--foreground-muted: #A3A3A3;
|
|
43
|
+
--foreground-subtle: #737373;
|
|
44
|
+
--foreground-inverse: #0A0A0A;
|
|
45
|
+
--border: #262626;
|
|
46
|
+
--border-strong: #404040;
|
|
47
|
+
--primary: #FAFAFA;
|
|
48
|
+
--primary-foreground: #171717;
|
|
49
|
+
--primary-hover: #E5E5E5;
|
|
50
|
+
--danger: #DC2626;
|
|
51
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
.dark {
|
|
67
|
+
--background: #0A0A0A;
|
|
68
|
+
--background-subtle: #171717;
|
|
69
|
+
--background-muted: #262626;
|
|
70
|
+
--background-inverse: #FFFFFF;
|
|
71
|
+
--foreground: #FAFAFA;
|
|
72
|
+
--foreground-muted: #A3A3A3;
|
|
73
|
+
--foreground-subtle: #737373;
|
|
74
|
+
--foreground-inverse: #0A0A0A;
|
|
75
|
+
--border: #262626;
|
|
76
|
+
--border-strong: #404040;
|
|
77
|
+
--primary: #FAFAFA;
|
|
78
|
+
--primary-foreground: #171717;
|
|
79
|
+
--primary-hover: #E5E5E5;
|
|
80
|
+
--danger: #DC2626;
|
|
81
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
94
|
+
}
|
|
95
|
+
/* sh-ui:theme-colors-end */
|
|
96
|
+
|
|
97
|
+
:root {
|
|
98
|
+
/* sh-ui:theme-radius-start */
|
|
99
|
+
--radius: 0.5rem;
|
|
100
|
+
/* sh-ui:theme-radius-end */
|
|
101
|
+
/* sh-ui:theme-space-start */
|
|
102
|
+
--space-0: 0;
|
|
103
|
+
--space-1: 0.25rem;
|
|
104
|
+
--space-2: 0.5rem;
|
|
105
|
+
--space-3: 0.75rem;
|
|
106
|
+
--space-4: 1rem;
|
|
107
|
+
--space-5: 1.25rem;
|
|
108
|
+
--space-6: 1.5rem;
|
|
109
|
+
--space-8: 2rem;
|
|
110
|
+
--space-10: 2.5rem;
|
|
111
|
+
--space-12: 3rem;
|
|
112
|
+
--space-16: 4rem;
|
|
113
|
+
/* sh-ui:theme-space-end */
|
|
114
|
+
/* sh-ui:theme-text-start */
|
|
115
|
+
--text-xs: 0.75rem;
|
|
116
|
+
--text-sm: 0.875rem;
|
|
117
|
+
--text-base: 1rem;
|
|
118
|
+
--text-lg: 1.125rem;
|
|
119
|
+
--text-xl: 1.25rem;
|
|
120
|
+
--text-2xl: 1.5rem;
|
|
121
|
+
--text-3xl: 1.875rem;
|
|
122
|
+
--text-4xl: 2.25rem;
|
|
123
|
+
/* sh-ui:theme-text-end */
|
|
124
|
+
/* sh-ui:theme-weight-start */
|
|
125
|
+
--weight-regular: 400;
|
|
126
|
+
--weight-medium: 500;
|
|
127
|
+
--weight-semibold: 600;
|
|
128
|
+
--weight-bold: 700;
|
|
129
|
+
/* sh-ui:theme-weight-end */
|
|
130
|
+
/* sh-ui:theme-shadow-start */
|
|
131
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
132
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
133
|
+
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
134
|
+
--shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.18);
|
|
135
|
+
--shadow-menu: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
|
|
136
|
+
/* sh-ui:theme-shadow-end */
|
|
137
|
+
/* sh-ui:theme-duration-start */
|
|
138
|
+
--duration-fast: 120ms;
|
|
139
|
+
--duration-base: 160ms;
|
|
140
|
+
--duration-slow: 200ms;
|
|
141
|
+
/* sh-ui:theme-duration-end */
|
|
142
|
+
/* sh-ui:theme-ease-start */
|
|
143
|
+
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
|
144
|
+
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
|
|
145
|
+
/* sh-ui:theme-ease-end */
|
|
146
|
+
/* sh-ui:theme-control-start */
|
|
147
|
+
--control-sm: 2rem;
|
|
148
|
+
--control-md: 2.25rem;
|
|
149
|
+
--control-lg: 2.5rem;
|
|
150
|
+
/* sh-ui:theme-control-end */
|
|
151
|
+
/* sh-ui:theme-border-width-start */
|
|
152
|
+
--border-width: 1px;
|
|
153
|
+
--border-width-strong: 2px;
|
|
154
|
+
/* sh-ui:theme-border-width-end */
|
|
155
|
+
/* sh-ui:theme-gradient-start */
|
|
156
|
+
--gradient-primary: linear-gradient(135deg, #171717 0%, #525252 100%);
|
|
157
|
+
--gradient-surface: linear-gradient(180deg, #FFFFFF 0%, #F5F5F5 100%);
|
|
158
|
+
--gradient-overlay: linear-gradient(180deg, #000000 0%, #1F1F1F 100%);
|
|
159
|
+
/* sh-ui:theme-gradient-end */
|
|
160
|
+
--opacity-disabled: 0.5;
|
|
161
|
+
--z-base: 0;
|
|
162
|
+
--z-sticky: 100;
|
|
163
|
+
--z-dropdown: 200;
|
|
164
|
+
--z-overlay: 300;
|
|
165
|
+
--z-modal: 400;
|
|
166
|
+
--z-popover: 500;
|
|
167
|
+
--z-toast: 600;
|
|
168
|
+
--z-tooltip: 700;
|
|
169
|
+
--bp-sm: 640px;
|
|
170
|
+
--bp-md: 768px;
|
|
171
|
+
--bp-lg: 1024px;
|
|
172
|
+
--bp-xl: 1280px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* WCAG 2.5.5 — 터치 타겟 최소 44×44px. 마우스/스타일러스 대신 손가락 입력 시 control 높이를 보정. */
|
|
176
|
+
@media (hover: none) and (pointer: coarse) {
|
|
177
|
+
:root {
|
|
178
|
+
--control-sm: 2.75rem;
|
|
179
|
+
--control-md: 2.75rem;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* WCAG AA 보장 — 사용자가 OS/브라우저에서 "고대비" 접근성 옵션 켰을 때.
|
|
184
|
+
* foreground-subtle, border, border-strong 만 강화 (다른 토큰은 이미 AA 통과). */
|
|
185
|
+
@media (prefers-contrast: high) {
|
|
186
|
+
:root {
|
|
187
|
+
--foreground-subtle: #767676;
|
|
188
|
+
--border: #767676;
|
|
189
|
+
--border-strong: #595959;
|
|
190
|
+
}
|
|
191
|
+
.dark {
|
|
192
|
+
--foreground-subtle: #B3B3B3;
|
|
193
|
+
--border: #757575;
|
|
194
|
+
--border-strong: #999999;
|
|
195
|
+
}
|
|
196
|
+
@media (prefers-color-scheme: dark) {
|
|
197
|
+
:root:not(.light):not(.dark) {
|
|
198
|
+
--foreground-subtle: #B3B3B3;
|
|
199
|
+
--border: #757575;
|
|
200
|
+
--border-strong: #999999;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Bundler",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/lib/*": ["./src/lib/*"],
|
|
19
|
+
"@/components/*": ["./src/components/*"]
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/sanghyeonKim0201/sh-ui/live/packages/cli/sh-ui.schema.json",
|
|
3
|
+
"platform": "react",
|
|
4
|
+
"cssFramework": "tailwind",
|
|
5
|
+
"theme": {
|
|
6
|
+
"base": "neutral",
|
|
7
|
+
"radius": "md",
|
|
8
|
+
"mode": "light-dark"
|
|
9
|
+
},
|
|
10
|
+
"paths": {
|
|
11
|
+
"tokens": "src/shared/styles/tokens.css",
|
|
12
|
+
"cssEntry": "src/shared/styles/globals.css",
|
|
13
|
+
"styles": "src/shared/styles",
|
|
14
|
+
"components": "src/shared/ui",
|
|
15
|
+
"utils": "src/shared/lib/utils.ts"
|
|
16
|
+
},
|
|
17
|
+
"aliases": {
|
|
18
|
+
"components": "@/shared/ui",
|
|
19
|
+
"utils": "@/shared/lib/utils",
|
|
20
|
+
"ui": "@/shared/ui"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GlobalProvider } from '@/app/providers/GlobalProvider';
|
|
2
|
+
import { RootLayout } from '@/app/layouts/RootLayout';
|
|
3
|
+
import Home from './Home';
|
|
4
|
+
|
|
5
|
+
export default function App() {
|
|
6
|
+
return (
|
|
7
|
+
<GlobalProvider>
|
|
8
|
+
<RootLayout>
|
|
9
|
+
<Home />
|
|
10
|
+
</RootLayout>
|
|
11
|
+
</GlobalProvider>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
+
import { type ReactNode, useState } from 'react';
|
|
3
|
+
import { createQueryClient } from '@/shared/api/queryClient';
|
|
4
|
+
import { ThemeProvider } from '../theme/ThemeProvider';
|
|
5
|
+
|
|
6
|
+
export function GlobalProvider({ children }: { children: ReactNode }) {
|
|
7
|
+
const [queryClient] = useState(() => createQueryClient());
|
|
8
|
+
return (
|
|
9
|
+
<ThemeProvider>
|
|
10
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
11
|
+
</ThemeProvider>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createContext, useCallback, useEffect, useMemo, useState, type ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export type Theme = 'light' | 'dark' | 'system';
|
|
4
|
+
|
|
5
|
+
type ThemeContextValue = {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
resolvedTheme: 'light' | 'dark';
|
|
8
|
+
setTheme: (theme: Theme) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const ThemeContext = createContext<ThemeContextValue | null>(null);
|
|
12
|
+
|
|
13
|
+
const STORAGE_KEY = 'theme';
|
|
14
|
+
|
|
15
|
+
function getSystemTheme(): 'light' | 'dark' {
|
|
16
|
+
if (typeof window === 'undefined') return 'light';
|
|
17
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveTheme(theme: Theme): 'light' | 'dark' {
|
|
21
|
+
return theme === 'system' ? getSystemTheme() : theme;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
25
|
+
const [theme, setThemeState] = useState<Theme>(() => {
|
|
26
|
+
if (typeof window === 'undefined') return 'system';
|
|
27
|
+
return (localStorage.getItem(STORAGE_KEY) as Theme) ?? 'system';
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>(() => resolveTheme(theme));
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const resolved = resolveTheme(theme);
|
|
34
|
+
setResolvedTheme(resolved);
|
|
35
|
+
document.documentElement.classList.toggle('dark', resolved === 'dark');
|
|
36
|
+
}, [theme]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (theme !== 'system') return;
|
|
40
|
+
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
41
|
+
const handler = () => {
|
|
42
|
+
const resolved = getSystemTheme();
|
|
43
|
+
setResolvedTheme(resolved);
|
|
44
|
+
document.documentElement.classList.toggle('dark', resolved === 'dark');
|
|
45
|
+
};
|
|
46
|
+
mq.addEventListener('change', handler);
|
|
47
|
+
return () => mq.removeEventListener('change', handler);
|
|
48
|
+
}, [theme]);
|
|
49
|
+
|
|
50
|
+
const setTheme = useCallback((next: Theme) => {
|
|
51
|
+
setThemeState(next);
|
|
52
|
+
if (next === 'system') localStorage.removeItem(STORAGE_KEY);
|
|
53
|
+
else localStorage.setItem(STORAGE_KEY, next);
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const value = useMemo(() => ({ theme, resolvedTheme, setTheme }), [theme, resolvedTheme, setTheme]);
|
|
57
|
+
|
|
58
|
+
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { ThemeContext } from '@/app/providers/theme/ThemeProvider';
|
|
3
|
+
|
|
4
|
+
export function useTheme() {
|
|
5
|
+
const ctx = useContext(ThemeContext);
|
|
6
|
+
if (!ctx) throw new Error('useTheme must be used within a ThemeProvider');
|
|
7
|
+
return ctx;
|
|
8
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* sh-ui:external-imports-start
|
|
2
|
+
* 외부 폰트 / 아이콘셋 / 디자인시스템 URL @import 는 반드시 이 블록 안에 둘 것. */
|
|
3
|
+
/* sh-ui:external-imports-end */
|
|
4
|
+
|
|
5
|
+
@import 'tailwindcss';
|
|
6
|
+
@import './tokens.css';
|
|
7
|
+
|
|
8
|
+
@custom-variant dark (&:is(.dark *));
|
|
9
|
+
|
|
10
|
+
@theme inline {
|
|
11
|
+
--color-background: var(--background);
|
|
12
|
+
--color-background-subtle: var(--background-subtle);
|
|
13
|
+
--color-background-muted: var(--background-muted);
|
|
14
|
+
--color-background-inverse: var(--background-inverse);
|
|
15
|
+
--color-foreground: var(--foreground);
|
|
16
|
+
--color-foreground-muted: var(--foreground-muted);
|
|
17
|
+
--color-foreground-subtle: var(--foreground-subtle);
|
|
18
|
+
--color-foreground-inverse: var(--foreground-inverse);
|
|
19
|
+
--color-border: var(--border);
|
|
20
|
+
--color-border-strong: var(--border-strong);
|
|
21
|
+
--color-primary: var(--primary);
|
|
22
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
23
|
+
--color-primary-hover: var(--primary-hover);
|
|
24
|
+
--color-ring: var(--ring);
|
|
25
|
+
--color-danger: var(--danger);
|
|
26
|
+
--color-danger-hover: var(--danger-hover);
|
|
27
|
+
--color-danger-foreground: var(--danger-foreground);
|
|
28
|
+
--color-success: var(--success);
|
|
29
|
+
--color-success-foreground: var(--success-foreground);
|
|
30
|
+
--color-warning: var(--warning);
|
|
31
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
32
|
+
--color-info: var(--info);
|
|
33
|
+
--color-info-foreground: var(--info-foreground);
|
|
34
|
+
--color-sidebar-bg: var(--sidebar-bg);
|
|
35
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/* Generated by @sh-ui/tokens — do not edit directly */
|
|
2
|
+
/* base=neutral radius=md mode=light-dark */
|
|
3
|
+
|
|
4
|
+
/* sh-ui:theme-colors-start */
|
|
5
|
+
:root {
|
|
6
|
+
--background: #FFFFFF;
|
|
7
|
+
--background-subtle: #FAFAFA;
|
|
8
|
+
--background-muted: #F5F5F5;
|
|
9
|
+
--background-inverse: #0A0A0A;
|
|
10
|
+
--foreground: #0A0A0A;
|
|
11
|
+
--foreground-muted: #525252;
|
|
12
|
+
--foreground-subtle: #A3A3A3;
|
|
13
|
+
--foreground-inverse: #FFFFFF;
|
|
14
|
+
--border: #E5E5E5;
|
|
15
|
+
--border-strong: #D4D4D4;
|
|
16
|
+
--primary: #171717;
|
|
17
|
+
--primary-foreground: #FAFAFA;
|
|
18
|
+
--primary-hover: #262626;
|
|
19
|
+
--ring: color-mix(in srgb, var(--primary) 50%, transparent);
|
|
20
|
+
--danger: #DC2626;
|
|
21
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
34
|
+
}
|
|
35
|
+
@media (prefers-color-scheme: dark) {
|
|
36
|
+
:root:not(.light):not(.dark) {
|
|
37
|
+
--background: #0A0A0A;
|
|
38
|
+
--background-subtle: #171717;
|
|
39
|
+
--background-muted: #262626;
|
|
40
|
+
--background-inverse: #FFFFFF;
|
|
41
|
+
--foreground: #FAFAFA;
|
|
42
|
+
--foreground-muted: #A3A3A3;
|
|
43
|
+
--foreground-subtle: #737373;
|
|
44
|
+
--foreground-inverse: #0A0A0A;
|
|
45
|
+
--border: #262626;
|
|
46
|
+
--border-strong: #404040;
|
|
47
|
+
--primary: #FAFAFA;
|
|
48
|
+
--primary-foreground: #171717;
|
|
49
|
+
--primary-hover: #E5E5E5;
|
|
50
|
+
--danger: #DC2626;
|
|
51
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
.dark {
|
|
67
|
+
--background: #0A0A0A;
|
|
68
|
+
--background-subtle: #171717;
|
|
69
|
+
--background-muted: #262626;
|
|
70
|
+
--background-inverse: #FFFFFF;
|
|
71
|
+
--foreground: #FAFAFA;
|
|
72
|
+
--foreground-muted: #A3A3A3;
|
|
73
|
+
--foreground-subtle: #737373;
|
|
74
|
+
--foreground-inverse: #0A0A0A;
|
|
75
|
+
--border: #262626;
|
|
76
|
+
--border-strong: #404040;
|
|
77
|
+
--primary: #FAFAFA;
|
|
78
|
+
--primary-foreground: #171717;
|
|
79
|
+
--primary-hover: #E5E5E5;
|
|
80
|
+
--danger: #DC2626;
|
|
81
|
+
--danger-hover: color-mix(in srgb, var(--danger) 90%, black);
|
|
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;
|
|
94
|
+
}
|
|
95
|
+
/* sh-ui:theme-colors-end */
|
|
96
|
+
|
|
97
|
+
:root {
|
|
98
|
+
/* sh-ui:theme-radius-start */
|
|
99
|
+
--radius: 0.5rem;
|
|
100
|
+
/* sh-ui:theme-radius-end */
|
|
101
|
+
/* sh-ui:theme-space-start */
|
|
102
|
+
--space-0: 0;
|
|
103
|
+
--space-1: 0.25rem;
|
|
104
|
+
--space-2: 0.5rem;
|
|
105
|
+
--space-3: 0.75rem;
|
|
106
|
+
--space-4: 1rem;
|
|
107
|
+
--space-5: 1.25rem;
|
|
108
|
+
--space-6: 1.5rem;
|
|
109
|
+
--space-8: 2rem;
|
|
110
|
+
--space-10: 2.5rem;
|
|
111
|
+
--space-12: 3rem;
|
|
112
|
+
--space-16: 4rem;
|
|
113
|
+
/* sh-ui:theme-space-end */
|
|
114
|
+
/* sh-ui:theme-text-start */
|
|
115
|
+
--text-xs: 0.75rem;
|
|
116
|
+
--text-sm: 0.875rem;
|
|
117
|
+
--text-base: 1rem;
|
|
118
|
+
--text-lg: 1.125rem;
|
|
119
|
+
--text-xl: 1.25rem;
|
|
120
|
+
--text-2xl: 1.5rem;
|
|
121
|
+
--text-3xl: 1.875rem;
|
|
122
|
+
--text-4xl: 2.25rem;
|
|
123
|
+
/* sh-ui:theme-text-end */
|
|
124
|
+
/* sh-ui:theme-weight-start */
|
|
125
|
+
--weight-regular: 400;
|
|
126
|
+
--weight-medium: 500;
|
|
127
|
+
--weight-semibold: 600;
|
|
128
|
+
--weight-bold: 700;
|
|
129
|
+
/* sh-ui:theme-weight-end */
|
|
130
|
+
/* sh-ui:theme-shadow-start */
|
|
131
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
132
|
+
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
133
|
+
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
134
|
+
--shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.18);
|
|
135
|
+
--shadow-menu: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
|
|
136
|
+
/* sh-ui:theme-shadow-end */
|
|
137
|
+
/* sh-ui:theme-duration-start */
|
|
138
|
+
--duration-fast: 120ms;
|
|
139
|
+
--duration-base: 160ms;
|
|
140
|
+
--duration-slow: 200ms;
|
|
141
|
+
/* sh-ui:theme-duration-end */
|
|
142
|
+
/* sh-ui:theme-ease-start */
|
|
143
|
+
--ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
|
144
|
+
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
|
|
145
|
+
/* sh-ui:theme-ease-end */
|
|
146
|
+
/* sh-ui:theme-control-start */
|
|
147
|
+
--control-sm: 2rem;
|
|
148
|
+
--control-md: 2.25rem;
|
|
149
|
+
--control-lg: 2.5rem;
|
|
150
|
+
/* sh-ui:theme-control-end */
|
|
151
|
+
/* sh-ui:theme-border-width-start */
|
|
152
|
+
--border-width: 1px;
|
|
153
|
+
--border-width-strong: 2px;
|
|
154
|
+
/* sh-ui:theme-border-width-end */
|
|
155
|
+
/* sh-ui:theme-gradient-start */
|
|
156
|
+
--gradient-primary: linear-gradient(135deg, #171717 0%, #525252 100%);
|
|
157
|
+
--gradient-surface: linear-gradient(180deg, #FFFFFF 0%, #F5F5F5 100%);
|
|
158
|
+
--gradient-overlay: linear-gradient(180deg, #000000 0%, #1F1F1F 100%);
|
|
159
|
+
/* sh-ui:theme-gradient-end */
|
|
160
|
+
--opacity-disabled: 0.5;
|
|
161
|
+
--z-base: 0;
|
|
162
|
+
--z-sticky: 100;
|
|
163
|
+
--z-dropdown: 200;
|
|
164
|
+
--z-overlay: 300;
|
|
165
|
+
--z-modal: 400;
|
|
166
|
+
--z-popover: 500;
|
|
167
|
+
--z-toast: 600;
|
|
168
|
+
--z-tooltip: 700;
|
|
169
|
+
--bp-sm: 640px;
|
|
170
|
+
--bp-md: 768px;
|
|
171
|
+
--bp-lg: 1024px;
|
|
172
|
+
--bp-xl: 1280px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* WCAG 2.5.5 — 터치 타겟 최소 44×44px. 마우스/스타일러스 대신 손가락 입력 시 control 높이를 보정. */
|
|
176
|
+
@media (hover: none) and (pointer: coarse) {
|
|
177
|
+
:root {
|
|
178
|
+
--control-sm: 2.75rem;
|
|
179
|
+
--control-md: 2.75rem;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* WCAG AA 보장 — 사용자가 OS/브라우저에서 "고대비" 접근성 옵션 켰을 때.
|
|
184
|
+
* foreground-subtle, border, border-strong 만 강화 (다른 토큰은 이미 AA 통과). */
|
|
185
|
+
@media (prefers-contrast: high) {
|
|
186
|
+
:root {
|
|
187
|
+
--foreground-subtle: #767676;
|
|
188
|
+
--border: #767676;
|
|
189
|
+
--border-strong: #595959;
|
|
190
|
+
}
|
|
191
|
+
.dark {
|
|
192
|
+
--foreground-subtle: #B3B3B3;
|
|
193
|
+
--border: #757575;
|
|
194
|
+
--border-strong: #999999;
|
|
195
|
+
}
|
|
196
|
+
@media (prefers-color-scheme: dark) {
|
|
197
|
+
:root:not(.light):not(.dark) {
|
|
198
|
+
--foreground-subtle: #B3B3B3;
|
|
199
|
+
--border: #757575;
|
|
200
|
+
--border-strong: #999999;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"moduleResolution": "Bundler",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./src/*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["src"]
|
|
22
|
+
}
|