@jgamaraalv/ts-dev-kit 1.0.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/.claude-plugin/marketplace.json +24 -0
- package/.claude-plugin/plugin.json +24 -0
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/agents/accessibility-pro.md +139 -0
- package/agents/api-builder.md +110 -0
- package/agents/code-reviewer.md +190 -0
- package/agents/database-expert.md +138 -0
- package/agents/debugger.md +241 -0
- package/agents/docker-expert.md +51 -0
- package/agents/multi-agent-coordinator.md +378 -0
- package/agents/nextjs-expert.md +136 -0
- package/agents/performance-engineer.md +138 -0
- package/agents/playwright-expert.md +126 -0
- package/agents/react-specialist.md +97 -0
- package/agents/security-scanner.md +105 -0
- package/agents/test-generator.md +221 -0
- package/agents/typescript-pro.md +253 -0
- package/agents/ux-optimizer.md +93 -0
- package/docs/rules/orchestration.md.template +126 -0
- package/package.json +28 -0
- package/skills/bullmq/SKILL.md +225 -0
- package/skills/bullmq/references/flows-and-schedulers.md +186 -0
- package/skills/bullmq/references/job-types-and-options.md +163 -0
- package/skills/bullmq/references/patterns.md +273 -0
- package/skills/bullmq/references/production.md +308 -0
- package/skills/composition-patterns/SKILL.md +58 -0
- package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
- package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
- package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
- package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
- package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
- package/skills/composition-patterns/references/state-context-interface.md +194 -0
- package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
- package/skills/composition-patterns/references/state-lift-state.md +126 -0
- package/skills/conventional-commits/SKILL.md +148 -0
- package/skills/docker/SKILL.md +55 -0
- package/skills/docker/references/compose-configs.md +95 -0
- package/skills/docker/references/monorepo-dockerfile.md +111 -0
- package/skills/drizzle-pg/SKILL.md +202 -0
- package/skills/drizzle-pg/references/advanced.md +299 -0
- package/skills/drizzle-pg/references/migrations.md +214 -0
- package/skills/drizzle-pg/references/queries.md +321 -0
- package/skills/drizzle-pg/references/relations.md +272 -0
- package/skills/drizzle-pg/references/schema-pg.md +256 -0
- package/skills/drizzle-pg/references/sql-operator.md +215 -0
- package/skills/fastify-best-practices/SKILL.md +143 -0
- package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
- package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
- package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
- package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
- package/skills/fastify-best-practices/references/server-and-options.md +127 -0
- package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
- package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
- package/skills/ioredis/SKILL.md +51 -0
- package/skills/ioredis/references/advanced-patterns.md +312 -0
- package/skills/ioredis/references/cluster-sentinel.md +280 -0
- package/skills/ioredis/references/connection-options.md +187 -0
- package/skills/ioredis/references/core-api.md +179 -0
- package/skills/nextjs-best-practices/SKILL.md +194 -0
- package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
- package/skills/nextjs-best-practices/references/bundling.md +192 -0
- package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
- package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
- package/skills/nextjs-best-practices/references/directives.md +74 -0
- package/skills/nextjs-best-practices/references/error-handling.md +237 -0
- package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
- package/skills/nextjs-best-practices/references/font.md +175 -0
- package/skills/nextjs-best-practices/references/functions.md +116 -0
- package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
- package/skills/nextjs-best-practices/references/image.md +184 -0
- package/skills/nextjs-best-practices/references/metadata.md +305 -0
- package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
- package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
- package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
- package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
- package/skills/nextjs-best-practices/references/scripts.md +148 -0
- package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
- package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
- package/skills/owasp-security-review/SKILL.md +98 -0
- package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
- package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
- package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
- package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
- package/skills/owasp-security-review/references/a05-injection.md +106 -0
- package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
- package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
- package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
- package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
- package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
- package/skills/postgresql/SKILL.md +50 -0
- package/skills/postgresql/references/ddl-schema.md +300 -0
- package/skills/postgresql/references/indexes.md +257 -0
- package/skills/postgresql/references/jsonb.md +261 -0
- package/skills/postgresql/references/performance.md +291 -0
- package/skills/postgresql/references/psql-cli.md +153 -0
- package/skills/postgresql/references/queries.md +287 -0
- package/skills/postgresql/references/transactions.md +280 -0
- package/skills/react-best-practices/SKILL.md +110 -0
- package/skills/react-best-practices/references/advanced-patterns.md +91 -0
- package/skills/react-best-practices/references/async-patterns.md +233 -0
- package/skills/react-best-practices/references/bundle-optimization.md +201 -0
- package/skills/react-best-practices/references/client-patterns.md +178 -0
- package/skills/react-best-practices/references/js-performance.md +210 -0
- package/skills/react-best-practices/references/rendering-performance.md +209 -0
- package/skills/react-best-practices/references/rerender-optimization.md +316 -0
- package/skills/react-best-practices/references/server-performance.md +274 -0
- package/skills/service-worker/SKILL.md +195 -0
- package/skills/service-worker/references/api-reference.md +114 -0
- package/skills/service-worker/references/caching-strategies.md +202 -0
- package/skills/service-worker/references/push-and-sync.md +261 -0
- package/skills/typescript-conventions/SKILL.md +51 -0
- package/skills/ui-ux-guidelines/SKILL.md +105 -0
- package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
- package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
- package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Push Notifications & Background Sync
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Push Notifications](#push-notifications)
|
|
6
|
+
- [Subscribe from the Page](#subscribe-from-the-page)
|
|
7
|
+
- [Send Push from Server](#send-push-from-server)
|
|
8
|
+
- [Handle Push in SW](#handle-push-in-sw)
|
|
9
|
+
- [Handle Notification Click](#handle-notification-click)
|
|
10
|
+
- [VAPID Keys](#vapid-keys)
|
|
11
|
+
- [Background Sync](#background-sync)
|
|
12
|
+
- [Register Sync from Page](#register-sync-from-page)
|
|
13
|
+
- [Handle Sync in SW](#handle-sync-in-sw)
|
|
14
|
+
- [Retry Pattern](#retry-pattern)
|
|
15
|
+
- [Periodic Background Sync](#periodic-background-sync)
|
|
16
|
+
|
|
17
|
+
## Push Notifications
|
|
18
|
+
|
|
19
|
+
### Subscribe from the Page
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
async function subscribeToPush() {
|
|
23
|
+
const reg = await navigator.serviceWorker.ready;
|
|
24
|
+
|
|
25
|
+
// Check permission
|
|
26
|
+
const permission = await Notification.requestPermission();
|
|
27
|
+
if (permission !== "granted") return null;
|
|
28
|
+
|
|
29
|
+
// Subscribe with VAPID public key
|
|
30
|
+
const subscription = await reg.pushManager.subscribe({
|
|
31
|
+
userVisibleOnly: true, // Required: must show a notification for each push
|
|
32
|
+
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Send subscription to your server
|
|
36
|
+
await fetch("/api/push/subscribe", {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
body: JSON.stringify(subscription),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return subscription;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Helper: convert VAPID key
|
|
46
|
+
function urlBase64ToUint8Array(base64String) {
|
|
47
|
+
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
|
|
48
|
+
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
49
|
+
const raw = atob(base64);
|
|
50
|
+
return Uint8Array.from([...raw].map((char) => char.charCodeAt(0)));
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Send Push from Server
|
|
55
|
+
|
|
56
|
+
Using the `web-push` npm package (Node.js):
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import webpush from "web-push";
|
|
60
|
+
|
|
61
|
+
webpush.setVapidDetails(
|
|
62
|
+
"mailto:admin@example.com",
|
|
63
|
+
process.env.VAPID_PUBLIC_KEY,
|
|
64
|
+
process.env.VAPID_PRIVATE_KEY,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// subscription = the PushSubscription JSON from the client
|
|
68
|
+
await webpush.sendNotification(
|
|
69
|
+
subscription,
|
|
70
|
+
JSON.stringify({
|
|
71
|
+
title: "New message",
|
|
72
|
+
body: "You have a new notification",
|
|
73
|
+
icon: "/icon-192.png",
|
|
74
|
+
url: "/messages",
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Generate VAPID keys: `npx web-push generate-vapid-keys`
|
|
80
|
+
|
|
81
|
+
### Handle Push in SW
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
self.addEventListener("push", (event) => {
|
|
85
|
+
const data = event.data?.json() ?? {};
|
|
86
|
+
const { title = "Notification", body, icon, url } = data;
|
|
87
|
+
|
|
88
|
+
event.waitUntil(
|
|
89
|
+
self.registration.showNotification(title, {
|
|
90
|
+
body,
|
|
91
|
+
icon: icon || "/icon-192.png",
|
|
92
|
+
badge: "/badge-72.png",
|
|
93
|
+
data: { url }, // Pass URL to notificationclick handler
|
|
94
|
+
vibrate: [100, 50, 100],
|
|
95
|
+
actions: [
|
|
96
|
+
{ action: "open", title: "Open" },
|
|
97
|
+
{ action: "dismiss", title: "Dismiss" },
|
|
98
|
+
],
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Handle Notification Click
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
self.addEventListener("notificationclick", (event) => {
|
|
108
|
+
event.notification.close();
|
|
109
|
+
|
|
110
|
+
if (event.action === "dismiss") return;
|
|
111
|
+
|
|
112
|
+
const url = event.notification.data?.url || "/";
|
|
113
|
+
|
|
114
|
+
event.waitUntil(
|
|
115
|
+
self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => {
|
|
116
|
+
// Focus existing tab if one matches
|
|
117
|
+
for (const client of clients) {
|
|
118
|
+
if (client.url === url && "focus" in client) {
|
|
119
|
+
return client.focus();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Otherwise open a new window
|
|
123
|
+
return self.clients.openWindow(url);
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
self.addEventListener("notificationclose", (event) => {
|
|
129
|
+
// Track dismissals (analytics)
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### VAPID Keys
|
|
134
|
+
|
|
135
|
+
VAPID (Voluntary Application Server Identification) authenticates your server to the push service.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Generate keys (do this once, store securely)
|
|
139
|
+
npx web-push generate-vapid-keys
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Output:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
Public Key: BNbxG... (use in client subscribe call)
|
|
146
|
+
Private Key: 3KW9e... (use on server only, keep secret)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Store as environment variables:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
VAPID_PUBLIC_KEY=BNbxG...
|
|
153
|
+
VAPID_PRIVATE_KEY=3KW9e...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Background Sync
|
|
157
|
+
|
|
158
|
+
Defers actions until the user has connectivity. Chromium-only (Chrome, Edge).
|
|
159
|
+
|
|
160
|
+
> **Safari/iOS caveat:** Background Sync is not supported in Safari/iOS. Use periodic polling as fallback.
|
|
161
|
+
|
|
162
|
+
### Register Sync from Page
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
async function saveForSync(data) {
|
|
166
|
+
// Store the pending action in IndexedDB
|
|
167
|
+
await saveToIndexedDB("pending-actions", data);
|
|
168
|
+
|
|
169
|
+
// Register a sync
|
|
170
|
+
const reg = await navigator.serviceWorker.ready;
|
|
171
|
+
await reg.sync.register("sync-pending-actions");
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Handle Sync in SW
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
self.addEventListener("sync", (event) => {
|
|
179
|
+
if (event.tag === "sync-pending-actions") {
|
|
180
|
+
event.waitUntil(processPendingActions());
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
async function processPendingActions() {
|
|
185
|
+
const actions = await getFromIndexedDB("pending-actions");
|
|
186
|
+
|
|
187
|
+
for (const action of actions) {
|
|
188
|
+
try {
|
|
189
|
+
await fetch("/api/action", {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: { "Content-Type": "application/json" },
|
|
192
|
+
body: JSON.stringify(action),
|
|
193
|
+
});
|
|
194
|
+
await removeFromIndexedDB("pending-actions", action.id);
|
|
195
|
+
} catch {
|
|
196
|
+
// Will retry on next sync event
|
|
197
|
+
throw new Error("Sync failed, will retry");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Retry Pattern
|
|
204
|
+
|
|
205
|
+
The browser retries `sync` events with exponential backoff when the promise passed to `waitUntil` rejects. The event includes `event.lastChance` — if `true`, this is the final retry:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
self.addEventListener("sync", (event) => {
|
|
209
|
+
if (event.tag === "sync-form-data") {
|
|
210
|
+
event.waitUntil(
|
|
211
|
+
submitFormData().catch((err) => {
|
|
212
|
+
if (event.lastChance) {
|
|
213
|
+
// Final attempt failed — notify user via notification
|
|
214
|
+
return self.registration.showNotification("Sync failed", {
|
|
215
|
+
body: "Your data could not be submitted. Please try again.",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
throw err; // Reject to trigger retry
|
|
219
|
+
}),
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Periodic Background Sync
|
|
226
|
+
|
|
227
|
+
Runs at periodic intervals even when the page is closed. Chromium-only, requires site engagement score.
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
// Register from page
|
|
231
|
+
const reg = await navigator.serviceWorker.ready;
|
|
232
|
+
const status = await navigator.permissions.query({ name: "periodic-background-sync" });
|
|
233
|
+
|
|
234
|
+
if (status.state === "granted") {
|
|
235
|
+
await reg.periodicSync.register("update-content", {
|
|
236
|
+
minInterval: 24 * 60 * 60 * 1000, // Once per day minimum
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Handle in SW
|
|
241
|
+
self.addEventListener("periodicsync", (event) => {
|
|
242
|
+
if (event.tag === "update-content") {
|
|
243
|
+
event.waitUntil(updateCachedContent());
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
async function updateCachedContent() {
|
|
248
|
+
const cache = await caches.open("content-v1");
|
|
249
|
+
const response = await fetch("/api/latest-content");
|
|
250
|
+
if (response.ok) {
|
|
251
|
+
await cache.put("/api/latest-content", response);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Notes:**
|
|
257
|
+
|
|
258
|
+
- `minInterval` is a hint — the browser decides actual frequency based on site engagement
|
|
259
|
+
- Requires a minimum site engagement score (user must visit regularly)
|
|
260
|
+
- Not supported in Firefox or Safari
|
|
261
|
+
- Always check for API availability before registering
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript-conventions
|
|
3
|
+
description: "TypeScript coding conventions for strict, type-safe projects. Use when: (1) writing or reviewing TypeScript code, (2) choosing between `any` and `unknown`, (3) structuring imports with verbatimModuleSyntax, (4) naming functions, booleans, queries, and commands, (5) handling errors with guard clauses and early returns, or (6) avoiding common anti-patterns like primitive obsession, magic strings, and premature abstraction."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript Conventions
|
|
7
|
+
|
|
8
|
+
Project-wide TypeScript standards that complement agent-specific instructions.
|
|
9
|
+
|
|
10
|
+
## Type Safety
|
|
11
|
+
|
|
12
|
+
- **No `any`**: Use `unknown` if the type is truly dynamic, then narrow.
|
|
13
|
+
- **Strict config**: `strict: true`, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`.
|
|
14
|
+
- Use `Readonly<T>`, `Pick`, `Omit`, and `Record` for precise types.
|
|
15
|
+
- Use branded types for entity IDs (e.g., `UserId`, `OrderId`) to prevent mixing.
|
|
16
|
+
- Prefer `z.infer<typeof schema>` over hand-written types when a Zod schema exists.
|
|
17
|
+
|
|
18
|
+
## Imports
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// Type-only imports (required by verbatimModuleSyntax)
|
|
22
|
+
import type { FastifyInstance } from "fastify";
|
|
23
|
+
|
|
24
|
+
// Mixed imports: separate values and types
|
|
25
|
+
import { z } from "zod/v4";
|
|
26
|
+
import type { ZodType } from "zod/v4";
|
|
27
|
+
|
|
28
|
+
// ioredis: always named import
|
|
29
|
+
import { Redis } from "ioredis";
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Error Handling
|
|
33
|
+
|
|
34
|
+
- Handle errors at the beginning of functions with early returns / guard clauses.
|
|
35
|
+
- Avoid deep nesting -- use if-return pattern instead of else chains.
|
|
36
|
+
- In Fastify routes, throw `httpErrors` or use `reply.status().send()` -- the centralized `setErrorHandler` formats the response.
|
|
37
|
+
- Custom error classes for domain-specific errors (e.g., `NotFoundError`, `ConflictError`).
|
|
38
|
+
|
|
39
|
+
## Naming
|
|
40
|
+
|
|
41
|
+
- **Functions**: `getUserById`, `createReport`, `isActive`, `hasPermission`
|
|
42
|
+
- **Booleans**: `is/has/can/should` prefix
|
|
43
|
+
- **Query** (returns data): `get`, `find`, `list`, `fetch`
|
|
44
|
+
- **Command** (changes state): `create`, `update`, `delete`, `add`, `remove`
|
|
45
|
+
|
|
46
|
+
## Anti-Patterns
|
|
47
|
+
|
|
48
|
+
- **Primitive obsession**: Use branded types or Zod enums, not raw strings for IDs and statuses.
|
|
49
|
+
- **Magic numbers/strings**: Use constants from a shared package (e.g., `RATE_LIMITS`, `PAGINATION`, `CACHE`).
|
|
50
|
+
- **Long parameter lists**: Use an options object or a Zod schema.
|
|
51
|
+
- **Premature abstraction**: Three similar lines > one premature helper. Abstract on the third repetition.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ui-ux-guidelines
|
|
3
|
+
description: "Review UI code for Web Interface Guidelines compliance. Use when asked to review UI, check accessibility, audit design, review UX, or check against best practices."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Web Interface Guidelines
|
|
7
|
+
|
|
8
|
+
Dispatch hub for UI/UX rules. Load the relevant reference file for full details.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
1. [Rule Categories](#rule-categories-by-priority)
|
|
13
|
+
2. [Workflows](#workflows)
|
|
14
|
+
3. [Anti-patterns](#anti-patterns-flag-these)
|
|
15
|
+
4. [Output Format](#code-review-output-format)
|
|
16
|
+
5. [Reference Files](#reference-files)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Rule Categories by Priority
|
|
21
|
+
|
|
22
|
+
| Priority | Category | Impact | Reference File |
|
|
23
|
+
| -------- | -------------------- | -------- | ------------------------------- |
|
|
24
|
+
| 1 | Accessibility | CRITICAL | `accessibility-and-interaction` |
|
|
25
|
+
| 2 | Touch & Interaction | CRITICAL | `accessibility-and-interaction` |
|
|
26
|
+
| 3 | Performance | HIGH | `layout-typography-animation` |
|
|
27
|
+
| 4 | Layout & Responsive | HIGH | `layout-typography-animation` |
|
|
28
|
+
| 5 | Typography & Color | MEDIUM | `layout-typography-animation` |
|
|
29
|
+
| 6 | Animation | MEDIUM | `layout-typography-animation` |
|
|
30
|
+
| 7 | Forms | HIGH | `forms-content-checklist` |
|
|
31
|
+
| 8 | Content & Navigation | MEDIUM | `forms-content-checklist` |
|
|
32
|
+
| 9 | Charts & Data | LOW | `layout-typography-animation` |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Workflows
|
|
37
|
+
|
|
38
|
+
### 1. Review UI code
|
|
39
|
+
|
|
40
|
+
1. Read the target file(s).
|
|
41
|
+
2. Load the relevant reference file(s) from `references/` based on what the code contains.
|
|
42
|
+
3. Check each applicable rule. Report violations in the output format below.
|
|
43
|
+
|
|
44
|
+
### 2. Build new component
|
|
45
|
+
|
|
46
|
+
1. Load `references/accessibility-and-interaction.md` -- all components must meet CRITICAL rules.
|
|
47
|
+
2. Load additional references based on component type:
|
|
48
|
+
- Form component -> `references/forms-content-checklist.md`
|
|
49
|
+
- Layout/visual component -> `references/layout-typography-animation.md`
|
|
50
|
+
3. Follow rules during implementation.
|
|
51
|
+
|
|
52
|
+
### 3. Pre-delivery checklist
|
|
53
|
+
|
|
54
|
+
1. Load `references/forms-content-checklist.md` for the full checklist.
|
|
55
|
+
2. Load `references/accessibility-and-interaction.md` for the interaction checklist.
|
|
56
|
+
3. Walk through every checkbox before shipping.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Anti-patterns (flag these)
|
|
61
|
+
|
|
62
|
+
- `user-scalable=no` or `maximum-scale=1` -- disables zoom
|
|
63
|
+
- `onPaste` with `preventDefault` -- blocks paste
|
|
64
|
+
- `transition: all` -- list properties explicitly
|
|
65
|
+
- `outline-none` without `:focus-visible` replacement
|
|
66
|
+
- `<div>`/`<span>` with click handlers -- use `<button>` or `<a>`
|
|
67
|
+
- `<img>` without `width` and `height` (causes CLS)
|
|
68
|
+
- Inline `onClick` navigation without `<a>` (breaks Cmd+click)
|
|
69
|
+
- Large `.map()` without virtualization (>50 items)
|
|
70
|
+
- Hardcoded date/number formats -- use `Intl.*`
|
|
71
|
+
- Icon-only buttons without `aria-label`
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Code Review Output Format
|
|
76
|
+
|
|
77
|
+
Group findings by file. Use `file:line` format (VS Code clickable). Be terse -- state issue and location. Skip explanation unless fix is non-obvious.
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
## src/Button.tsx
|
|
81
|
+
|
|
82
|
+
src/Button.tsx:42 - icon button missing aria-label
|
|
83
|
+
src/Button.tsx:18 - input lacks label
|
|
84
|
+
src/Button.tsx:55 - animation missing prefers-reduced-motion
|
|
85
|
+
src/Button.tsx:67 - transition: all -> list properties explicitly
|
|
86
|
+
|
|
87
|
+
## src/Modal.tsx
|
|
88
|
+
|
|
89
|
+
src/Modal.tsx:12 - missing overscroll-behavior: contain
|
|
90
|
+
src/Modal.tsx:34 - "..." -> "..."
|
|
91
|
+
|
|
92
|
+
## src/Card.tsx
|
|
93
|
+
|
|
94
|
+
pass
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Reference Files
|
|
100
|
+
|
|
101
|
+
Load these as needed during reviews and implementation:
|
|
102
|
+
|
|
103
|
+
- **[Accessibility & Interaction](references/accessibility-and-interaction.md)** -- Focus, ARIA, keyboard, touch targets, cursors, drag UX
|
|
104
|
+
- **[Layout, Typography & Animation](references/layout-typography-animation.md)** -- Performance, responsive, fonts, color, motion, charts
|
|
105
|
+
- **[Forms, Content & Checklist](references/forms-content-checklist.md)** -- Forms, content handling, navigation, dark mode, locale, hydration, pre-delivery checklist
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Accessibility & Interaction Reference
|
|
2
|
+
|
|
3
|
+
Rules for accessible, touch-friendly interfaces. Loaded by the dispatch hub when reviewing accessibility or interaction code.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Accessibility (CRITICAL)
|
|
8
|
+
|
|
9
|
+
### Focus & Keyboard
|
|
10
|
+
|
|
11
|
+
- `focus-states` — Visible focus rings on all interactive elements; use `:focus-visible` over `:focus`
|
|
12
|
+
- `keyboard-nav` — Tab order matches visual order; interactive elements need `onKeyDown`/`onKeyUp` handlers
|
|
13
|
+
- `focus-within` — Use `:focus-within` for compound controls (e.g., search with icon)
|
|
14
|
+
- `scroll-margin` — `scroll-margin-top` on heading anchors to avoid content hidden behind fixed headers
|
|
15
|
+
- `heading-hierarchy` — `<h1>`--`<h6>` hierarchical; include skip link for main content
|
|
16
|
+
|
|
17
|
+
### ARIA & Semantics
|
|
18
|
+
|
|
19
|
+
- `aria-labels` — `aria-label` for icon-only buttons; `aria-hidden="true"` for decorative icons
|
|
20
|
+
- `aria-live` — Async updates (toasts, loading, validation) need `aria-live="polite"`
|
|
21
|
+
- `semantic-first` — Use semantic HTML (`<button>`, `<a>`, `<label>`, `<table>`) before reaching for ARIA
|
|
22
|
+
- `color-contrast` — Minimum 4.5:1 ratio for normal text; color must not be the only indicator
|
|
23
|
+
|
|
24
|
+
### Form Accessibility
|
|
25
|
+
|
|
26
|
+
- `form-labels` — Use `<label>` with `htmlFor` or wrapping control on all inputs
|
|
27
|
+
- Checkboxes/radios: label and control share a single hit target (no dead zones)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Touch & Interaction (CRITICAL)
|
|
32
|
+
|
|
33
|
+
### Touch Targets
|
|
34
|
+
|
|
35
|
+
- `touch-target-size` — Minimum 44x44px touch targets
|
|
36
|
+
- `touch-action` — Add `touch-action: manipulation` to interactive elements (prevents double-tap zoom delay)
|
|
37
|
+
- `tap-highlight` — Set `-webkit-tap-highlight-color` intentionally (don't leave as default)
|
|
38
|
+
|
|
39
|
+
### Click & Hover
|
|
40
|
+
|
|
41
|
+
- `hover-vs-tap` — Use click/tap for primary interactions, never hover-only
|
|
42
|
+
- `cursor-pointer` — Add `cursor-pointer` to all clickable elements
|
|
43
|
+
- `loading-buttons` — Disable button during async operations; show loading state
|
|
44
|
+
- `error-feedback` — Clear error messages placed near the problem field
|
|
45
|
+
|
|
46
|
+
### Containment & Drag
|
|
47
|
+
|
|
48
|
+
- `overscroll` — Add `overscroll-behavior: contain` in modals, drawers, and sheets
|
|
49
|
+
- `drag-ux` — During drag operations: disable text selection, use `inert` on dragged elements
|
|
50
|
+
- `autofocus` — Use `autoFocus` sparingly — desktop only, single primary input; avoid on mobile
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Anti-patterns to Flag
|
|
55
|
+
|
|
56
|
+
- `user-scalable=no` or `maximum-scale=1` — disables zoom (accessibility violation)
|
|
57
|
+
- `outline-none` / `outline: 0` without a `:focus-visible` replacement
|
|
58
|
+
- `<div>` or `<span>` with click handlers — use `<button>` or `<a>`
|
|
59
|
+
- Icon-only buttons without `aria-label`
|
|
60
|
+
- Form inputs without associated labels
|
|
61
|
+
- `autoFocus` without clear justification on mobile
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Interaction Review Checklist
|
|
66
|
+
|
|
67
|
+
- [ ] All clickable elements have `cursor-pointer`
|
|
68
|
+
- [ ] Hover states provide clear visual feedback without layout shift
|
|
69
|
+
- [ ] Transitions are smooth (150--300ms)
|
|
70
|
+
- [ ] Focus states visible for keyboard navigation (`:focus-visible`)
|
|
71
|
+
- [ ] Destructive actions have confirmation modal or undo
|
|
72
|
+
- [ ] All images have descriptive alt text (or `alt=""` for decorative)
|
|
73
|
+
- [ ] Async updates (toasts, validation) use `aria-live="polite"`
|
|
74
|
+
- [ ] Interactive elements have keyboard handlers
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Forms, Content & Pre-Delivery Checklist
|
|
2
|
+
|
|
3
|
+
Rules for forms, content handling, navigation, dark mode, locale, and hydration. Includes the pre-delivery checklist. Loaded by the dispatch hub for form reviews and final delivery checks.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Forms
|
|
8
|
+
|
|
9
|
+
- Inputs need `autocomplete` attribute and meaningful `name`
|
|
10
|
+
- Use correct `type` (`email`, `tel`, `url`, `number`, `search`) and `inputmode` for virtual keyboards
|
|
11
|
+
- Never block paste (`onPaste` + `preventDefault`)
|
|
12
|
+
- Labels must be clickable: use `htmlFor` matching `id`, or wrap control in `<label>`
|
|
13
|
+
- Disable spellcheck on emails, codes, and usernames: `spellCheck={false}`
|
|
14
|
+
- Checkboxes/radios: label and control share a single hit target (no dead zones)
|
|
15
|
+
- Submit button stays enabled until request starts; show spinner during request
|
|
16
|
+
- Errors inline next to fields; focus the first error field on submit
|
|
17
|
+
- Placeholders end with `…` and show an example pattern (e.g., `"Ex: nome@email.com…"`)
|
|
18
|
+
- Use `autocomplete="off"` on non-auth fields where password manager triggers are unwanted
|
|
19
|
+
- Warn before navigation with unsaved changes (`beforeunload` event or router guard)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Content Handling
|
|
24
|
+
|
|
25
|
+
- Text containers must handle long content: use `truncate`, `line-clamp-*`, or `break-words`
|
|
26
|
+
- Flex children that contain text need `min-w-0` to allow text truncation to work
|
|
27
|
+
- Always handle empty states — never render broken UI for empty strings or arrays
|
|
28
|
+
- User-generated content: design and test for short, average, and very long inputs
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Navigation & State
|
|
33
|
+
|
|
34
|
+
- URL reflects state — filters, tabs, pagination, and expanded panels belong in query params
|
|
35
|
+
- Links must use `<a>`/`<Link>` to support Cmd/Ctrl+click and middle-click
|
|
36
|
+
- Deep-link all stateful UI — if it uses `useState`, consider URL sync via `nuqs` or similar
|
|
37
|
+
- Destructive actions require a confirmation modal or undo window — never immediate execution
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Dark Mode & Theming
|
|
42
|
+
|
|
43
|
+
- Set `color-scheme: dark` on `<html>` for dark themes (fixes scrollbar and native input appearance)
|
|
44
|
+
- `<meta name="theme-color">` must match the page background color
|
|
45
|
+
- Native `<select>` elements need explicit `background-color` and `color` for Windows dark mode
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Locale & i18n
|
|
50
|
+
|
|
51
|
+
- Dates and times: use `Intl.DateTimeFormat` — never hardcode date formats
|
|
52
|
+
- Numbers and currency: use `Intl.NumberFormat` — never hardcode number/currency formats
|
|
53
|
+
- Detect language via `Accept-Language` header or `navigator.languages` — not by IP geolocation
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Hydration Safety
|
|
58
|
+
|
|
59
|
+
- Controlled inputs with `value` need `onChange` (or use `defaultValue` for uncontrolled)
|
|
60
|
+
- Date/time rendering: guard against hydration mismatch between server and client
|
|
61
|
+
- `suppressHydrationWarning` only where truly necessary (e.g., system clock, browser extensions)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Content & Copy
|
|
66
|
+
|
|
67
|
+
- Active voice: "Install the CLI" not "The CLI will be installed"
|
|
68
|
+
- Sentence case for headings (e.g., "Configure notifications")
|
|
69
|
+
- Numerals for counts: "8 items" not "eight items"
|
|
70
|
+
- Specific button labels: "Save API Key" not "Continue"
|
|
71
|
+
- Error messages include the fix or next step — not just the problem description
|
|
72
|
+
- Second person ("you"); avoid first person ("I")
|
|
73
|
+
- Use `&` over "and" where space is constrained
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Pre-Delivery Checklist
|
|
78
|
+
|
|
79
|
+
Before delivering UI code, verify all items below.
|
|
80
|
+
|
|
81
|
+
### Visual Quality
|
|
82
|
+
|
|
83
|
+
- [ ] No emojis used as icons (SVG only)
|
|
84
|
+
- [ ] All icons from a consistent icon set (Lucide for this project)
|
|
85
|
+
- [ ] Brand logos verified from Simple Icons
|
|
86
|
+
- [ ] Hover states do not cause layout shift
|
|
87
|
+
- [ ] Theme colors used directly (e.g., `bg-primary`), not wrapped in `var()`
|
|
88
|
+
|
|
89
|
+
### Forms
|
|
90
|
+
|
|
91
|
+
- [ ] All inputs have `autocomplete` and meaningful `name`
|
|
92
|
+
- [ ] Correct `type` and `inputmode` on inputs
|
|
93
|
+
- [ ] Paste is not blocked
|
|
94
|
+
- [ ] All labels are clickable (`htmlFor` or wrapping)
|
|
95
|
+
- [ ] Submit button disabled + spinner during request
|
|
96
|
+
- [ ] Errors shown inline; first error focused on submit
|
|
97
|
+
|
|
98
|
+
### Light/Dark Mode
|
|
99
|
+
|
|
100
|
+
- [ ] Light mode text meets 4.5:1 contrast minimum
|
|
101
|
+
- [ ] Glass/transparent elements are visible in light mode
|
|
102
|
+
- [ ] Borders visible in both light and dark modes
|
|
103
|
+
- [ ] Both modes tested before delivery
|
|
104
|
+
- [ ] `color-scheme` set on `<html>` for dark themes
|
|
105
|
+
|
|
106
|
+
### Layout
|
|
107
|
+
|
|
108
|
+
- [ ] Floating elements have proper spacing from viewport edges
|
|
109
|
+
- [ ] No content hidden behind fixed navbars
|
|
110
|
+
- [ ] Responsive at 375px, 768px, 1024px, 1440px
|
|
111
|
+
- [ ] No horizontal scroll on mobile
|
|
112
|
+
- [ ] Safe area insets applied for full-bleed layouts
|
|
113
|
+
|
|
114
|
+
### Typography & Copy
|
|
115
|
+
|
|
116
|
+
- [ ] `…` (U+2026) used instead of `...` (three dots)
|
|
117
|
+
- [ ] Loading states end with `…`
|
|
118
|
+
- [ ] `font-variant-numeric: tabular-nums` on number columns
|
|
119
|
+
- [ ] `text-wrap: balance` on headings
|
|
120
|
+
- [ ] Error messages include the next step, not just the problem
|
|
121
|
+
|
|
122
|
+
### i18n & Hydration
|
|
123
|
+
|
|
124
|
+
- [ ] Dates use `Intl.DateTimeFormat`
|
|
125
|
+
- [ ] Numbers/currency use `Intl.NumberFormat`
|
|
126
|
+
- [ ] Controlled inputs have `onChange`; date/time rendering guarded against hydration mismatch
|