@mandujs/mcp 0.9.19 → 0.9.21
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 +320 -0
- package/package.json +1 -1
- package/src/activity-monitor.ts +847 -231
- package/src/resources/handlers.ts +244 -0
- package/src/resources/skills/guides.ts +1136 -0
- package/src/resources/skills/index.ts +12 -0
- package/src/resources/skills/loader.ts +218 -0
- package/src/resources/skills/mandu-composition/SKILL.md +91 -0
- package/src/resources/skills/mandu-composition/metadata.json +13 -0
- package/src/resources/skills/mandu-composition/rules/_sections.md +26 -0
- package/src/resources/skills/mandu-composition/rules/_template.md +77 -0
- package/src/resources/skills/mandu-composition/rules/comp-arch-avoid-boolean-props.md +146 -0
- package/src/resources/skills/mandu-composition/rules/comp-arch-compound-components.md +164 -0
- package/src/resources/skills/mandu-composition/rules/comp-island-event.md +161 -0
- package/src/resources/skills/mandu-composition/rules/comp-island-slot-split.md +167 -0
- package/src/resources/skills/mandu-composition/rules/comp-pattern-children.md +149 -0
- package/src/resources/skills/mandu-composition/rules/comp-state-context-interface.md +148 -0
- package/src/resources/skills/mandu-composition/rules/comp-state-lift-state.md +150 -0
- package/src/resources/skills/mandu-deployment/SKILL.md +92 -0
- package/src/resources/skills/mandu-deployment/_sections.md +41 -0
- package/src/resources/skills/mandu-deployment/_template.md +38 -0
- package/src/resources/skills/mandu-deployment/metadata.json +13 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-build-bun.md +109 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-build-output.md +115 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-cicd-github.md +219 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-bun.md +150 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-docker-compose.md +223 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-fly.md +152 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-render.md +179 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-supabase.md +323 -0
- package/src/resources/skills/mandu-deployment/rules/deploy-platform-vercel.md +140 -0
- package/src/resources/skills/mandu-fs-routes/SKILL.md +82 -0
- package/src/resources/skills/mandu-fs-routes/metadata.json +12 -0
- package/src/resources/skills/mandu-fs-routes/rules/_sections.md +36 -0
- package/src/resources/skills/mandu-fs-routes/rules/_template.md +69 -0
- package/src/resources/skills/mandu-fs-routes/rules/routes-api-methods.md +65 -0
- package/src/resources/skills/mandu-fs-routes/rules/routes-dynamic-param.md +93 -0
- package/src/resources/skills/mandu-fs-routes/rules/routes-naming-page.md +55 -0
- package/src/resources/skills/mandu-guard/SKILL.md +129 -0
- package/src/resources/skills/mandu-guard/metadata.json +12 -0
- package/src/resources/skills/mandu-guard/rules/_sections.md +36 -0
- package/src/resources/skills/mandu-guard/rules/_template.md +82 -0
- package/src/resources/skills/mandu-guard/rules/guard-config-rules.md +100 -0
- package/src/resources/skills/mandu-guard/rules/guard-layer-direction.md +76 -0
- package/src/resources/skills/mandu-guard/rules/guard-preset-mandu.md +81 -0
- package/src/resources/skills/mandu-guard/rules/guard-validate-import.md +80 -0
- package/src/resources/skills/mandu-hydration/SKILL.md +91 -0
- package/src/resources/skills/mandu-hydration/metadata.json +12 -0
- package/src/resources/skills/mandu-hydration/rules/_sections.md +31 -0
- package/src/resources/skills/mandu-hydration/rules/_template.md +72 -0
- package/src/resources/skills/mandu-hydration/rules/hydration-data-event.md +109 -0
- package/src/resources/skills/mandu-hydration/rules/hydration-directive-use-client.md +55 -0
- package/src/resources/skills/mandu-hydration/rules/hydration-island-setup.md +113 -0
- package/src/resources/skills/mandu-hydration/rules/hydration-priority-visible.md +68 -0
- package/src/resources/skills/mandu-performance/SKILL.md +85 -0
- package/src/resources/skills/mandu-performance/metadata.json +14 -0
- package/src/resources/skills/mandu-performance/rules/_sections.md +31 -0
- package/src/resources/skills/mandu-performance/rules/_template.md +64 -0
- package/src/resources/skills/mandu-performance/rules/perf-async-defer-await.md +103 -0
- package/src/resources/skills/mandu-performance/rules/perf-async-parallel.md +95 -0
- package/src/resources/skills/mandu-performance/rules/perf-bun-file.md +124 -0
- package/src/resources/skills/mandu-performance/rules/perf-bun-serve.md +125 -0
- package/src/resources/skills/mandu-performance/rules/perf-bundle-imports.md +80 -0
- package/src/resources/skills/mandu-performance/rules/perf-bundle-island-lazy.md +145 -0
- package/src/resources/skills/mandu-performance/rules/perf-cache-react.md +98 -0
- package/src/resources/skills/mandu-performance/rules/perf-render-transitions.md +154 -0
- package/src/resources/skills/mandu-security/SKILL.md +87 -0
- package/src/resources/skills/mandu-security/metadata.json +13 -0
- package/src/resources/skills/mandu-security/rules/_sections.md +31 -0
- package/src/resources/skills/mandu-security/rules/_template.md +74 -0
- package/src/resources/skills/mandu-security/rules/sec-auth-guard.md +127 -0
- package/src/resources/skills/mandu-security/rules/sec-env-management.md +133 -0
- package/src/resources/skills/mandu-security/rules/sec-input-validate.md +148 -0
- package/src/resources/skills/mandu-security/rules/sec-protect-csrf.md +146 -0
- package/src/resources/skills/mandu-security/rules/sec-protect-headers.md +138 -0
- package/src/resources/skills/mandu-slot/SKILL.md +85 -0
- package/src/resources/skills/mandu-slot/metadata.json +12 -0
- package/src/resources/skills/mandu-slot/rules/_sections.md +36 -0
- package/src/resources/skills/mandu-slot/rules/_template.md +63 -0
- package/src/resources/skills/mandu-slot/rules/slot-basic-structure.md +38 -0
- package/src/resources/skills/mandu-slot/rules/slot-ctx-response.md +56 -0
- package/src/resources/skills/mandu-slot/rules/slot-guard-auth.md +59 -0
- package/src/resources/skills/mandu-slot/rules/slot-http-methods.md +64 -0
- package/src/resources/skills/mandu-styling/SKILL.md +118 -0
- package/src/resources/skills/mandu-styling/_sections.md +36 -0
- package/src/resources/skills/mandu-styling/_template.md +32 -0
- package/src/resources/skills/mandu-styling/metadata.json +13 -0
- package/src/resources/skills/mandu-styling/rules/style-component-compound.md +235 -0
- package/src/resources/skills/mandu-styling/rules/style-component-slots.md +255 -0
- package/src/resources/skills/mandu-styling/rules/style-component-tokens.md +205 -0
- package/src/resources/skills/mandu-styling/rules/style-island-animations.md +272 -0
- package/src/resources/skills/mandu-styling/rules/style-island-scoping.md +167 -0
- package/src/resources/skills/mandu-styling/rules/style-island-variants.md +221 -0
- package/src/resources/skills/mandu-styling/rules/style-perf-critical.md +209 -0
- package/src/resources/skills/mandu-styling/rules/style-perf-purge.md +192 -0
- package/src/resources/skills/mandu-styling/rules/style-setup-modules.md +162 -0
- package/src/resources/skills/mandu-styling/rules/style-setup-panda.md +164 -0
- package/src/resources/skills/mandu-styling/rules/style-setup-tailwind.md +161 -0
- package/src/resources/skills/mandu-styling/rules/style-theme-darkmode.md +229 -0
- package/src/resources/skills/mandu-testing/SKILL.md +99 -0
- package/src/resources/skills/mandu-testing/metadata.json +13 -0
- package/src/resources/skills/mandu-testing/rules/_sections.md +26 -0
- package/src/resources/skills/mandu-testing/rules/_template.md +65 -0
- package/src/resources/skills/mandu-testing/rules/test-component-island.md +195 -0
- package/src/resources/skills/mandu-testing/rules/test-e2e-playwright.md +196 -0
- package/src/resources/skills/mandu-testing/rules/test-mock-fetch.md +219 -0
- package/src/resources/skills/mandu-testing/rules/test-slot-unit.md +192 -0
- package/src/resources/skills/mandu-ui/SKILL.md +117 -0
- package/src/resources/skills/mandu-ui/_sections.md +23 -0
- package/src/resources/skills/mandu-ui/_template.md +32 -0
- package/src/resources/skills/mandu-ui/metadata.json +13 -0
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-aria.md +232 -0
- package/src/resources/skills/mandu-ui/rules/ui-accessibility-focus.md +238 -0
- package/src/resources/skills/mandu-ui/rules/ui-composition-patterns.md +259 -0
- package/src/resources/skills/mandu-ui/rules/ui-island-integration.md +258 -0
- package/src/resources/skills/mandu-ui/rules/ui-radix-patterns.md +213 -0
- package/src/resources/skills/mandu-ui/rules/ui-shadcn-setup.md +209 -0
- package/src/resources/skills/recipes.ts +932 -0
- package/src/server.ts +3 -0
- package/src/tools/hydration.ts +8 -8
- package/src/tools/index.ts +1 -0
- package/src/tools/seo.ts +417 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Context Response Methods
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Consistent API responses
|
|
5
|
+
tags: slot, context, response
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Context Response Methods
|
|
9
|
+
|
|
10
|
+
Use `ctx.ok()`, `ctx.created()`, `ctx.error()` instead of manual Response objects.
|
|
11
|
+
These methods ensure consistent response format across your API.
|
|
12
|
+
|
|
13
|
+
**Incorrect (manual Response):**
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export default Mandu.filling()
|
|
17
|
+
.get((ctx) => {
|
|
18
|
+
return new Response(JSON.stringify({ data: [] }), {
|
|
19
|
+
headers: { "Content-Type": "application/json" }
|
|
20
|
+
});
|
|
21
|
+
})
|
|
22
|
+
.post(async (ctx) => {
|
|
23
|
+
return new Response(JSON.stringify({ error: "Bad request" }), {
|
|
24
|
+
status: 400
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (context methods):**
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
export default Mandu.filling()
|
|
33
|
+
.get((ctx) => {
|
|
34
|
+
return ctx.ok({ data: [] });
|
|
35
|
+
})
|
|
36
|
+
.post(async (ctx) => {
|
|
37
|
+
const body = await ctx.body();
|
|
38
|
+
if (!body.name) {
|
|
39
|
+
return ctx.error("name is required");
|
|
40
|
+
}
|
|
41
|
+
return ctx.created({ data: body });
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Response Methods
|
|
46
|
+
|
|
47
|
+
| Method | Status | Use Case |
|
|
48
|
+
|--------|--------|----------|
|
|
49
|
+
| `ctx.ok(data)` | 200 | Success |
|
|
50
|
+
| `ctx.created(data)` | 201 | Resource created |
|
|
51
|
+
| `ctx.noContent()` | 204 | Success, no body |
|
|
52
|
+
| `ctx.error(msg)` | 400 | Bad request |
|
|
53
|
+
| `ctx.unauthorized(msg)` | 401 | Auth required |
|
|
54
|
+
| `ctx.forbidden(msg)` | 403 | Permission denied |
|
|
55
|
+
| `ctx.notFound(msg)` | 404 | Not found |
|
|
56
|
+
| `ctx.fail(msg)` | 500 | Server error |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use .guard() for Authentication
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Clean auth separation
|
|
5
|
+
tags: slot, guard, authentication
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use .guard() for Authentication
|
|
9
|
+
|
|
10
|
+
Use `.guard()` to check authentication before handlers run. Return a response
|
|
11
|
+
to block the request, or return void/undefined to continue.
|
|
12
|
+
|
|
13
|
+
**Incorrect (auth check in every handler):**
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
export default Mandu.filling()
|
|
17
|
+
.get((ctx) => {
|
|
18
|
+
const user = ctx.get("user");
|
|
19
|
+
if (!user) {
|
|
20
|
+
return ctx.unauthorized("Login required");
|
|
21
|
+
}
|
|
22
|
+
return ctx.ok({ data: [] });
|
|
23
|
+
})
|
|
24
|
+
.post(async (ctx) => {
|
|
25
|
+
const user = ctx.get("user");
|
|
26
|
+
if (!user) {
|
|
27
|
+
return ctx.unauthorized("Login required");
|
|
28
|
+
}
|
|
29
|
+
// ... create logic
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Correct (single guard):**
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
export default Mandu.filling()
|
|
37
|
+
.guard((ctx) => {
|
|
38
|
+
const user = ctx.get("user");
|
|
39
|
+
if (!user) {
|
|
40
|
+
return ctx.unauthorized("Login required");
|
|
41
|
+
}
|
|
42
|
+
// void return = continue to handlers
|
|
43
|
+
})
|
|
44
|
+
.get((ctx) => {
|
|
45
|
+
const user = ctx.get("user");
|
|
46
|
+
return ctx.ok({ data: [], user });
|
|
47
|
+
})
|
|
48
|
+
.post(async (ctx) => {
|
|
49
|
+
const body = await ctx.body();
|
|
50
|
+
return ctx.created({ data: body });
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Guard Rules
|
|
55
|
+
|
|
56
|
+
1. Return response → Request blocked
|
|
57
|
+
2. Return void → Continue to handler
|
|
58
|
+
3. Guard runs before ALL HTTP method handlers
|
|
59
|
+
4. Use `.onRequest()` to set user before guard runs
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Appropriate HTTP Methods
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: RESTful API design
|
|
5
|
+
tags: slot, http, methods, rest
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Appropriate HTTP Methods
|
|
9
|
+
|
|
10
|
+
Chain HTTP methods on Mandu.filling() for RESTful API design.
|
|
11
|
+
|
|
12
|
+
**Incorrect (single handler for all):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
export default Mandu.filling()
|
|
16
|
+
.get(async (ctx) => {
|
|
17
|
+
const method = ctx.req.method;
|
|
18
|
+
if (method === "GET") {
|
|
19
|
+
return ctx.ok({ data: [] });
|
|
20
|
+
} else if (method === "POST") {
|
|
21
|
+
const body = await ctx.body();
|
|
22
|
+
return ctx.created({ data: body });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (separate methods):**
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
export default Mandu.filling()
|
|
31
|
+
.get((ctx) => {
|
|
32
|
+
// GET /api/users - List users
|
|
33
|
+
return ctx.ok({ data: [] });
|
|
34
|
+
})
|
|
35
|
+
.post(async (ctx) => {
|
|
36
|
+
// POST /api/users - Create user
|
|
37
|
+
const body = await ctx.body<{ name: string }>();
|
|
38
|
+
return ctx.created({ data: { id: 1, ...body } });
|
|
39
|
+
})
|
|
40
|
+
.put(async (ctx) => {
|
|
41
|
+
// PUT /api/users/:id - Replace user
|
|
42
|
+
const body = await ctx.body();
|
|
43
|
+
return ctx.ok({ data: body });
|
|
44
|
+
})
|
|
45
|
+
.patch(async (ctx) => {
|
|
46
|
+
// PATCH /api/users/:id - Update user
|
|
47
|
+
const body = await ctx.body();
|
|
48
|
+
return ctx.ok({ data: body });
|
|
49
|
+
})
|
|
50
|
+
.delete((ctx) => {
|
|
51
|
+
// DELETE /api/users/:id - Delete user
|
|
52
|
+
return ctx.noContent();
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## HTTP Method Semantics
|
|
57
|
+
|
|
58
|
+
| Method | Idempotent | Use Case |
|
|
59
|
+
|--------|------------|----------|
|
|
60
|
+
| GET | Yes | Read resource |
|
|
61
|
+
| POST | No | Create resource |
|
|
62
|
+
| PUT | Yes | Replace resource |
|
|
63
|
+
| PATCH | No | Partial update |
|
|
64
|
+
| DELETE | Yes | Remove resource |
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Mandu Styling
|
|
3
|
+
description: CSS framework integration and Island styling patterns
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0.0"
|
|
6
|
+
author: mandu
|
|
7
|
+
globs:
|
|
8
|
+
- "tailwind.config.{js,ts}"
|
|
9
|
+
- "panda.config.{js,ts}"
|
|
10
|
+
- "postcss.config.{js,ts}"
|
|
11
|
+
- "**/*.css"
|
|
12
|
+
- "**/*.module.css"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Mandu Styling Skill
|
|
16
|
+
|
|
17
|
+
Mandu Island 아키텍처에 최적화된 CSS 스타일링 가이드입니다.
|
|
18
|
+
|
|
19
|
+
## 핵심 원칙
|
|
20
|
+
|
|
21
|
+
1. **Zero-Runtime**: 빌드 타임 CSS 생성 (SSR 호환)
|
|
22
|
+
2. **Island 격리**: 컴포넌트 간 스타일 충돌 방지
|
|
23
|
+
3. **Composition**: Tailwind utility + 컴포넌트 패턴
|
|
24
|
+
4. **Performance**: Critical CSS, Tree-shaking
|
|
25
|
+
|
|
26
|
+
## 권장 스택
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Primary: Tailwind CSS + clsx/tailwind-merge
|
|
30
|
+
Alternative: Panda CSS (type-safe 선호 시)
|
|
31
|
+
Fallback: CSS Modules (최소 의존성)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 빠른 시작
|
|
35
|
+
|
|
36
|
+
### Tailwind CSS 설정
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bun add -d tailwindcss postcss autoprefixer
|
|
40
|
+
bunx tailwindcss init -p
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// tailwind.config.ts
|
|
45
|
+
import type { Config } from "tailwindcss";
|
|
46
|
+
|
|
47
|
+
export default {
|
|
48
|
+
content: ["./app/**/*.{ts,tsx}"],
|
|
49
|
+
theme: {
|
|
50
|
+
extend: {
|
|
51
|
+
colors: {
|
|
52
|
+
mandu: {
|
|
53
|
+
primary: "#3b82f6",
|
|
54
|
+
secondary: "#64748b",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
plugins: [],
|
|
60
|
+
} satisfies Config;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```css
|
|
64
|
+
/* app/globals.css */
|
|
65
|
+
@tailwind base;
|
|
66
|
+
@tailwind components;
|
|
67
|
+
@tailwind utilities;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Island 스타일링
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
// app/counter/client.tsx
|
|
74
|
+
"use client";
|
|
75
|
+
|
|
76
|
+
import { cn } from "@/lib/utils";
|
|
77
|
+
|
|
78
|
+
interface CounterProps {
|
|
79
|
+
variant?: "default" | "outline";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function CounterIsland({ variant = "default" }: CounterProps) {
|
|
83
|
+
const variants = {
|
|
84
|
+
default: "bg-mandu-primary text-white",
|
|
85
|
+
outline: "border-2 border-mandu-primary text-mandu-primary",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<button className={cn("px-4 py-2 rounded-lg", variants[variant])}>
|
|
90
|
+
Count: 0
|
|
91
|
+
</button>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## cn 유틸리티
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// lib/utils.ts
|
|
100
|
+
import { clsx, type ClassValue } from "clsx";
|
|
101
|
+
import { twMerge } from "tailwind-merge";
|
|
102
|
+
|
|
103
|
+
export function cn(...inputs: ClassValue[]) {
|
|
104
|
+
return twMerge(clsx(inputs));
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 규칙 카테고리
|
|
109
|
+
|
|
110
|
+
| Category | Description | Rules |
|
|
111
|
+
|----------|-------------|-------|
|
|
112
|
+
| Setup | 프레임워크 설정 | 3 |
|
|
113
|
+
| Island | Island 스타일 패턴 | 3 |
|
|
114
|
+
| Component | 컴포넌트 스타일 | 3 |
|
|
115
|
+
| Performance | 최적화 | 2 |
|
|
116
|
+
| Theme | 테마/다크모드 | 1 |
|
|
117
|
+
|
|
118
|
+
→ 세부 규칙은 `rules/` 폴더 참조
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Mandu Styling - Rule Categories
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
CSS 프레임워크 초기 설정 및 Bun 통합
|
|
5
|
+
|
|
6
|
+
| Impact | Description |
|
|
7
|
+
|--------|-------------|
|
|
8
|
+
| CRITICAL | 프레임워크 설정 없이 스타일링 불가 |
|
|
9
|
+
|
|
10
|
+
## Island
|
|
11
|
+
Island 컴포넌트 특화 스타일링 패턴
|
|
12
|
+
|
|
13
|
+
| Impact | Description |
|
|
14
|
+
|--------|-------------|
|
|
15
|
+
| HIGH | 스타일 충돌 방지, 독립적 hydration 지원 |
|
|
16
|
+
|
|
17
|
+
## Component
|
|
18
|
+
컴포넌트 레벨 스타일링 패턴
|
|
19
|
+
|
|
20
|
+
| Impact | Description |
|
|
21
|
+
|--------|-------------|
|
|
22
|
+
| HIGH | 재사용성, 일관성, 유지보수성 향상 |
|
|
23
|
+
|
|
24
|
+
## Performance
|
|
25
|
+
CSS 빌드 및 로딩 최적화
|
|
26
|
+
|
|
27
|
+
| Impact | Description |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| MEDIUM | 번들 크기, 초기 로딩 성능 개선 |
|
|
30
|
+
|
|
31
|
+
## Theme
|
|
32
|
+
다크모드 및 테마 시스템
|
|
33
|
+
|
|
34
|
+
| Impact | Description |
|
|
35
|
+
|--------|-------------|
|
|
36
|
+
| MEDIUM | 사용자 경험, 접근성 향상 |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rule Title Here
|
|
3
|
+
impact: CRITICAL | HIGH | MEDIUM | LOW
|
|
4
|
+
impactDescription: One-line description of why this matters
|
|
5
|
+
tags: styling, tag2, tag3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Rule Title Here
|
|
9
|
+
|
|
10
|
+
**Impact: LEVEL (Impact description)**
|
|
11
|
+
|
|
12
|
+
규칙에 대한 간단한 설명.
|
|
13
|
+
|
|
14
|
+
**설정:**
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// 설정 예시
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**사용 예시:**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// 컴포넌트 예시
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 추가 패턴
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// 추가 패턴
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Reference: [문서 링크](https://example.com)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "mandu-styling",
|
|
3
|
+
"name": "Mandu Styling",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "CSS framework integration and Island styling patterns",
|
|
6
|
+
"author": "mandu",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://tailwindcss.com/docs",
|
|
9
|
+
"https://panda-css.com/docs",
|
|
10
|
+
"https://lightningcss.dev/"
|
|
11
|
+
],
|
|
12
|
+
"abstract": "Mandu Island 아키텍처에 최적화된 CSS 스타일링 패턴. Tailwind CSS를 primary로, Panda CSS와 CSS Modules를 대안으로 제공합니다."
|
|
13
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Compound Component Styling
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Consistent styling for compound component patterns
|
|
5
|
+
tags: styling, compound, composition, patterns
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Compound Component Styling
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (Consistent styling for compound component patterns)**
|
|
11
|
+
|
|
12
|
+
Mandu의 compound component 패턴에 맞는 스타일링 방법입니다.
|
|
13
|
+
|
|
14
|
+
## 기본 패턴
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
// app/card/client.tsx
|
|
18
|
+
"use client";
|
|
19
|
+
|
|
20
|
+
import { createContext, useContext } from "react";
|
|
21
|
+
import { cn } from "@/lib/utils";
|
|
22
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
23
|
+
|
|
24
|
+
// 1. Variants 정의
|
|
25
|
+
const cardVariants = cva("rounded-lg border", {
|
|
26
|
+
variants: {
|
|
27
|
+
variant: {
|
|
28
|
+
default: "bg-card text-card-foreground",
|
|
29
|
+
elevated: "bg-card shadow-lg",
|
|
30
|
+
ghost: "border-transparent",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
variant: "default",
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 2. Context로 variant 공유
|
|
39
|
+
type CardContextValue = VariantProps<typeof cardVariants>;
|
|
40
|
+
const CardContext = createContext<CardContextValue>({});
|
|
41
|
+
|
|
42
|
+
// 3. Compound 컴포넌트
|
|
43
|
+
export const Card = {
|
|
44
|
+
Root: ({
|
|
45
|
+
variant,
|
|
46
|
+
className,
|
|
47
|
+
children,
|
|
48
|
+
...props
|
|
49
|
+
}: React.HTMLAttributes<HTMLDivElement> & CardContextValue) => (
|
|
50
|
+
<CardContext.Provider value={{ variant }}>
|
|
51
|
+
<div className={cn(cardVariants({ variant }), className)} {...props}>
|
|
52
|
+
{children}
|
|
53
|
+
</div>
|
|
54
|
+
</CardContext.Provider>
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
Header: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
58
|
+
<div className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
59
|
+
),
|
|
60
|
+
|
|
61
|
+
Title: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
|
62
|
+
<h3 className={cn("text-2xl font-semibold leading-none tracking-tight", className)} {...props} />
|
|
63
|
+
),
|
|
64
|
+
|
|
65
|
+
Description: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
|
|
66
|
+
<p className={cn("text-sm text-muted-foreground", className)} {...props} />
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
Content: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
70
|
+
<div className={cn("p-6 pt-0", className)} {...props} />
|
|
71
|
+
),
|
|
72
|
+
|
|
73
|
+
Footer: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
|
74
|
+
<div className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
|
75
|
+
),
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 사용 예시
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Card.Root variant="elevated">
|
|
83
|
+
<Card.Header>
|
|
84
|
+
<Card.Title>Card Title</Card.Title>
|
|
85
|
+
<Card.Description>Card description</Card.Description>
|
|
86
|
+
</Card.Header>
|
|
87
|
+
<Card.Content>
|
|
88
|
+
Content here
|
|
89
|
+
</Card.Content>
|
|
90
|
+
<Card.Footer>
|
|
91
|
+
<button>Action</button>
|
|
92
|
+
</Card.Footer>
|
|
93
|
+
</Card.Root>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Context 기반 스타일 상속
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
// app/alert/client.tsx
|
|
100
|
+
"use client";
|
|
101
|
+
|
|
102
|
+
import { createContext, useContext } from "react";
|
|
103
|
+
|
|
104
|
+
type AlertVariant = "default" | "info" | "success" | "warning" | "error";
|
|
105
|
+
|
|
106
|
+
const AlertContext = createContext<{ variant: AlertVariant }>({
|
|
107
|
+
variant: "default",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const alertStyles = {
|
|
111
|
+
default: {
|
|
112
|
+
root: "bg-background text-foreground",
|
|
113
|
+
icon: "text-foreground",
|
|
114
|
+
},
|
|
115
|
+
info: {
|
|
116
|
+
root: "bg-blue-50 text-blue-900 border-blue-200",
|
|
117
|
+
icon: "text-blue-600",
|
|
118
|
+
},
|
|
119
|
+
success: {
|
|
120
|
+
root: "bg-green-50 text-green-900 border-green-200",
|
|
121
|
+
icon: "text-green-600",
|
|
122
|
+
},
|
|
123
|
+
warning: {
|
|
124
|
+
root: "bg-yellow-50 text-yellow-900 border-yellow-200",
|
|
125
|
+
icon: "text-yellow-600",
|
|
126
|
+
},
|
|
127
|
+
error: {
|
|
128
|
+
root: "bg-red-50 text-red-900 border-red-200",
|
|
129
|
+
icon: "text-red-600",
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const Alert = {
|
|
134
|
+
Root: ({ variant = "default", className, ...props }) => (
|
|
135
|
+
<AlertContext.Provider value={{ variant }}>
|
|
136
|
+
<div
|
|
137
|
+
role="alert"
|
|
138
|
+
className={cn(
|
|
139
|
+
"relative w-full rounded-lg border p-4",
|
|
140
|
+
alertStyles[variant].root,
|
|
141
|
+
className
|
|
142
|
+
)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
</AlertContext.Provider>
|
|
146
|
+
),
|
|
147
|
+
|
|
148
|
+
Icon: ({ className, children, ...props }) => {
|
|
149
|
+
const { variant } = useContext(AlertContext);
|
|
150
|
+
return (
|
|
151
|
+
<span
|
|
152
|
+
className={cn("h-5 w-5", alertStyles[variant].icon, className)}
|
|
153
|
+
{...props}
|
|
154
|
+
>
|
|
155
|
+
{children}
|
|
156
|
+
</span>
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
Title: ({ className, ...props }) => (
|
|
161
|
+
<h5
|
|
162
|
+
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
|
163
|
+
{...props}
|
|
164
|
+
/>
|
|
165
|
+
),
|
|
166
|
+
|
|
167
|
+
Description: ({ className, ...props }) => (
|
|
168
|
+
<div className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
|
|
169
|
+
),
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Slot 기반 커스터마이징
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
// asChild 패턴으로 스타일 전달
|
|
177
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
178
|
+
|
|
179
|
+
export const Button = ({
|
|
180
|
+
asChild,
|
|
181
|
+
className,
|
|
182
|
+
variant,
|
|
183
|
+
size,
|
|
184
|
+
...props
|
|
185
|
+
}) => {
|
|
186
|
+
const Comp = asChild ? Slot : "button";
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<Comp
|
|
190
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
191
|
+
{...props}
|
|
192
|
+
/>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// 사용: Link에 Button 스타일 적용
|
|
197
|
+
<Button asChild variant="outline">
|
|
198
|
+
<a href="/about">About</a>
|
|
199
|
+
</Button>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## CSS Modules와 조합
|
|
203
|
+
|
|
204
|
+
```css
|
|
205
|
+
/* card.module.css */
|
|
206
|
+
.root {
|
|
207
|
+
@apply rounded-lg border;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.root[data-variant="elevated"] {
|
|
211
|
+
@apply shadow-lg;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.header {
|
|
215
|
+
@apply p-6;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.content {
|
|
219
|
+
@apply p-6 pt-0;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
import styles from "./card.module.css";
|
|
225
|
+
|
|
226
|
+
export const Card = {
|
|
227
|
+
Root: ({ variant, ...props }) => (
|
|
228
|
+
<div className={styles.root} data-variant={variant} {...props} />
|
|
229
|
+
),
|
|
230
|
+
Header: (props) => <div className={styles.header} {...props} />,
|
|
231
|
+
Content: (props) => <div className={styles.content} {...props} />,
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Reference: [Radix UI Primitives](https://www.radix-ui.com/primitives)
|