claude-toolkit 0.16.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/CHANGELOG.md +20 -0
- package/README.md +54 -13
- package/bin/cli.ts +75 -19
- package/package.json +6 -7
- package/src/generator.ts +64 -26
- package/src/refresh-hint.ts +95 -0
- package/bin/postinstall.mjs +0 -69
- package/docs/README.md +0 -59
- package/docs/agents/code-reviewer.md +0 -76
- package/docs/agents/github-workflow.md +0 -98
- package/docs/best-practices/capacitor/README.md +0 -64
- package/docs/best-practices/capacitor/capacitor-8.md +0 -60
- package/docs/best-practices/capacitor/capgo-setup.md +0 -151
- package/docs/best-practices/capacitor/channels-and-rollouts.md +0 -101
- package/docs/best-practices/capacitor/live-updates-ota.md +0 -75
- package/docs/best-practices/capacitor/security-encryption.md +0 -68
- package/docs/best-practices/solidjs/README.md +0 -43
- package/docs/best-practices/solidjs/anti-patterns.md +0 -166
- package/docs/best-practices/solidjs/component-patterns.md +0 -131
- package/docs/best-practices/solidjs/context-and-global-state.md +0 -131
- package/docs/best-practices/solidjs/control-flow.md +0 -124
- package/docs/best-practices/solidjs/data-fetching.md +0 -205
- package/docs/best-practices/solidjs/effects-and-lifecycle.md +0 -113
- package/docs/best-practices/solidjs/performance.md +0 -100
- package/docs/best-practices/solidjs/props-patterns.md +0 -100
- package/docs/best-practices/solidjs/reactivity-model.md +0 -104
- package/docs/best-practices/solidjs/signals-and-state.md +0 -78
- package/docs/best-practices/solidjs/stores-and-nested-state.md +0 -111
- package/docs/best-practices/solidjs/typescript-integration.md +0 -186
- package/docs/best-practices/testing/README.md +0 -84
- package/docs/best-practices/testing/playwright-e2e.md +0 -649
- package/docs/best-practices/testing/storybook-interaction.md +0 -455
- package/docs/best-practices/testing/vitest-unit.md +0 -451
- package/docs/best-practices/typescript/README.md +0 -45
- package/docs/best-practices/typescript/any-and-unknown.md +0 -73
- package/docs/best-practices/typescript/deriving-vs-decoupling.md +0 -83
- package/docs/best-practices/typescript/discriminated-unions.md +0 -75
- package/docs/best-practices/typescript/enums-alternatives.md +0 -72
- package/docs/best-practices/typescript/essential-patterns.md +0 -119
- package/docs/best-practices/typescript/generics-patterns.md +0 -105
- package/docs/best-practices/typescript/micro-opinions.md +0 -87
- package/docs/best-practices/typescript/runtime-validation.md +0 -62
- package/docs/best-practices/typescript/satisfies-operator.md +0 -100
- package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +0 -160
- package/docs/best-practices/typescript/type-organization.md +0 -64
- package/docs/best-practices/typescript/type-vs-interface.md +0 -80
- package/docs/commands/code-quality.md +0 -36
- package/docs/commands/onboard.md +0 -63
- package/docs/commands/pr-review.md +0 -91
- package/docs/commands/pr-summary.md +0 -50
- package/docs/commands/proto-check.md +0 -59
- package/docs/commands/ticket.md +0 -56
- package/docs/skills/code-style.md +0 -26
- package/docs/skills/systematic-debugging.md +0 -70
- package/docs/skills/testing-patterns.md +0 -140
- package/docs/skills/typescript-conventions.md +0 -283
- package/docs/skills/verification-before-completion.md +0 -103
- package/docs/stacks/capacitor-ota.md +0 -145
- package/docs/stacks/cloudflare-d1-kv.md +0 -144
- package/docs/stacks/esnext-idioms.md +0 -25
- package/docs/stacks/i18n-typesafe.md +0 -141
- package/docs/stacks/playwright-patterns.md +0 -179
- package/docs/stacks/protobuf-contracts.md +0 -85
- package/docs/stacks/rust-wasm-patterns.md +0 -106
- package/docs/stacks/solidjs-patterns.md +0 -110
- package/docs/stacks/storybook-patterns.md +0 -177
- package/docs/stacks/vanilla-extract-patterns.md +0 -115
- package/docs/stacks/vite-vitest-patterns.md +0 -485
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Protobuf Contracts
|
|
2
|
-
|
|
3
|
-
> Protocol Buffer definitions and code generation for frontend/backend contracts.
|
|
4
|
-
|
|
5
|
-
**Type:** Stack Skill (requires `protobuf` stack)
|
|
6
|
-
**Source:** [`stacks/protobuf/skills/ct-protobuf-contracts/SKILL.md`](../stacks/protobuf/skills/ct-protobuf-contracts/SKILL.md)
|
|
7
|
-
**Directory Mappings:** `proto/`
|
|
8
|
-
**File Extensions:** `.proto`
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Protocol Buffers (proto3) define the API contract between frontend and backend. Protobuf provides compact binary serialization, type safety across languages, and backward-compatible schema evolution.
|
|
13
|
-
|
|
14
|
-
## Proto3 Conventions
|
|
15
|
-
|
|
16
|
-
- Use **PascalCase** for message names, **snake_case** for field names
|
|
17
|
-
- Fields 1-15 use 1 byte (reserve for frequent fields), 16-2047 use 2 bytes
|
|
18
|
-
- **Never reuse** a field number after removing a field
|
|
19
|
-
- When removing fields, reserve the number AND name
|
|
20
|
-
- Enums must always start with `UNSPECIFIED = 0`
|
|
21
|
-
|
|
22
|
-
## Message Patterns
|
|
23
|
-
|
|
24
|
-
### Request/Response Pairs
|
|
25
|
-
```protobuf
|
|
26
|
-
message GetUserRequest { string user_id = 1; }
|
|
27
|
-
message GetUserResponse { UserProfile user = 1; }
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Lists with Pagination
|
|
31
|
-
```protobuf
|
|
32
|
-
message ListEventsRequest {
|
|
33
|
-
int32 page_size = 1;
|
|
34
|
-
string page_token = 2;
|
|
35
|
-
}
|
|
36
|
-
message ListEventsResponse {
|
|
37
|
-
repeated Event events = 1;
|
|
38
|
-
string next_page_token = 2;
|
|
39
|
-
int32 total_count = 3;
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Service Definitions
|
|
44
|
-
```protobuf
|
|
45
|
-
service UserService {
|
|
46
|
-
rpc GetUser(GetUserRequest) returns (GetUserResponse);
|
|
47
|
-
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
|
|
48
|
-
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Code Generation
|
|
53
|
-
|
|
54
|
-
| Target | Tool | Command |
|
|
55
|
-
|---|---|---|
|
|
56
|
-
| Frontend (TypeScript) | protoc-gen-ts | `buf generate --template buf.gen.ts.yaml` |
|
|
57
|
-
| Backend (Rust) | prost-build | Via `build.rs` with `prost_build::Config` |
|
|
58
|
-
|
|
59
|
-
## Buf CLI Commands
|
|
60
|
-
|
|
61
|
-
| Command | Purpose |
|
|
62
|
-
|---|---|
|
|
63
|
-
| `buf lint` | Lint proto files |
|
|
64
|
-
| `buf format -w` | Format in place |
|
|
65
|
-
| `buf breaking --against .git#branch=main` | Check for breaking changes |
|
|
66
|
-
| `buf generate` | Run code generation |
|
|
67
|
-
|
|
68
|
-
## Versioning Rules
|
|
69
|
-
|
|
70
|
-
1. **Never remove or renumber fields** -- use `reserved` instead
|
|
71
|
-
2. **Never change a field type** -- add a new field with the correct type
|
|
72
|
-
3. **Enum values are forever** -- 0 must always be UNSPECIFIED
|
|
73
|
-
4. **Adding fields is safe** -- new fields get default values in old clients
|
|
74
|
-
5. **Run `buf breaking` before merging** -- catch breaking changes in CI
|
|
75
|
-
|
|
76
|
-
## Anti-patterns
|
|
77
|
-
|
|
78
|
-
| Anti-pattern | Why it's wrong |
|
|
79
|
-
|---|---|
|
|
80
|
-
| **Reusing field numbers** | Causes data corruption. Always reserve removed field numbers. |
|
|
81
|
-
| **Overly nested messages** | Deep nesting hurts readability and evolution. Flatten where practical. |
|
|
82
|
-
| **Missing UNSPECIFIED enum** | Proto3 requires 0 as default. Without UNSPECIFIED, 0 maps to a real value. |
|
|
83
|
-
| **Using proto2 syntax** | Always use proto3 for new projects. |
|
|
84
|
-
| **Skipping buf lint** | Inconsistent naming compounds over time. |
|
|
85
|
-
| **Large messages** | Protobuf is not designed for multi-MB payloads. Use streaming or chunking. |
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# Rust WASM Patterns
|
|
2
|
-
|
|
3
|
-
> Rust WASM patterns for Cloudflare Workers with worker-rs.
|
|
4
|
-
|
|
5
|
-
**Type:** Stack Skill (requires `rust-wasm` stack)
|
|
6
|
-
**Source:** [`stacks/rust-wasm/skills/ct-rust-wasm-patterns/SKILL.md`](../stacks/rust-wasm/skills/ct-rust-wasm-patterns/SKILL.md)
|
|
7
|
-
**Directory Mappings:** `worker/`, `worker/src/`
|
|
8
|
-
**File Extensions:** `.rs`, `.toml`
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Cloudflare Workers can run Rust compiled to WebAssembly via the `worker` crate (worker-rs). The target is `wasm32-unknown-unknown`. Workers handle HTTP requests at the edge with low latency.
|
|
13
|
-
|
|
14
|
-
## Project Structure
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
worker/
|
|
18
|
-
Cargo.toml
|
|
19
|
-
src/
|
|
20
|
-
lib.rs # Entry point with main router
|
|
21
|
-
routes/
|
|
22
|
-
models/
|
|
23
|
-
error.rs # Custom error types
|
|
24
|
-
wrangler.toml # Cloudflare config
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Key Patterns
|
|
28
|
-
|
|
29
|
-
### Request Routing
|
|
30
|
-
|
|
31
|
-
```rust
|
|
32
|
-
#[event(fetch)]
|
|
33
|
-
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
|
|
34
|
-
Router::new()
|
|
35
|
-
.get_async("/api/users/:id", get_user)
|
|
36
|
-
.post_async("/api/users", create_user)
|
|
37
|
-
.run(req, env)
|
|
38
|
-
.await
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Handler Pattern
|
|
43
|
-
|
|
44
|
-
```rust
|
|
45
|
-
async fn get_user(req: Request, ctx: RouteContext<()>) -> Result<Response> {
|
|
46
|
-
let id = ctx.param("id")
|
|
47
|
-
.ok_or_else(|| Error::RustError("Missing id parameter".into()))?;
|
|
48
|
-
let db = ctx.env.d1("DB")?;
|
|
49
|
-
// ...
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Error Handling
|
|
54
|
-
|
|
55
|
-
Define a custom `AppError` enum that converts to `worker::Error`:
|
|
56
|
-
|
|
57
|
-
```rust
|
|
58
|
-
pub enum AppError {
|
|
59
|
-
NotFound(String),
|
|
60
|
-
BadRequest(String),
|
|
61
|
-
Internal(String),
|
|
62
|
-
Unauthorized,
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
impl From<AppError> for WorkerError { ... }
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Env Bindings
|
|
69
|
-
|
|
70
|
-
Access Cloudflare services through the `Env` object:
|
|
71
|
-
|
|
72
|
-
| Binding | Access |
|
|
73
|
-
|---|---|
|
|
74
|
-
| D1 Database | `env.d1("DB")?` |
|
|
75
|
-
| KV Namespace | `env.kv("MY_KV")?` |
|
|
76
|
-
| R2 Bucket | `env.bucket("MY_BUCKET")?` |
|
|
77
|
-
| Secrets | `env.secret("API_KEY")?.to_string()` |
|
|
78
|
-
| Variables | `env.var("CONFIG_VAR")?.to_string()` |
|
|
79
|
-
|
|
80
|
-
### Serde for JSON
|
|
81
|
-
|
|
82
|
-
Use `#[derive(Serialize, Deserialize)]` for request/response types. Parse request bodies with `req.json().await?` and respond with `Response::from_json(&data)`.
|
|
83
|
-
|
|
84
|
-
## Cargo.toml Essentials
|
|
85
|
-
|
|
86
|
-
```toml
|
|
87
|
-
[lib]
|
|
88
|
-
crate-type = ["cdylib"]
|
|
89
|
-
|
|
90
|
-
[dependencies]
|
|
91
|
-
worker = "0.5"
|
|
92
|
-
serde = { version = "1", features = ["derive"] }
|
|
93
|
-
serde_json = "1"
|
|
94
|
-
console_error_panic_hook = "0.1"
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Anti-patterns
|
|
98
|
-
|
|
99
|
-
| Anti-pattern | Why it's wrong |
|
|
100
|
-
|---|---|
|
|
101
|
-
| **Panics in production** | Always use `Result`. `unwrap()` and `expect()` crash the worker. |
|
|
102
|
-
| **Blocking operations** | WASM is single-threaded. All I/O must be `async`. |
|
|
103
|
-
| **Large binary sizes** | Keep dependencies minimal. Target under 1MB compressed. |
|
|
104
|
-
| **Ignoring memory limits** | Workers have 128MB memory. Stream large files, don't buffer. |
|
|
105
|
-
| **Raw SQL string concatenation** | Always use prepared statements with `bind()`. |
|
|
106
|
-
| **Missing CORS headers** | Workers serving APIs must handle OPTIONS preflight requests. |
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# SolidJS Patterns
|
|
2
|
-
|
|
3
|
-
> SolidJS reactivity, signals, effects, and component patterns.
|
|
4
|
-
|
|
5
|
-
**Type:** Stack Skill (requires `solidjs` stack)
|
|
6
|
-
**Source:** [`stacks/solidjs/skills/ct-solidjs-patterns/SKILL.md`](../stacks/solidjs/skills/ct-solidjs-patterns/SKILL.md)
|
|
7
|
-
**Directory Mappings:** `src/components/`, `src/hooks/`, `src/pages/`
|
|
8
|
-
**File Extensions:** `.tsx`, `.jsx`
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
SolidJS uses **fine-grained reactivity** with no virtual DOM. Components run once; only the reactive expressions inside them re-execute when dependencies change. This is fundamentally different from React.
|
|
13
|
-
|
|
14
|
-
## Core Primitives
|
|
15
|
-
|
|
16
|
-
### createSignal
|
|
17
|
-
```tsx
|
|
18
|
-
const [count, setCount] = createSignal(0);
|
|
19
|
-
// Read: count() -- always call as function
|
|
20
|
-
// Write: setCount(1) or setCount(prev => prev + 1)
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### createEffect
|
|
24
|
-
Runs side effects when tracked dependencies change. Automatically tracks any signal read inside it.
|
|
25
|
-
```tsx
|
|
26
|
-
createEffect(() => {
|
|
27
|
-
console.log("Count changed:", count());
|
|
28
|
-
});
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### createMemo
|
|
32
|
-
Derived computation that caches its result. Only recalculates when dependencies change.
|
|
33
|
-
```tsx
|
|
34
|
-
const doubled = createMemo(() => count() * 2);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### createResource
|
|
38
|
-
Async data fetching tied to a signal source. Returns a resource with loading/error states.
|
|
39
|
-
```tsx
|
|
40
|
-
const [data] = createResource(userId, fetchUser);
|
|
41
|
-
// data(), data.loading, data.error
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Component Patterns
|
|
45
|
-
|
|
46
|
-
| Component | Purpose | Example |
|
|
47
|
-
|---|---|---|
|
|
48
|
-
| `<Show>` | Conditional rendering | `<Show when={isLoggedIn()} fallback={<Login />}>` |
|
|
49
|
-
| `<For>` | List rendering (keyed by reference) | `<For each={items()}>{(item) => ...}</For>` |
|
|
50
|
-
| `<Switch>/<Match>` | Multi-branch conditional | `<Match when={state() === "loading"}>` |
|
|
51
|
-
| `<ErrorBoundary>` | Error catching | `<ErrorBoundary fallback={(err) => ...}>` |
|
|
52
|
-
| `<Suspense>` | Async loading states | `<Suspense fallback={<Loading />}>` |
|
|
53
|
-
|
|
54
|
-
## Props Handling
|
|
55
|
-
|
|
56
|
-
**CRITICAL: Never destructure props.** Destructuring breaks reactivity.
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
// WRONG -- kills reactivity
|
|
60
|
-
const MyComponent = ({ name, count }) => { ... };
|
|
61
|
-
|
|
62
|
-
// CORRECT -- preserves reactivity
|
|
63
|
-
const MyComponent = (props) => {
|
|
64
|
-
return <div>{props.name}: {props.count}</div>;
|
|
65
|
-
};
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
- Use `mergeProps` for defaults: `const merged = mergeProps({ color: "blue" }, props)`
|
|
69
|
-
- Use `splitProps` to separate prop groups: `const [local, others] = splitProps(props, ["class", "style"])`
|
|
70
|
-
|
|
71
|
-
## Store Patterns
|
|
72
|
-
|
|
73
|
-
`createStore` provides deep reactivity for complex nested state with path-based updates:
|
|
74
|
-
|
|
75
|
-
```tsx
|
|
76
|
-
const [state, setState] = createStore({
|
|
77
|
-
user: { name: "Alice", settings: { theme: "dark" } }
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Granular updates
|
|
81
|
-
setState("user", "settings", "theme", "light");
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Anti-patterns
|
|
85
|
-
|
|
86
|
-
| Anti-pattern | Why it's wrong |
|
|
87
|
-
|---|---|
|
|
88
|
-
| **Destructuring props** | Breaks reactivity. Always use `props.x`. |
|
|
89
|
-
| **Unnecessary createEffect** | If you just need a derived value, use `createMemo`. |
|
|
90
|
-
| **Treating it like React** | Components do NOT re-run. Only reactive expressions update. |
|
|
91
|
-
| **Array index as key in For** | `<For>` is keyed by reference automatically. |
|
|
92
|
-
| **Reading signals outside reactive context** | Captures the value once at component top level. |
|
|
93
|
-
| **Forgetting to call signals** | `count` is a getter function, `count()` is the value. |
|
|
94
|
-
|
|
95
|
-
## Best Practices Reference
|
|
96
|
-
|
|
97
|
-
For deeper guidance on SolidJS patterns (sourced from official docs and Ryan Carniato):
|
|
98
|
-
|
|
99
|
-
| Topic | Guide |
|
|
100
|
-
|---|---|
|
|
101
|
-
| How fine-grained reactivity works internally | [Reactivity Model](../best-practices/solidjs/reactivity-model.md) |
|
|
102
|
-
| Signal vs memo, derived values, when to use what | [Signals & State](../best-practices/solidjs/signals-and-state.md) |
|
|
103
|
-
| `produce`, `reconcile`, path syntax for nested state | [Stores & Nested State](../best-practices/solidjs/stores-and-nested-state.md) |
|
|
104
|
-
| `mergeProps`, `splitProps`, `children` helper | [Props Patterns](../best-practices/solidjs/props-patterns.md) |
|
|
105
|
-
| When NOT to use effects, `onMount`, `onCleanup` | [Effects & Lifecycle](../best-practices/solidjs/effects-and-lifecycle.md) |
|
|
106
|
-
| `Show`, `For`, `Switch`/`Match`, `Suspense` details | [Control Flow](../best-practices/solidjs/control-flow.md) |
|
|
107
|
-
| `createResource`, `createAsync`, optimistic updates | [Data Fetching](../best-practices/solidjs/data-fetching.md) |
|
|
108
|
-
| Full catalog of common SolidJS mistakes | [Anti-Patterns](../best-practices/solidjs/anti-patterns.md) |
|
|
109
|
-
|
|
110
|
-
See the full collection: [SolidJS Best Practices](../best-practices/solidjs/README.md)
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
# Storybook Patterns
|
|
2
|
-
|
|
3
|
-
> Storybook interaction testing, CSF 3 stories, play functions, a11y, and visual regression for SolidJS components.
|
|
4
|
-
|
|
5
|
-
**Type:** Stack Skill (requires `storybook` stack)
|
|
6
|
-
**Source:** [`stacks/storybook/skills/ct-storybook-patterns/SKILL.md`](../../stacks/storybook/skills/ct-storybook-patterns/SKILL.md)
|
|
7
|
-
**Directory Mappings:** `.storybook/`, `src/**/*.stories.tsx`
|
|
8
|
-
**File Extensions:** `.stories.tsx`, `.stories.ts`
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Storybook is the middle layer of the testing pyramid: component interaction testing, visual regression, and accessibility auditing in a real browser.
|
|
13
|
-
|
|
14
|
-
## SolidJS Integration
|
|
15
|
-
|
|
16
|
-
Uses community packages: `storybook-solidjs` (renderer) + `storybook-solidjs-vite` (builder).
|
|
17
|
-
|
|
18
|
-
**Critical:** Always use `createJSXDecorator` for JSX decorators. Standard decorators cause duplicate DOM elements with SolidJS.
|
|
19
|
-
|
|
20
|
-
```tsx
|
|
21
|
-
import { createJSXDecorator } from "storybook-solidjs";
|
|
22
|
-
|
|
23
|
-
const ThemeDecorator = createJSXDecorator((Story) => (
|
|
24
|
-
<ThemeProvider><Story /></ThemeProvider>
|
|
25
|
-
));
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## CSF 3 Stories
|
|
29
|
-
|
|
30
|
-
Use CSF 3 (not CSF Factories -- React-only as of Storybook 10.3).
|
|
31
|
-
|
|
32
|
-
```tsx
|
|
33
|
-
import type { Meta, StoryObj } from "storybook-solidjs";
|
|
34
|
-
|
|
35
|
-
const meta = {
|
|
36
|
-
title: "Components/Button",
|
|
37
|
-
component: Button,
|
|
38
|
-
tags: ["autodocs"],
|
|
39
|
-
} satisfies Meta<typeof Button>;
|
|
40
|
-
|
|
41
|
-
export default meta;
|
|
42
|
-
type Story = StoryObj<typeof meta>;
|
|
43
|
-
|
|
44
|
-
export const Primary: Story = {
|
|
45
|
-
args: { variant: "primary", children: "Click me" },
|
|
46
|
-
};
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Coverage Checklist
|
|
50
|
-
|
|
51
|
-
Every component needs stories for: all visual states (default, hover, focus, active, disabled, loading, error, empty), viewport sizes where layout differs, theme variants, edge cases (long text, missing data).
|
|
52
|
-
|
|
53
|
-
## Interaction Testing
|
|
54
|
-
|
|
55
|
-
Import everything from `@storybook/test` (instrumented versions). Never import from `@testing-library/dom` directly.
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
import { expect, fn, userEvent, within } from "@storybook/test";
|
|
59
|
-
|
|
60
|
-
export const SubmitForm: Story = {
|
|
61
|
-
args: { onSubmit: fn() },
|
|
62
|
-
play: async ({ canvasElement, args, step }) => {
|
|
63
|
-
const canvas = within(canvasElement);
|
|
64
|
-
await step("Fill form", async () => {
|
|
65
|
-
await userEvent.type(canvas.getByLabelText("Email"), "user@example.com");
|
|
66
|
-
});
|
|
67
|
-
await step("Submit", async () => {
|
|
68
|
-
await userEvent.click(canvas.getByRole("button", { name: "Sign in" }));
|
|
69
|
-
});
|
|
70
|
-
await expect(args.onSubmit).toHaveBeenCalledOnce();
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
**Key rules:**
|
|
76
|
-
1. Always `await` expect calls (enables Interactions panel logging).
|
|
77
|
-
2. Use `step()` to organize complex interactions.
|
|
78
|
-
3. Use `fn()` for spying (auto-restored between stories).
|
|
79
|
-
|
|
80
|
-
## Running in CI (Vitest Addon)
|
|
81
|
-
|
|
82
|
-
Use `@storybook/addon-vitest` (replaces old test-runner). No running Storybook instance needed.
|
|
83
|
-
|
|
84
|
-
Required dependencies: `@storybook/addon-vitest`, `@vitest/browser`, `@vitest/browser-playwright`, `@vitest/coverage-v8`.
|
|
85
|
-
|
|
86
|
-
Add as an inline project in your main Vite config using `test.projects`:
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
// vite.config.ts
|
|
90
|
-
import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
|
|
91
|
-
import { playwright } from "@vitest/browser-playwright";
|
|
92
|
-
|
|
93
|
-
export default defineConfig({
|
|
94
|
-
plugins: [/* ...app plugins */],
|
|
95
|
-
test: {
|
|
96
|
-
projects: [
|
|
97
|
-
{
|
|
98
|
-
extends: true,
|
|
99
|
-
test: { name: "unit", environment: "jsdom", setupFiles: ["./tests/setup.ts"] },
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
extends: true,
|
|
103
|
-
plugins: [storybookTest({ configDir: ".storybook" })],
|
|
104
|
-
test: {
|
|
105
|
-
name: "storybook",
|
|
106
|
-
browser: { enabled: true, headless: true, provider: playwright(), instances: [{ browser: "chromium" }] },
|
|
107
|
-
setupFiles: [".storybook/vitest.setup.ts"],
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Accessibility
|
|
116
|
-
|
|
117
|
-
Built on axe-core. Set globally in `.storybook/preview.ts`:
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
export default {
|
|
121
|
-
parameters: { a11y: { test: "error" } }, // "error" | "todo" | "off"
|
|
122
|
-
tags: ["a11y-test"],
|
|
123
|
-
};
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## MSW for API Components
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// .storybook/preview.ts
|
|
130
|
-
import { initialize, mswLoader } from "msw-storybook-addon";
|
|
131
|
-
initialize();
|
|
132
|
-
export default { loaders: [mswLoader] };
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Per-story handlers via `parameters.msw.handlers`. Keep success handlers in shared `mocks/handlers.ts`, override with error/edge-case at story level.
|
|
136
|
-
|
|
137
|
-
## Module Mocking (Storybook 10)
|
|
138
|
-
|
|
139
|
-
`sb.mock` for internal module replacement. Register in `.storybook/preview.ts` only:
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { sb } from "@storybook/test";
|
|
143
|
-
sb.mock("../src/api/client", () => ({ fetchUser: async () => ({ name: "Mock" }) }));
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
MSW for network-level; `sb.mock` for internal modules.
|
|
147
|
-
|
|
148
|
-
## Portable Stories
|
|
149
|
-
|
|
150
|
-
Reuse stories in Vitest with `composeStories`:
|
|
151
|
-
|
|
152
|
-
```tsx
|
|
153
|
-
import { composeStories } from "storybook-solidjs";
|
|
154
|
-
import * as stories from "./Button.stories";
|
|
155
|
-
const { Primary } = composeStories(stories);
|
|
156
|
-
|
|
157
|
-
test("renders primary", () => {
|
|
158
|
-
const { getByRole } = render(() => <Primary />);
|
|
159
|
-
expect(getByRole("button")).toBeInTheDocument();
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Anti-Patterns
|
|
164
|
-
|
|
165
|
-
1. **Destructuring SolidJS props in stories** -- Pass props via `args`; use `createJSXDecorator` for decorators.
|
|
166
|
-
2. **Importing from `@testing-library/dom`** -- Use `@storybook/test` for instrumented versions.
|
|
167
|
-
3. **Not awaiting `expect()` in play functions** -- Always `await expect(...)`.
|
|
168
|
-
4. **Skipping error/edge-case stories** -- Always include loading, error, empty, boundary states.
|
|
169
|
-
5. **Using `@storybook/test-runner`** -- Use `@storybook/addon-vitest` instead.
|
|
170
|
-
6. **Registering `sb.mock` in story files** -- Register in `.storybook/preview.ts` only.
|
|
171
|
-
|
|
172
|
-
## See Also
|
|
173
|
-
|
|
174
|
-
- [ct-testing-patterns](../skills/testing-patterns.md) -- Framework-agnostic TDD practices
|
|
175
|
-
- [Storybook Interaction Best Practices](../best-practices/testing/storybook-interaction.md) -- SolidJS-specific Storybook patterns
|
|
176
|
-
- [ct-vite-vitest-patterns](vite-vitest-patterns.md) -- Unit testing layer
|
|
177
|
-
- [ct-playwright-patterns](playwright-patterns.md) -- E2E testing layer
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# Vanilla Extract Patterns
|
|
2
|
-
|
|
3
|
-
> Type-safe CSS with vanilla-extract, sprinkles, and recipes.
|
|
4
|
-
|
|
5
|
-
**Type:** Stack Skill (requires `vanilla-extract` stack)
|
|
6
|
-
**Source:** [`stacks/vanilla-extract/skills/ct-vanilla-extract-patterns/SKILL.md`](../stacks/vanilla-extract/skills/ct-vanilla-extract-patterns/SKILL.md)
|
|
7
|
-
**Directory Mappings:** `src/styles/`, `src/theme/`
|
|
8
|
-
**File Extensions:** `.css.ts`
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
vanilla-extract provides zero-runtime, type-safe CSS written in TypeScript. All styles are authored in `*.css.ts` files and extracted to static CSS at build time.
|
|
13
|
-
|
|
14
|
-
**Important:** All vanilla-extract files MUST use the `*.css.ts` extension. The build plugin only processes these files.
|
|
15
|
-
|
|
16
|
-
## Core API
|
|
17
|
-
|
|
18
|
-
### style() -- Single Class Styles
|
|
19
|
-
|
|
20
|
-
Creates a single scoped class name with type-checked CSS properties.
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
import { style } from '@vanilla-extract/css';
|
|
24
|
-
|
|
25
|
-
export const button = style({
|
|
26
|
-
padding: '8px 16px',
|
|
27
|
-
borderRadius: '4px',
|
|
28
|
-
':hover': { opacity: 0.9 },
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Compose styles by passing an array:
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
export const primaryButton = style([button, { background: 'blue', color: 'white' }]);
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### styleVariants() -- Variant Maps
|
|
39
|
-
|
|
40
|
-
Creates a map of related style variants from a single definition.
|
|
41
|
-
|
|
42
|
-
```ts
|
|
43
|
-
export const colorVariant = styleVariants({
|
|
44
|
-
primary: { background: 'blue', color: 'white' },
|
|
45
|
-
secondary: { background: 'gray', color: 'black' },
|
|
46
|
-
danger: { background: 'red', color: 'white' },
|
|
47
|
-
});
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### recipe() -- Component Variants
|
|
51
|
-
|
|
52
|
-
Creates multi-variant styles with `defaultVariants` and compound variants. Requires `@vanilla-extract/recipes`.
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
export const buttonRecipe = recipe({
|
|
56
|
-
base: { padding: '8px 16px', borderRadius: '4px' },
|
|
57
|
-
variants: {
|
|
58
|
-
color: {
|
|
59
|
-
primary: { background: 'blue', color: 'white' },
|
|
60
|
-
secondary: { background: 'gray', color: 'black' },
|
|
61
|
-
},
|
|
62
|
-
size: {
|
|
63
|
-
small: { fontSize: '12px' },
|
|
64
|
-
medium: { fontSize: '14px' },
|
|
65
|
-
large: { fontSize: '16px' },
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
defaultVariants: { color: 'primary', size: 'medium' },
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### globalStyle() -- Use Sparingly
|
|
73
|
-
|
|
74
|
-
For CSS resets, body defaults, or third-party element overrides only.
|
|
75
|
-
|
|
76
|
-
## Theming
|
|
77
|
-
|
|
78
|
-
### Contract-first theming (recommended)
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
import { createThemeContract, createTheme } from '@vanilla-extract/css';
|
|
82
|
-
|
|
83
|
-
export const vars = createThemeContract({
|
|
84
|
-
color: { brand: null, text: null, background: null },
|
|
85
|
-
space: { small: null, medium: null, large: null },
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
export const lightTheme = createTheme(vars, {
|
|
89
|
-
color: { brand: '#0066ff', text: '#1a1a1a', background: '#ffffff' },
|
|
90
|
-
space: { small: '4px', medium: '8px', large: '16px' },
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Use `vars` for type-safe token references: `style({ color: vars.color.brand })`
|
|
95
|
-
|
|
96
|
-
### Sprinkles -- Atomic Utility Classes
|
|
97
|
-
|
|
98
|
-
Requires `@vanilla-extract/sprinkles`. Creates constrained atomic CSS classes with responsive conditions.
|
|
99
|
-
|
|
100
|
-
## Anti-patterns
|
|
101
|
-
|
|
102
|
-
| Anti-pattern | Why it's wrong |
|
|
103
|
-
| ----------------------------- | ---------------------------------------------------------------- |
|
|
104
|
-
| **Inline styles** | Defeats the purpose. Use `style()` or `sprinkles()`. |
|
|
105
|
-
| **Runtime CSS** | vanilla-extract is build-time only. |
|
|
106
|
-
| **Overusing globalStyle** | Prefer scoped `style()`. Global styles create implicit coupling. |
|
|
107
|
-
| **Non-`.css.ts` files** | Style definitions in `.ts` files will not be processed. |
|
|
108
|
-
| **String-based theme values** | Use the `vars` object for type-safe references. |
|
|
109
|
-
|
|
110
|
-
## Best Practices Reference
|
|
111
|
-
|
|
112
|
-
| Topic | Guide |
|
|
113
|
-
| ----------------------------------------- | ---------------------------------------------------------------------------- |
|
|
114
|
-
| `type` vs `interface` for theme contracts | [Type vs Interface](../best-practices/typescript/type-vs-interface.md) |
|
|
115
|
-
| `satisfies` for config validation | [The satisfies Operator](../best-practices/typescript/satisfies-operator.md) |
|