@wangs-ui/skills 1.0.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/dist/bin.js +84 -0
- package/dist/index.js +2 -0
- package/dist/skills/create-form/SKILL.md +67 -0
- package/dist/skills/data-table/SKILL.md +68 -0
- package/dist/skills/dialog-modal/SKILL.md +58 -0
- package/dist/skills/i18n-usage/SKILL.md +232 -0
- package/dist/skills/layout-navigation/SKILL.md +62 -0
- package/dist/skills/react19-compiler-typescript/SKILL.md +388 -0
- package/dist/skills/typescript-strict-typing/SKILL.md +317 -0
- package/dist/skills/wangs-ui-components/SKILL.md +108 -0
- package/dist/src-BsIrKDsV.js +244 -0
- package/package.json +58 -0
- package/skills/create-form/SKILL.md +67 -0
- package/skills/data-table/SKILL.md +68 -0
- package/skills/dialog-modal/SKILL.md +58 -0
- package/skills/i18n-usage/SKILL.md +232 -0
- package/skills/layout-navigation/SKILL.md +62 -0
- package/skills/react19-compiler-typescript/SKILL.md +388 -0
- package/skills/typescript-strict-typing/SKILL.md +317 -0
- package/skills/wangs-ui-components/SKILL.md +108 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript-strict-typing
|
|
3
|
+
description: Enforce strict TypeScript typing discipline and naming conventions whenever writing, generating, reviewing, or refactoring TypeScript/TSX code. Use this any time code contains `any`, loose/implicit types, untyped catch blocks, unchecked type assertions, boolean-flag state instead of variants, or inconsistent naming — even if the user didn't explicitly ask for a "strict" pass. Governs `any` vs `unknown`, narrowing, discriminated unions, `interface` vs `type` usage, naming conventions, and tsconfig strictness baseline.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript Strict Typing Enforcer
|
|
7
|
+
|
|
8
|
+
## Why this matters
|
|
9
|
+
|
|
10
|
+
TypeScript's type system is only as strong as its weakest escape hatch. A single `any`,
|
|
11
|
+
an un-narrowed `unknown`, or a lazy `as` assertion silently turns off the compiler for
|
|
12
|
+
everything downstream of it — the bug doesn't disappear, it just moves to runtime where
|
|
13
|
+
it's more expensive to find. The goal of this skill is not "add types for the sake of
|
|
14
|
+
it," it's **make illegal states unrepresentable** and **make the compiler prove
|
|
15
|
+
correctness wherever possible**, so bugs surface at build time instead of in production.
|
|
16
|
+
|
|
17
|
+
Apply these rules by default whenever writing or editing TypeScript, without waiting for
|
|
18
|
+
the user to ask for "strict mode" explicitly. If a rule would need to be broken (e.g. a
|
|
19
|
+
third-party type is genuinely untyped), say so explicitly and isolate the escape hatch
|
|
20
|
+
rather than letting it leak.
|
|
21
|
+
|
|
22
|
+
## Core principle
|
|
23
|
+
|
|
24
|
+
> Narrow, don't cast. Model states, don't flag them. Let the compiler do the checking.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Never use `any`
|
|
29
|
+
|
|
30
|
+
`any` is not "unknown type," it's "type checking off." It's contagious — once a value is
|
|
31
|
+
`any`, everything it touches becomes unchecked too.
|
|
32
|
+
|
|
33
|
+
- Never write `any` for parameters, return types, variables, or generics.
|
|
34
|
+
- Use `unknown` for genuinely unknown external data (API responses, `JSON.parse`, catch
|
|
35
|
+
clauses, third-party callbacks) and narrow it before use.
|
|
36
|
+
- Use generics (`<T>`) when a function needs to work across types but preserve the
|
|
37
|
+
relationship between input and output.
|
|
38
|
+
- If a library ships untyped, write a minimal local type/interface for the surface area
|
|
39
|
+
you actually use instead of reaching for `any`.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// ❌ Bad
|
|
43
|
+
function parseConfig(json: any) {
|
|
44
|
+
return json.settings.theme; // no safety, no autocomplete, silent runtime crash
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ✅ Good
|
|
48
|
+
function parseConfig(json: unknown): string {
|
|
49
|
+
if (
|
|
50
|
+
typeof json === 'object' &&
|
|
51
|
+
json !== null &&
|
|
52
|
+
'settings' in json &&
|
|
53
|
+
typeof (json as { settings: unknown }).settings === 'object'
|
|
54
|
+
) {
|
|
55
|
+
// still narrow further or validate with a schema library (zod, valibot, etc.)
|
|
56
|
+
}
|
|
57
|
+
throw new Error('Invalid config shape');
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The only acceptable `any` is a well-justified, isolated, and commented one (e.g.
|
|
62
|
+
interfacing with a genuinely untyped legacy module) — never a default.
|
|
63
|
+
|
|
64
|
+
## 2. `unknown` + narrowing, not casting
|
|
65
|
+
|
|
66
|
+
Prefer proving a type through control flow over asserting it with `as`.
|
|
67
|
+
|
|
68
|
+
**Narrowing techniques, in order of preference:**
|
|
69
|
+
|
|
70
|
+
1. **`typeof`** — primitives (`string`, `number`, `boolean`, `undefined`, `function`)
|
|
71
|
+
2. **`instanceof`** — class instances, `Error`, `Date`, custom classes
|
|
72
|
+
3. **`in`** — checking a property exists before accessing it on a union/unknown
|
|
73
|
+
4. **User-defined type guards** — `function isUser(x: unknown): x is User`
|
|
74
|
+
5. **Discriminated union tag checks** — `switch (value.kind) { ... }` (see §3)
|
|
75
|
+
6. **Exhaustiveness checks** — a `never`-typed default branch so adding a new variant is
|
|
76
|
+
a compile error until every switch/if-chain handles it
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// ✅ Type guard
|
|
80
|
+
function isUser(value: unknown): value is User {
|
|
81
|
+
return typeof value === 'object' && value !== null && 'id' in value && 'email' in value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ✅ Exhaustiveness check
|
|
85
|
+
function assertNever(x: never): never {
|
|
86
|
+
throw new Error(`Unhandled case: ${JSON.stringify(x)}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function area(shape: Shape): number {
|
|
90
|
+
switch (shape.kind) {
|
|
91
|
+
case 'circle':
|
|
92
|
+
return Math.PI * shape.radius ** 2;
|
|
93
|
+
case 'square':
|
|
94
|
+
return shape.side ** 2;
|
|
95
|
+
default:
|
|
96
|
+
return assertNever(shape); // compile error if a variant is missed
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Type assertions (`as X`) and the non-null assertion (`!`) bypass this entirely — treat
|
|
102
|
+
them as a last resort (see §7), not a shortcut.
|
|
103
|
+
|
|
104
|
+
## 3. Discriminated unions for variant state
|
|
105
|
+
|
|
106
|
+
Whenever a value can be one of several distinct "shapes" (loading/success/error states,
|
|
107
|
+
event types, API response variants), model it as a **discriminated union** with a
|
|
108
|
+
literal tag field — never as a loose object with optional fields or boolean flags.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// ❌ Bad — booleans can contradict each other; unclear which fields are valid together
|
|
112
|
+
interface FetchState {
|
|
113
|
+
isLoading: boolean;
|
|
114
|
+
isError: boolean;
|
|
115
|
+
data?: User;
|
|
116
|
+
error?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ✅ Good — only one shape is possible at a time, and the compiler enforces it
|
|
120
|
+
type FetchState =
|
|
121
|
+
| { status: 'idle' }
|
|
122
|
+
| { status: 'loading' }
|
|
123
|
+
| { status: 'success'; data: User }
|
|
124
|
+
| { status: 'error'; error: string };
|
|
125
|
+
|
|
126
|
+
function render(state: FetchState) {
|
|
127
|
+
switch (state.status) {
|
|
128
|
+
case 'success':
|
|
129
|
+
return state.data.name; // `data` is guaranteed to exist here
|
|
130
|
+
case 'error':
|
|
131
|
+
return state.error; // `error` is guaranteed to exist here
|
|
132
|
+
default:
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use a consistent tag field name across a codebase (`kind`, `type`, or `status` — pick
|
|
139
|
+
one and stick with it) so narrowing patterns stay predictable.
|
|
140
|
+
|
|
141
|
+
## 4. `interface` vs `type` — pick by intent, not habit
|
|
142
|
+
|
|
143
|
+
Both can describe object shapes, but they signal different intent. Default rule:
|
|
144
|
+
|
|
145
|
+
| Use `interface` for... | Use `type` for... |
|
|
146
|
+
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
147
|
+
| Object / entity shapes (a `User`, a `Product`, a component's `Props`) | Unions (`"a" \| "b"`) and discriminated unions |
|
|
148
|
+
| Public API contracts meant to be `implements`-ed by classes | Intersections (`A & B`) |
|
|
149
|
+
| Shapes that consumers may want to **extend/augment** (declaration merging) | Tuples (`[string, number]`) |
|
|
150
|
+
| | Function types / callback signatures |
|
|
151
|
+
| | Mapped, conditional, or utility-derived types (`Partial<T>`, `Pick<T, K>`) |
|
|
152
|
+
| | Aliasing a primitive or another type for readability |
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// ✅ interface — an entity with identity, extendable
|
|
156
|
+
interface User {
|
|
157
|
+
id: string;
|
|
158
|
+
email: string;
|
|
159
|
+
role: UserRole;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface AdminUser extends User {
|
|
163
|
+
permissions: Permission[];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ✅ type — union, alias, derived shape
|
|
167
|
+
type UserRole = 'admin' | 'editor' | 'viewer';
|
|
168
|
+
type UserId = User['id'];
|
|
169
|
+
type PartialUser = Partial<User>;
|
|
170
|
+
type Callback<T> = (value: T) => void;
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Don't mix conventions arbitrarily within one file — if a shape is a plain data object
|
|
174
|
+
that will never need a union/intersection, `interface` is the default; the moment it
|
|
175
|
+
needs to express "one of several shapes," reach for `type`.
|
|
176
|
+
|
|
177
|
+
## 5. Naming conventions
|
|
178
|
+
|
|
179
|
+
| Kind | Convention | Example |
|
|
180
|
+
| ---------------------------------------------------------- | ------------------------------------------- | ----------------------------------- |
|
|
181
|
+
| Types, interfaces, classes, enums | `PascalCase` | `UserProfile`, `OrderStatus` |
|
|
182
|
+
| Interfaces | `PascalCase`, **no `I` prefix** | `User`, not `IUser` |
|
|
183
|
+
| Type aliases | `PascalCase` | `type ApiResponse<T> = ...` |
|
|
184
|
+
| Variables, functions, methods, properties | `camelCase` | `getUserById`, `isValid` |
|
|
185
|
+
| Booleans | `camelCase` with `is/has/should/can` prefix | `isLoading`, `hasPermission` |
|
|
186
|
+
| True constants (module-level, never reassigned, primitive) | `UPPER_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_TIMEOUT_MS` |
|
|
187
|
+
| Enum members | `PascalCase` | `enum Status { Active, Archived }` |
|
|
188
|
+
| Generic type parameters (simple, single-purpose) | Single uppercase letter | `T`, `K`, `V`, `E` for errors |
|
|
189
|
+
| Generic type parameters (multiple / non-obvious) | Descriptive, prefixed with `T` | `TInput`, `TOutput`, `TContext` |
|
|
190
|
+
| Discriminated union tag field | Consistent across the codebase | `kind`, `type`, or `status` |
|
|
191
|
+
| Files with a single exported entity | Match the entity name | `UserProfile.ts`, `useAuth.ts` |
|
|
192
|
+
|
|
193
|
+
Naming should describe **intent**, not implementation — `fetchUser` not
|
|
194
|
+
`getUserFromApiEndpoint`; `retryCount` not `numRetries2`.
|
|
195
|
+
|
|
196
|
+
## 6. Baseline `tsconfig.json` strictness
|
|
197
|
+
|
|
198
|
+
Treat these as the non-negotiable floor for any project this skill touches:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"compilerOptions": {
|
|
203
|
+
"strict": true,
|
|
204
|
+
"noImplicitAny": true,
|
|
205
|
+
"strictNullChecks": true,
|
|
206
|
+
"strictFunctionTypes": true,
|
|
207
|
+
"strictPropertyInitialization": true,
|
|
208
|
+
"noUncheckedIndexedAccess": true,
|
|
209
|
+
"exactOptionalPropertyTypes": true,
|
|
210
|
+
"noImplicitOverride": true,
|
|
211
|
+
"noFallthroughCasesInSwitch": true,
|
|
212
|
+
"noUnusedLocals": true,
|
|
213
|
+
"noUnusedParameters": true,
|
|
214
|
+
"forceConsistentCasingInFileNames": true
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`strict: true` alone enables the core group (`noImplicitAny`, `strictNullChecks`, etc.),
|
|
220
|
+
but `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes` are commonly missed and
|
|
221
|
+
close real gaps (array/object index access returning `T` instead of `T | undefined`;
|
|
222
|
+
optional properties silently accepting `undefined` as an explicit value).
|
|
223
|
+
|
|
224
|
+
## 7. Type assertions and non-null assertions are a last resort
|
|
225
|
+
|
|
226
|
+
- `as X` and `x!` tell the compiler "trust me" — they produce zero runtime safety and
|
|
227
|
+
actively hide bugs if wrong.
|
|
228
|
+
- Acceptable only when the compiler genuinely cannot know something you do (e.g. a DOM
|
|
229
|
+
query you've already null-checked, or narrowing a third-party type at a well-tested
|
|
230
|
+
boundary) — and even then, prefer a type guard or a runtime check over a bare
|
|
231
|
+
assertion.
|
|
232
|
+
- Never use `as any` or `as unknown as X` to force an incompatible cast — that's `any`
|
|
233
|
+
wearing a disguise.
|
|
234
|
+
- `x!` should almost always be replaceable by an actual null check or optional chaining
|
|
235
|
+
(`x?.y`) plus a real fallback.
|
|
236
|
+
|
|
237
|
+
## 8. Readonly by default
|
|
238
|
+
|
|
239
|
+
Prefer immutable shapes unless mutation is intentional and localized.
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
interface Point {
|
|
243
|
+
readonly x: number;
|
|
244
|
+
readonly y: number;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function config(values: readonly string[]) {
|
|
248
|
+
/* ... */
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const ROLES = ['admin', 'editor', 'viewer'] as const;
|
|
252
|
+
type UserRole = (typeof ROLES)[number];
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## 9. Explicit return types on exported/public functions
|
|
256
|
+
|
|
257
|
+
Inference is fine for local, private helpers, but exported functions, class methods, and
|
|
258
|
+
anything forming a public API should declare an explicit return type. This prevents an
|
|
259
|
+
internal implementation change from silently widening/narrowing the public contract.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
// ❌ Return type is inferred and can silently drift
|
|
263
|
+
export function getActiveUsers(users: User[]) {
|
|
264
|
+
return users.filter((u) => u.active);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ✅ Explicit, intentional contract
|
|
268
|
+
export function getActiveUsers(users: User[]): User[] {
|
|
269
|
+
return users.filter((u) => u.active);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## 10. Prefer literal unions over numeric enums
|
|
274
|
+
|
|
275
|
+
String literal unions are simpler, tree-shake better, and produce clearer error
|
|
276
|
+
messages than TypeScript `enum`. Reserve `enum` (or `as const` object maps) for cases
|
|
277
|
+
that need reverse lookup or genuinely benefit from a namespaced runtime value.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
// ✅ Preferred
|
|
281
|
+
type OrderStatus = 'pending' | 'shipped' | 'delivered' | 'cancelled';
|
|
282
|
+
|
|
283
|
+
// Acceptable when a namespaced runtime object is actually needed
|
|
284
|
+
const OrderStatus = {
|
|
285
|
+
Pending: 'pending',
|
|
286
|
+
Shipped: 'shipped',
|
|
287
|
+
} as const;
|
|
288
|
+
type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Review checklist
|
|
294
|
+
|
|
295
|
+
Before considering TypeScript code "done," verify:
|
|
296
|
+
|
|
297
|
+
- [ ] No `any` anywhere (including implicit `any` from missing annotations)
|
|
298
|
+
- [ ] External/uncertain data enters as `unknown` and is narrowed before use
|
|
299
|
+
- [ ] Variant state is a discriminated union, not optional fields + booleans
|
|
300
|
+
- [ ] `interface` used for object/entity shapes; `type` used for unions/aliases/intersections
|
|
301
|
+
- [ ] No stray `I` prefixes on interfaces
|
|
302
|
+
- [ ] Naming follows the casing table in §5 consistently
|
|
303
|
+
- [ ] `as` / `!` are rare, justified, and can't be replaced by a guard or null check
|
|
304
|
+
- [ ] Exported functions/methods have explicit return types
|
|
305
|
+
- [ ] Switch statements over unions have an exhaustiveness (`never`) check
|
|
306
|
+
- [ ] `tsconfig.json` includes the strictness baseline in §6
|
|
307
|
+
|
|
308
|
+
## Quick reference
|
|
309
|
+
|
|
310
|
+
| Situation | Use |
|
|
311
|
+
| ---------------------------------------------- | ------------------------------------------------------------- |
|
|
312
|
+
| External/uncertain data | `unknown` + narrowing |
|
|
313
|
+
| "This value is definitely one of these shapes" | Discriminated union (`type`) |
|
|
314
|
+
| Object with identity, may be extended | `interface` |
|
|
315
|
+
| Union, intersection, tuple, mapped type | `type` |
|
|
316
|
+
| Need to prove a type through logic | Type guard / narrowing |
|
|
317
|
+
| Tempted to write `any` | Stop — use `unknown`, a generic, or a local interface instead |
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wangs-ui-components
|
|
3
|
+
description: Foundational rules, subpath imports, design tokens, and the MCP Discovery Protocol for building React apps with Wangs UI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Skill: Wangs UI Component Fundamentals & MCP Protocol
|
|
7
|
+
|
|
8
|
+
Use this skill whenever you write or modify UI components using Wangs UI (`@wangs-ui/react-core`, `@wangs-ui/react-icons`, `@wangs-ui/react-presets`, `@wangs-ui/foundation`).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. The MCP Discovery Protocol (Mandatory Single Source of Truth)
|
|
13
|
+
|
|
14
|
+
Do **NOT** guess component props, Pass-Through (`pt`) slots, or event names. Always query the MCP server dynamically to retrieve the current API signatures and live story implementations:
|
|
15
|
+
|
|
16
|
+
```mermaid
|
|
17
|
+
graph TD
|
|
18
|
+
A[Identify Component Needed] --> B[Call get-documentation id]
|
|
19
|
+
B --> C{Need live story / variant code?}
|
|
20
|
+
C -->|Yes| D[Call get-documentation-for-story]
|
|
21
|
+
C -->|No| E[Check Graphify: query_graph]
|
|
22
|
+
D --> E
|
|
23
|
+
E --> F[Implement Component with Subpath Imports]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Discovery Steps:
|
|
27
|
+
|
|
28
|
+
1. **Inspect Component Contract & Props**:
|
|
29
|
+
```json
|
|
30
|
+
get-documentation({ "id": "button" })
|
|
31
|
+
get-documentation({ "id": "input" })
|
|
32
|
+
get-documentation({ "id": "datatable" })
|
|
33
|
+
```
|
|
34
|
+
2. **Inspect Live Usage & Story Variants**:
|
|
35
|
+
```json
|
|
36
|
+
get-documentation-for-story({ "id": "button", "storyName": "Default" })
|
|
37
|
+
get-documentation-for-story({ "id": "datatable", "storyName": "ServerPagination" })
|
|
38
|
+
```
|
|
39
|
+
3. **Inspect Relationships & Real Usages in Graph**:
|
|
40
|
+
```json
|
|
41
|
+
query_graph({ "query": "DataTable" })
|
|
42
|
+
query_graph({ "query": "usePT" })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. Subpath Modular Imports (Mandatory)
|
|
48
|
+
|
|
49
|
+
Always import via specific subpaths to guarantee tree-shaking and avoid bundling entire packages:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
// Primitives (@wangs-ui/react-core/primitive/*)
|
|
53
|
+
import Button from '@wangs-ui/react-core/primitive/button';
|
|
54
|
+
import Input from '@wangs-ui/react-core/primitive/input';
|
|
55
|
+
import NumberInput from '@wangs-ui/react-core/primitive/numberinput';
|
|
56
|
+
import Select from '@wangs-ui/react-core/primitive/select';
|
|
57
|
+
import Badge from '@wangs-ui/react-core/primitive/badge';
|
|
58
|
+
import Card from '@wangs-ui/react-core/primitive/card';
|
|
59
|
+
import DataTable from '@wangs-ui/react-core/primitive/datatable';
|
|
60
|
+
|
|
61
|
+
// Blocks (@wangs-ui/react-core/blocks/*)
|
|
62
|
+
import AppLayout from '@wangs-ui/react-core/blocks/applayout';
|
|
63
|
+
import Sidebar from '@wangs-ui/react-core/blocks/sidebar';
|
|
64
|
+
|
|
65
|
+
// Providers & System Hooks
|
|
66
|
+
import { WangsUiProvider } from '@wangs-ui/react-core/api';
|
|
67
|
+
import { useI18n } from '@wangs-ui/react-i18n';
|
|
68
|
+
import { useTheme } from '@wangs-ui/foundation/theme';
|
|
69
|
+
|
|
70
|
+
// Icons (@wangs-ui/react-icons)
|
|
71
|
+
import { SearchLine, AddLine, DeleteBin6Line, CheckLine } from '@wangs-ui/react-icons';
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 3. Strict Primitive Substitution Rule
|
|
77
|
+
|
|
78
|
+
Never write raw HTML elements when a Wangs UI primitive exists:
|
|
79
|
+
|
|
80
|
+
| Forbidden Raw HTML | Mandatory Wangs UI Component | Subpath Import | MCP Documentation ID |
|
|
81
|
+
| :------------------------ | :--------------------------- | :------------------------------------------- | :------------------- |
|
|
82
|
+
| `<button>` | `Button` | `@wangs-ui/react-core/primitive/button` | `button` |
|
|
83
|
+
| `<input type="text">` | `Input` | `@wangs-ui/react-core/primitive/input` | `input` |
|
|
84
|
+
| `<input type="number">` | `NumberInput` | `@wangs-ui/react-core/primitive/numberinput` | `numberinput` |
|
|
85
|
+
| `<input type="checkbox">` | `Checkbox` | `@wangs-ui/react-core/primitive/checkbox` | `checkbox` |
|
|
86
|
+
| `<select>` | `Select` | `@wangs-ui/react-core/primitive/select` | `select` |
|
|
87
|
+
| `<dialog>` / modal | `Dialog` / `Modal` | `@wangs-ui/react-core/primitive/dialog` | `dialog`, `modal` |
|
|
88
|
+
| `<table>` | `DataTable` | `@wangs-ui/react-core/primitive/datatable` | `datatable` |
|
|
89
|
+
| Container box | `Card` | `@wangs-ui/react-core/primitive/card` | `card` |
|
|
90
|
+
| Pill badge / status | `Badge` | `@wangs-ui/react-core/primitive/badge` | `badge` |
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 4. Typography Scale & 4px Spacing Tokens
|
|
95
|
+
|
|
96
|
+
### Typography Helper Classes
|
|
97
|
+
|
|
98
|
+
- `.heading-1` — Page title (22px, 600)
|
|
99
|
+
- `.heading-2` — Section / Card title (18px, 600)
|
|
100
|
+
- `.heading-3` — Sub-header (16px, 500)
|
|
101
|
+
- `.heading-4` — Field label (14px, 500)
|
|
102
|
+
- `.heading-5` — Small group header (12px, 600)
|
|
103
|
+
- `.p` — Body copy (12px, 500)
|
|
104
|
+
|
|
105
|
+
### 4px Spacing Tokens
|
|
106
|
+
|
|
107
|
+
- Gap: `gap-xs` (4px), `gap-s` (6px), `gap-md` (8px), `gap-m` (12px), `gap-l` (16px), `gap-xl` (20px), `gap-xxl` (24px)
|
|
108
|
+
- Padding: `p-xs`, `p-s`, `p-md`, `p-m`, `p-l`, `p-xl`, `p-xxl`
|