@mandujs/mcp 0.13.0 → 0.17.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/README.md +102 -7
- package/package.json +3 -2
- package/src/adapters/index.ts +20 -20
- package/src/adapters/monitor-adapter.ts +100 -100
- package/src/adapters/tool-adapter.ts +88 -88
- package/src/executor/error-handler.ts +250 -250
- package/src/executor/index.ts +22 -22
- package/src/executor/tool-executor.ts +148 -148
- package/src/hooks/config-watcher.ts +174 -174
- package/src/hooks/index.ts +23 -23
- package/src/hooks/mcp-hooks.ts +227 -227
- package/src/logging/index.ts +15 -15
- package/src/logging/mcp-transport.ts +134 -134
- package/src/registry/index.ts +13 -13
- package/src/registry/mcp-tool-registry.ts +298 -298
- package/src/resources/skills/guides.ts +1136 -1136
- package/src/resources/skills/index.ts +12 -12
- package/src/resources/skills/loader.ts +218 -218
- package/src/resources/skills/mandu-composition/SKILL.md +91 -91
- package/src/resources/skills/mandu-composition/metadata.json +13 -13
- package/src/resources/skills/mandu-composition/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-composition/rules/_template.md +77 -77
- package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +146 -146
- package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +164 -164
- package/src/resources/skills/mandu-composition/rules/comp-island-event.md +161 -161
- package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +167 -167
- package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +149 -149
- package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +148 -148
- package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +150 -150
- package/src/resources/skills/mandu-deployment/SKILL.md +92 -92
- package/src/resources/skills/mandu-deployment/_sections.md +41 -41
- package/src/resources/skills/mandu-deployment/_template.md +38 -38
- package/src/resources/skills/mandu-deployment/metadata.json +13 -13
- package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +109 -109
- package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +115 -115
- package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +219 -219
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +150 -150
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +223 -223
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +152 -152
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +179 -179
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-supabase.md +323 -323
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +140 -140
- package/src/resources/skills/mandu-fs-routes/SKILL.md +82 -82
- package/src/resources/skills/mandu-fs-routes/metadata.json +12 -12
- package/src/resources/skills/mandu-fs-routes/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-fs-routes/rules/_template.md +69 -69
- package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +65 -65
- package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +93 -93
- package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +55 -55
- package/src/resources/skills/mandu-guard/SKILL.md +129 -129
- package/src/resources/skills/mandu-guard/metadata.json +12 -12
- package/src/resources/skills/mandu-guard/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-guard/rules/_template.md +82 -82
- package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +100 -100
- package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +76 -76
- package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +81 -81
- package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +80 -80
- package/src/resources/skills/mandu-hydration/SKILL.md +91 -91
- package/src/resources/skills/mandu-hydration/metadata.json +12 -12
- package/src/resources/skills/mandu-hydration/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-hydration/rules/_template.md +72 -72
- package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +109 -109
- package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +55 -55
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +113 -113
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +68 -68
- package/src/resources/skills/mandu-performance/SKILL.md +85 -85
- package/src/resources/skills/mandu-performance/metadata.json +14 -14
- package/src/resources/skills/mandu-performance/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-performance/rules/_template.md +64 -64
- package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +103 -103
- package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +95 -95
- package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +124 -124
- package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +125 -125
- package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +80 -80
- package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +145 -145
- package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +98 -98
- package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +154 -154
- package/src/resources/skills/mandu-security/SKILL.md +87 -87
- package/src/resources/skills/mandu-security/metadata.json +13 -13
- package/src/resources/skills/mandu-security/rules/_sections.md +31 -31
- package/src/resources/skills/mandu-security/rules/_template.md +74 -74
- package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +127 -127
- package/src/resources/skills/mandu-security/rules/sec-env-management.md +133 -133
- package/src/resources/skills/mandu-security/rules/sec-input-validate.md +148 -148
- package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +146 -146
- package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +138 -138
- package/src/resources/skills/mandu-slot/SKILL.md +85 -85
- package/src/resources/skills/mandu-slot/metadata.json +12 -12
- package/src/resources/skills/mandu-slot/rules/_sections.md +36 -36
- package/src/resources/skills/mandu-slot/rules/_template.md +63 -63
- package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +38 -38
- package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +56 -56
- package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +59 -59
- package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +64 -64
- package/src/resources/skills/mandu-styling/SKILL.md +154 -154
- package/src/resources/skills/mandu-styling/_sections.md +43 -43
- package/src/resources/skills/mandu-styling/_template.md +32 -32
- package/src/resources/skills/mandu-styling/metadata.json +15 -15
- package/src/resources/skills/mandu-styling/rules/style-component-compound.md +235 -235
- package/src/resources/skills/mandu-styling/rules/style-component-slots.md +255 -255
- package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +205 -205
- package/src/resources/skills/mandu-styling/rules/style-island-animations.md +272 -272
- package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +167 -167
- package/src/resources/skills/mandu-styling/rules/style-island-variants.md +221 -221
- package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +209 -209
- package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +192 -192
- package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +162 -162
- package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +164 -164
- package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +170 -170
- package/src/resources/skills/mandu-styling/rules/style-tailwind-v4-gotchas.md +179 -179
- package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +229 -229
- package/src/resources/skills/mandu-testing/SKILL.md +99 -99
- package/src/resources/skills/mandu-testing/metadata.json +13 -13
- package/src/resources/skills/mandu-testing/rules/_sections.md +26 -26
- package/src/resources/skills/mandu-testing/rules/_template.md +65 -65
- package/src/resources/skills/mandu-testing/rules/test-component-island.md +195 -195
- package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +196 -196
- package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +219 -219
- package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +192 -192
- package/src/resources/skills/mandu-ui/SKILL.md +117 -117
- package/src/resources/skills/mandu-ui/_sections.md +23 -23
- package/src/resources/skills/mandu-ui/_template.md +32 -32
- package/src/resources/skills/mandu-ui/metadata.json +13 -13
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +232 -232
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +238 -238
- package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +259 -259
- package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +258 -258
- package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +213 -213
- package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +209 -209
- package/src/resources/skills/recipes.ts +932 -932
- package/src/tools/ate.ts +219 -0
- package/src/tools/index.ts +4 -1
- package/src/tools/project.ts +334 -334
- package/src/tools/runtime.ts +497 -497
- package/src/tools/seo.ts +417 -417
- package/src/utils/withWarnings.ts +83 -83
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Island Style Variants
|
|
3
|
-
impact: HIGH
|
|
4
|
-
impactDescription: Conditional styling patterns for Island components
|
|
5
|
-
tags: styling, island, variants, cva
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Island Style Variants
|
|
9
|
-
|
|
10
|
-
**Impact: HIGH (Conditional styling patterns for Island components)**
|
|
11
|
-
|
|
12
|
-
Island 컴포넌트의 조건부 스타일링을 체계적으로 관리하세요.
|
|
13
|
-
|
|
14
|
-
**class-variance-authority (cva) 설치:**
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
bun add class-variance-authority
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## cva 기본 패턴
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
// app/button/client.tsx
|
|
24
|
-
"use client";
|
|
25
|
-
|
|
26
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
27
|
-
import { cn } from "@/lib/utils";
|
|
28
|
-
|
|
29
|
-
const buttonVariants = cva(
|
|
30
|
-
// Base styles
|
|
31
|
-
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50",
|
|
32
|
-
{
|
|
33
|
-
variants: {
|
|
34
|
-
variant: {
|
|
35
|
-
default: "bg-mandu-primary text-white hover:bg-mandu-primary/90",
|
|
36
|
-
destructive: "bg-red-500 text-white hover:bg-red-500/90",
|
|
37
|
-
outline: "border border-input bg-background hover:bg-accent",
|
|
38
|
-
secondary: "bg-mandu-secondary text-white hover:bg-mandu-secondary/80",
|
|
39
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
40
|
-
link: "text-mandu-primary underline-offset-4 hover:underline",
|
|
41
|
-
},
|
|
42
|
-
size: {
|
|
43
|
-
default: "h-10 px-4 py-2",
|
|
44
|
-
sm: "h-9 rounded-md px-3",
|
|
45
|
-
lg: "h-11 rounded-md px-8",
|
|
46
|
-
icon: "h-10 w-10",
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
defaultVariants: {
|
|
50
|
-
variant: "default",
|
|
51
|
-
size: "default",
|
|
52
|
-
},
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
interface ButtonProps
|
|
57
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
58
|
-
VariantProps<typeof buttonVariants> {}
|
|
59
|
-
|
|
60
|
-
export function ButtonIsland({ className, variant, size, ...props }: ButtonProps) {
|
|
61
|
-
return (
|
|
62
|
-
<button className={cn(buttonVariants({ variant, size }), className)} {...props} />
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Export for reuse
|
|
67
|
-
export { buttonVariants };
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## 복합 Variants
|
|
71
|
-
|
|
72
|
-
```tsx
|
|
73
|
-
const cardVariants = cva("rounded-lg border", {
|
|
74
|
-
variants: {
|
|
75
|
-
variant: {
|
|
76
|
-
default: "bg-white",
|
|
77
|
-
elevated: "bg-white shadow-lg",
|
|
78
|
-
outlined: "bg-transparent",
|
|
79
|
-
},
|
|
80
|
-
padding: {
|
|
81
|
-
none: "",
|
|
82
|
-
sm: "p-4",
|
|
83
|
-
md: "p-6",
|
|
84
|
-
lg: "p-8",
|
|
85
|
-
},
|
|
86
|
-
interactive: {
|
|
87
|
-
true: "cursor-pointer hover:shadow-md transition-shadow",
|
|
88
|
-
false: "",
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
compoundVariants: [
|
|
92
|
-
{
|
|
93
|
-
variant: "elevated",
|
|
94
|
-
interactive: true,
|
|
95
|
-
className: "hover:shadow-xl",
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
variant: "outlined",
|
|
99
|
-
interactive: true,
|
|
100
|
-
className: "hover:border-mandu-primary",
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
defaultVariants: {
|
|
104
|
-
variant: "default",
|
|
105
|
-
padding: "md",
|
|
106
|
-
interactive: false,
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Boolean Variants
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
const inputVariants = cva(
|
|
115
|
-
"flex h-10 w-full rounded-md border px-3 py-2 text-sm",
|
|
116
|
-
{
|
|
117
|
-
variants: {
|
|
118
|
-
hasError: {
|
|
119
|
-
true: "border-red-500 focus:ring-red-500",
|
|
120
|
-
false: "border-gray-300 focus:ring-mandu-primary",
|
|
121
|
-
},
|
|
122
|
-
isDisabled: {
|
|
123
|
-
true: "bg-gray-100 cursor-not-allowed opacity-50",
|
|
124
|
-
false: "bg-white",
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
defaultVariants: {
|
|
128
|
-
hasError: false,
|
|
129
|
-
isDisabled: false,
|
|
130
|
-
},
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
export function InputIsland({ error, disabled, ...props }: InputProps) {
|
|
135
|
-
return (
|
|
136
|
-
<input
|
|
137
|
-
className={inputVariants({
|
|
138
|
-
hasError: !!error,
|
|
139
|
-
isDisabled: disabled,
|
|
140
|
-
})}
|
|
141
|
-
disabled={disabled}
|
|
142
|
-
{...props}
|
|
143
|
-
/>
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Responsive Variants
|
|
149
|
-
|
|
150
|
-
```tsx
|
|
151
|
-
// 반응형은 Tailwind 클래스로 처리
|
|
152
|
-
export function CardIsland({ className, ...props }: CardProps) {
|
|
153
|
-
return (
|
|
154
|
-
<div
|
|
155
|
-
className={cn(
|
|
156
|
-
cardVariants({ variant: "default", padding: "md" }),
|
|
157
|
-
// 반응형 오버라이드
|
|
158
|
-
"md:p-8 lg:p-10",
|
|
159
|
-
className
|
|
160
|
-
)}
|
|
161
|
-
{...props}
|
|
162
|
-
/>
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## Slot Pattern과 조합
|
|
168
|
-
|
|
169
|
-
```tsx
|
|
170
|
-
// app/alert/client.tsx
|
|
171
|
-
"use client";
|
|
172
|
-
|
|
173
|
-
import { cva } from "class-variance-authority";
|
|
174
|
-
|
|
175
|
-
const alertVariants = cva(
|
|
176
|
-
"relative w-full rounded-lg border p-4",
|
|
177
|
-
{
|
|
178
|
-
variants: {
|
|
179
|
-
variant: {
|
|
180
|
-
default: "bg-background text-foreground",
|
|
181
|
-
info: "border-blue-200 bg-blue-50 text-blue-900",
|
|
182
|
-
success: "border-green-200 bg-green-50 text-green-900",
|
|
183
|
-
warning: "border-yellow-200 bg-yellow-50 text-yellow-900",
|
|
184
|
-
error: "border-red-200 bg-red-50 text-red-900",
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
defaultVariants: {
|
|
188
|
-
variant: "default",
|
|
189
|
-
},
|
|
190
|
-
}
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const iconVariants = cva("h-5 w-5", {
|
|
194
|
-
variants: {
|
|
195
|
-
variant: {
|
|
196
|
-
default: "text-foreground",
|
|
197
|
-
info: "text-blue-600",
|
|
198
|
-
success: "text-green-600",
|
|
199
|
-
warning: "text-yellow-600",
|
|
200
|
-
error: "text-red-600",
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
export const Alert = {
|
|
206
|
-
Root: ({ variant, className, ...props }) => (
|
|
207
|
-
<div className={cn(alertVariants({ variant }), className)} {...props} />
|
|
208
|
-
),
|
|
209
|
-
Icon: ({ variant, className, ...props }) => (
|
|
210
|
-
<span className={cn(iconVariants({ variant }), className)} {...props} />
|
|
211
|
-
),
|
|
212
|
-
Title: ({ className, ...props }) => (
|
|
213
|
-
<h5 className={cn("font-medium leading-none", className)} {...props} />
|
|
214
|
-
),
|
|
215
|
-
Description: ({ className, ...props }) => (
|
|
216
|
-
<div className={cn("text-sm opacity-90", className)} {...props} />
|
|
217
|
-
),
|
|
218
|
-
};
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Reference: [class-variance-authority](https://cva.style/docs)
|
|
1
|
+
---
|
|
2
|
+
title: Island Style Variants
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Conditional styling patterns for Island components
|
|
5
|
+
tags: styling, island, variants, cva
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Island Style Variants
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Conditional styling patterns for Island components)**
|
|
11
|
+
|
|
12
|
+
Island 컴포넌트의 조건부 스타일링을 체계적으로 관리하세요.
|
|
13
|
+
|
|
14
|
+
**class-variance-authority (cva) 설치:**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add class-variance-authority
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## cva 기본 패턴
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// app/button/client.tsx
|
|
24
|
+
"use client";
|
|
25
|
+
|
|
26
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
27
|
+
import { cn } from "@/lib/utils";
|
|
28
|
+
|
|
29
|
+
const buttonVariants = cva(
|
|
30
|
+
// Base styles
|
|
31
|
+
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50",
|
|
32
|
+
{
|
|
33
|
+
variants: {
|
|
34
|
+
variant: {
|
|
35
|
+
default: "bg-mandu-primary text-white hover:bg-mandu-primary/90",
|
|
36
|
+
destructive: "bg-red-500 text-white hover:bg-red-500/90",
|
|
37
|
+
outline: "border border-input bg-background hover:bg-accent",
|
|
38
|
+
secondary: "bg-mandu-secondary text-white hover:bg-mandu-secondary/80",
|
|
39
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
40
|
+
link: "text-mandu-primary underline-offset-4 hover:underline",
|
|
41
|
+
},
|
|
42
|
+
size: {
|
|
43
|
+
default: "h-10 px-4 py-2",
|
|
44
|
+
sm: "h-9 rounded-md px-3",
|
|
45
|
+
lg: "h-11 rounded-md px-8",
|
|
46
|
+
icon: "h-10 w-10",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
variant: "default",
|
|
51
|
+
size: "default",
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
interface ButtonProps
|
|
57
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
58
|
+
VariantProps<typeof buttonVariants> {}
|
|
59
|
+
|
|
60
|
+
export function ButtonIsland({ className, variant, size, ...props }: ButtonProps) {
|
|
61
|
+
return (
|
|
62
|
+
<button className={cn(buttonVariants({ variant, size }), className)} {...props} />
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Export for reuse
|
|
67
|
+
export { buttonVariants };
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 복합 Variants
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const cardVariants = cva("rounded-lg border", {
|
|
74
|
+
variants: {
|
|
75
|
+
variant: {
|
|
76
|
+
default: "bg-white",
|
|
77
|
+
elevated: "bg-white shadow-lg",
|
|
78
|
+
outlined: "bg-transparent",
|
|
79
|
+
},
|
|
80
|
+
padding: {
|
|
81
|
+
none: "",
|
|
82
|
+
sm: "p-4",
|
|
83
|
+
md: "p-6",
|
|
84
|
+
lg: "p-8",
|
|
85
|
+
},
|
|
86
|
+
interactive: {
|
|
87
|
+
true: "cursor-pointer hover:shadow-md transition-shadow",
|
|
88
|
+
false: "",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
compoundVariants: [
|
|
92
|
+
{
|
|
93
|
+
variant: "elevated",
|
|
94
|
+
interactive: true,
|
|
95
|
+
className: "hover:shadow-xl",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
variant: "outlined",
|
|
99
|
+
interactive: true,
|
|
100
|
+
className: "hover:border-mandu-primary",
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
defaultVariants: {
|
|
104
|
+
variant: "default",
|
|
105
|
+
padding: "md",
|
|
106
|
+
interactive: false,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Boolean Variants
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
const inputVariants = cva(
|
|
115
|
+
"flex h-10 w-full rounded-md border px-3 py-2 text-sm",
|
|
116
|
+
{
|
|
117
|
+
variants: {
|
|
118
|
+
hasError: {
|
|
119
|
+
true: "border-red-500 focus:ring-red-500",
|
|
120
|
+
false: "border-gray-300 focus:ring-mandu-primary",
|
|
121
|
+
},
|
|
122
|
+
isDisabled: {
|
|
123
|
+
true: "bg-gray-100 cursor-not-allowed opacity-50",
|
|
124
|
+
false: "bg-white",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
defaultVariants: {
|
|
128
|
+
hasError: false,
|
|
129
|
+
isDisabled: false,
|
|
130
|
+
},
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
export function InputIsland({ error, disabled, ...props }: InputProps) {
|
|
135
|
+
return (
|
|
136
|
+
<input
|
|
137
|
+
className={inputVariants({
|
|
138
|
+
hasError: !!error,
|
|
139
|
+
isDisabled: disabled,
|
|
140
|
+
})}
|
|
141
|
+
disabled={disabled}
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Responsive Variants
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
// 반응형은 Tailwind 클래스로 처리
|
|
152
|
+
export function CardIsland({ className, ...props }: CardProps) {
|
|
153
|
+
return (
|
|
154
|
+
<div
|
|
155
|
+
className={cn(
|
|
156
|
+
cardVariants({ variant: "default", padding: "md" }),
|
|
157
|
+
// 반응형 오버라이드
|
|
158
|
+
"md:p-8 lg:p-10",
|
|
159
|
+
className
|
|
160
|
+
)}
|
|
161
|
+
{...props}
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Slot Pattern과 조합
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
// app/alert/client.tsx
|
|
171
|
+
"use client";
|
|
172
|
+
|
|
173
|
+
import { cva } from "class-variance-authority";
|
|
174
|
+
|
|
175
|
+
const alertVariants = cva(
|
|
176
|
+
"relative w-full rounded-lg border p-4",
|
|
177
|
+
{
|
|
178
|
+
variants: {
|
|
179
|
+
variant: {
|
|
180
|
+
default: "bg-background text-foreground",
|
|
181
|
+
info: "border-blue-200 bg-blue-50 text-blue-900",
|
|
182
|
+
success: "border-green-200 bg-green-50 text-green-900",
|
|
183
|
+
warning: "border-yellow-200 bg-yellow-50 text-yellow-900",
|
|
184
|
+
error: "border-red-200 bg-red-50 text-red-900",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
defaultVariants: {
|
|
188
|
+
variant: "default",
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const iconVariants = cva("h-5 w-5", {
|
|
194
|
+
variants: {
|
|
195
|
+
variant: {
|
|
196
|
+
default: "text-foreground",
|
|
197
|
+
info: "text-blue-600",
|
|
198
|
+
success: "text-green-600",
|
|
199
|
+
warning: "text-yellow-600",
|
|
200
|
+
error: "text-red-600",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
export const Alert = {
|
|
206
|
+
Root: ({ variant, className, ...props }) => (
|
|
207
|
+
<div className={cn(alertVariants({ variant }), className)} {...props} />
|
|
208
|
+
),
|
|
209
|
+
Icon: ({ variant, className, ...props }) => (
|
|
210
|
+
<span className={cn(iconVariants({ variant }), className)} {...props} />
|
|
211
|
+
),
|
|
212
|
+
Title: ({ className, ...props }) => (
|
|
213
|
+
<h5 className={cn("font-medium leading-none", className)} {...props} />
|
|
214
|
+
),
|
|
215
|
+
Description: ({ className, ...props }) => (
|
|
216
|
+
<div className={cn("text-sm opacity-90", className)} {...props} />
|
|
217
|
+
),
|
|
218
|
+
};
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Reference: [class-variance-authority](https://cva.style/docs)
|