opencodekit 0.18.9 → 0.18.10

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/index.js CHANGED
@@ -18,7 +18,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
18
18
 
19
19
  //#endregion
20
20
  //#region package.json
21
- var version = "0.18.9";
21
+ var version = "0.18.10";
22
22
 
23
23
  //#endregion
24
24
  //#region src/utils/errors.ts
@@ -0,0 +1,146 @@
1
+ ---
2
+ description: Audit changed UI files for AI slop patterns and design-system violations
3
+ argument-hint: "[path|auto] [--staged] [--since=<ref>] [--full-report]"
4
+ agent: vision
5
+ model: proxypal/gemini-3-pro-preview
6
+ ---
7
+
8
+ # UI Slop Check: $ARGUMENTS
9
+
10
+ Run a focused anti-slop audit against changed UI files using the frontend-design taxonomy.
11
+
12
+ ## Load Skills
13
+
14
+ ```typescript
15
+ skill({ name: "frontend-design" }); // Anti-pattern taxonomy + design references
16
+ skill({ name: "visual-analysis" }); // Structured visual/code analysis workflow
17
+ skill({ name: "accessibility-audit" }); // Keyboard/focus/contrast checks
18
+ ```
19
+
20
+ ## Parse Arguments
21
+
22
+ | Argument | Default | Description |
23
+ | --------------- | ------- | -------------------------------------------------- | ----------------------------------------------------------- |
24
+ | `[path | auto]` | `auto` | Specific file/dir to audit, or auto-detect changed UI files |
25
+ | `--staged` | false | Audit staged changes only (`git diff --cached`) |
26
+ | `--since=<ref>` | `HEAD` | Compare against ref (`main`, `HEAD~1`, commit SHA) |
27
+ | `--full-report` | false | Include all categories even when no issues found |
28
+
29
+ ## Phase 1: Resolve Target Files
30
+
31
+ If `[path]` is provided:
32
+
33
+ - Audit that path directly
34
+
35
+ If `auto`:
36
+
37
+ ```bash
38
+ # unstaged + staged by default
39
+ git diff --name-only $SINCE_REF -- \
40
+ '*.tsx' '*.jsx' '*.css' '*.scss' '*.sass' '*.less' '*.html' '*.mdx'
41
+ ```
42
+
43
+ If `--staged`:
44
+
45
+ ```bash
46
+ git diff --cached --name-only -- \
47
+ '*.tsx' '*.jsx' '*.css' '*.scss' '*.sass' '*.less' '*.html' '*.mdx'
48
+ ```
49
+
50
+ Prioritize files under:
51
+
52
+ - `src/components/**`
53
+ - `src/app/**`
54
+ - `src/pages/**`
55
+ - `app/**`
56
+ - `components/**`
57
+
58
+ If no UI files changed, return: **PASS (no changed UI files)**.
59
+
60
+ ## Phase 2: Run AI Slop Checklist
61
+
62
+ Evaluate each target file (or rendered screenshot if provided) against these checks.
63
+
64
+ ### A) Typography
65
+
66
+ - Banned default aesthetics (Inter/Roboto/Arial/Open Sans as dominant display voice)
67
+ - Body text uses `rem/em`, not fixed `px`
68
+ - Clear hierarchy (size/weight/spacing), not color-only hierarchy
69
+ - Body line length near readable measure (around 65ch when applicable)
70
+
71
+ ### B) Color and Theming
72
+
73
+ - No AI default palette tropes (purple-blue gradient defaults, neon-on-dark clichés)
74
+ - No pure `#000` / `#fff` as dominant surfaces
75
+ - Gray text is not placed on saturated backgrounds
76
+ - Semantic tokens are used (not random per-component hardcoded colors)
77
+ - Dark mode is adapted, not simple inversion
78
+
79
+ ### C) Layout and Spatial Rhythm
80
+
81
+ - No cards-inside-cards without strong information architecture reason
82
+ - No repetitive cookie-cutter card blocks with identical structure
83
+ - Spacing rhythm is consistent (4pt-style cadence), not arbitrary jumps
84
+ - Uses `gap`/layout primitives cleanly; avoids margin hacks when possible
85
+
86
+ ### D) Motion and Interaction
87
+
88
+ - No bounce/elastic gimmick motion for product UI
89
+ - Animations use transform/opacity (avoid layout-thrashing properties)
90
+ - Reduced motion support exists for meaningful motion
91
+ - States exist: hover, focus-visible, active, disabled, loading/error where relevant
92
+
93
+ ### E) UX Writing
94
+
95
+ - Buttons are verb + object (e.g. "Save changes")
96
+ - Error copy includes what happened + why + how to fix
97
+ - Empty states include guidance + next action
98
+ - Terminology is consistent (avoid mixed synonyms for same action)
99
+
100
+ ### F) Accessibility Safety Nets
101
+
102
+ - Keyboard-visible focus treatment (`:focus-visible`)
103
+ - Contrast baseline expectations (WCAG AA)
104
+ - Touch targets reasonable (44x44 context where applicable)
105
+
106
+ ## Phase 3: Severity and Scoring
107
+
108
+ Group findings by severity:
109
+
110
+ - **Critical**: accessibility failures, broken interaction states, unreadable contrast
111
+ - **Warning**: strong AI fingerprint/slop patterns, inconsistent design system usage
112
+ - **Info**: polish/consistency opportunities
113
+
114
+ Score each category 1-10 and include evidence (`file:line` for code audits).
115
+
116
+ ## Phase 4: Output
117
+
118
+ Return:
119
+
120
+ 1. **Result**: PASS / NEEDS WORK
121
+ 2. **Audited files** (list)
122
+ 3. **Category scores**
123
+ 4. **Findings by severity** with actionable fixes
124
+ 5. **Fast remediation plan** (top 3 fixes first)
125
+
126
+ If `--full-report` is false, omit empty categories.
127
+
128
+ ## Record Findings
129
+
130
+ ```typescript
131
+ observation({
132
+ type: "warning",
133
+ title: "UI Slop Check: [scope]",
134
+ narrative: "Detected [count] critical, [count] warning slop issues in changed UI files.",
135
+ concepts: "ui, design, anti-patterns, frontend",
136
+ confidence: "high",
137
+ });
138
+ ```
139
+
140
+ ## Related Commands
141
+
142
+ | Need | Command |
143
+ | ---------------------------------------- | ------------ |
144
+ | Design from scratch | `/design` |
145
+ | Full UI review (single screen/component) | `/ui-review` |
146
+ | Implementation work | `/start` |
Binary file
@@ -11,7 +11,7 @@
11
11
  "type-check": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.2.24"
14
+ "@opencode-ai/plugin": "1.2.27"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.3.0",
@@ -110,10 +110,14 @@ function compressMessage(msg: TransformMessage): TransformMessage {
110
110
  * Keeps first and last sentence, truncates middle.
111
111
  */
112
112
  function createSummary(text: string, role: string): string {
113
- const maxChars = 200;
113
+ const maxChars = 500;
114
114
 
115
115
  if (text.length <= maxChars) return text;
116
116
 
117
+ // For very large messages (>5000 chars), be more aggressive
118
+ const isLarge = text.length > 5000;
119
+ const effectiveMax = isLarge ? 300 : maxChars;
120
+
117
121
  // Split into sentences
118
122
  const sentences = text
119
123
  .split(/(?<=[.!?])\s+|\n+/)
@@ -121,16 +125,16 @@ function createSummary(text: string, role: string): string {
121
125
  .filter((s) => s.length > 0);
122
126
 
123
127
  if (sentences.length <= 2) {
124
- return `${text.slice(0, maxChars)}...`;
128
+ return `${text.slice(0, effectiveMax)}...`;
125
129
  }
126
130
 
127
131
  const first = sentences[0];
128
132
  const last = sentences[sentences.length - 1];
129
133
 
130
- const summary = `[compressed ${role} message] ${first} [...${sentences.length - 2} more sentences...] ${last}`;
134
+ const summary = `[compressed ${role}] ${first} [...${sentences.length - 2} sentences...] ${last}`;
131
135
 
132
- return summary.length > maxChars * 2
133
- ? `${summary.slice(0, maxChars * 2)}...`
136
+ return summary.length > effectiveMax
137
+ ? `${summary.slice(0, effectiveMax)}...`
134
138
  : summary;
135
139
  }
136
140
 
@@ -38,7 +38,7 @@ export const MEMORY_CONFIG = {
38
38
  },
39
39
  context: {
40
40
  enabled: true,
41
- maxContextTokens: 100_000,
41
+ maxContextTokens: 80_000,
42
42
  protectedMessages: 5,
43
43
  },
44
44
  fts: {
@@ -90,13 +90,24 @@ export function createHooks(deps: HookDeps) {
90
90
  }
91
91
  }
92
92
 
93
- // --- Session error: warn user ---
93
+ // --- Session error: show actual error details ---
94
94
  if (event.type === "session.error") {
95
- await showToast(
96
- "Session Error",
97
- "Save important learnings with observation tool",
98
- "warning",
99
- );
95
+ const props = event.properties as Record<string, unknown> | undefined;
96
+ const errorMsg = props?.error
97
+ ? String(props.error).slice(0, 120)
98
+ : props?.message
99
+ ? String(props.message).slice(0, 120)
100
+ : "Unknown error";
101
+
102
+ // Classify: match the specific AI SDK error pattern
103
+ const isTokenOverflow =
104
+ /token.{0,20}(exceed|limit)/i.test(errorMsg) ||
105
+ errorMsg.includes("context_length_exceeded");
106
+ const guidance = isTokenOverflow
107
+ ? "Context too large — use /compact or start a new session"
108
+ : "Save important learnings with observation tool";
109
+
110
+ await showToast("Session Error", `${guidance} (${errorMsg})`, "warning");
100
111
  }
101
112
  },
102
113
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: frontend-design
3
3
  description: Create distinctive, production-grade frontend interfaces. Use when building web components, pages, or applications with React-based frameworks. Includes Tailwind CSS v4, shadcn/ui components, Motion animations, and visual design philosophy for avoiding generic AI aesthetics.
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  tags: [ui, design]
6
6
  dependencies: []
7
7
  ---
@@ -21,7 +21,6 @@ dependencies: []
21
21
 
22
22
  - Backend-only tasks or minimal UI with no visual design requirements.
23
23
 
24
-
25
24
  ## Reference Documentation
26
25
 
27
26
  ### Tailwind CSS v4.1
@@ -46,7 +45,7 @@ Search: `Field`, `InputGroup`, `Spinner`, `ButtonGroup`, `next-themes`
46
45
 
47
46
  ### Animation (Motion + Tailwind)
48
47
 
49
- - `./references/animation/motion-core.md` - Core API, variants, gestures, layout animations
48
+ - `./references/animation/motion-core.md` - Timing system, easing constants, performance rules, reduced motion, core patterns
50
49
  - `./references/animation/motion-advanced.md` - AnimatePresence, scroll, orchestration, TypeScript
51
50
 
52
51
  **Stack**:
@@ -64,6 +63,15 @@ Search: `Field`, `InputGroup`, `Spinner`, `ButtonGroup`, `next-themes`
64
63
 
65
64
  For sophisticated compositions: posters, brand materials, design systems.
66
65
 
66
+ ### Design Systems (Deep Guides)
67
+
68
+ - `./references/design/color-system.md` - OKLCH, semantic tokens, dark mode architecture
69
+ - `./references/design/typography-rules.md` - Fluid type, modular scale, OpenType features
70
+ - `./references/design/interaction.md` - State models, focus, dialogs/popovers, loading patterns
71
+ - `./references/design/ux-writing.md` - Button copy, error structure, empty states, i18n
72
+
73
+ Search: `AI Slop Test`, `tinted neutrals`, `focus-visible`, `verb + object`, `65ch`
74
+
67
75
  ## Design Thinking
68
76
 
69
77
  Before coding, commit to BOLD aesthetic direction:
@@ -74,23 +82,60 @@ Before coding, commit to BOLD aesthetic direction:
74
82
 
75
83
  Bold maximalism and refined minimalism both work. Key is intentionality.
76
84
 
77
- ## Anti-Patterns (NEVER)
85
+ ## Anti-Patterns (AI Fingerprints — NEVER)
86
+
87
+ These patterns immediately signal "AI made this." Avoid them all.
88
+
89
+ ### Typography
90
+
91
+ - **Banned fonts**: Inter, Roboto, Arial, Open Sans, Lato, Montserrat, Space Grotesk, system-ui as display font
92
+ - Monospace used as "developer aesthetic" shorthand
93
+ - Big icons centered above every heading
94
+ - Using `px` for body text (use `rem`/`em` to respect user settings)
95
+
96
+ ### Color
78
97
 
79
- - Overused fonts: Inter, Roboto, Arial, system fonts, Space Grotesk
80
- - Cliched colors: purple gradients on white
81
- - Predictable layouts and component patterns
82
- - Cookie-cutter design lacking character
83
- - Generic AI-generated aesthetics
98
+ - **AI palette**: Cyan-on-dark, purple-to-blue gradients, neon accents on dark backgrounds
99
+ - Gray text on colored backgrounds (use darker shade of background color instead)
100
+ - Pure `#000` or `#fff` (always tint — pure black/white don't exist in nature)
101
+ - Gradient text on headings or metrics
102
+ - `rgba()` / heavy alpha transparency as primary palette (design smell — define explicit colors)
103
+
104
+ ### Layout
105
+
106
+ - Cards nested inside cards (use typography, spacing, dividers for hierarchy within a card)
107
+ - Identical card grids (icon + heading + text repeated 3-6 times)
108
+ - Hero metric template (big number + small label + gradient accent)
109
+ - Center-aligning everything
110
+ - Same spacing everywhere (no visual rhythm)
111
+
112
+ ### Visual
113
+
114
+ - Glassmorphism used decoratively (blur cards, glow borders)
115
+ - Thick colored border on one side of rounded rectangles
116
+ - Sparklines as decoration (not connected to real data)
117
+ - Generic drop shadows on everything
118
+ - Rounded rectangles as the only shape language
119
+
120
+ ### Motion
121
+
122
+ - Bounce or elastic easing (real objects decelerate smoothly)
123
+ - Animating `height`, `width`, `padding`, `margin` (causes layout recalculation)
124
+ - Default `ease` (compromise that's rarely optimal — use exponential easing)
125
+ - Missing `prefers-reduced-motion` handling
126
+
127
+ > **The AI Slop Test**: If you showed this interface to someone and said "AI made this," would they believe you immediately? If yes, that's the problem.
84
128
 
85
129
  ## Best Practices
86
130
 
87
- 1. **Accessibility First**: Radix primitives, focus states, semantic HTML
88
- 2. **Mobile-First**: Start mobile, layer responsive variants
89
- 3. **Design Tokens**: Use `@theme` for spacing, colors, typography
90
- 4. **Dark Mode**: Apply dark variants to all themed elements
91
- 5. **Performance**: Automatic CSS purging, avoid dynamic class names
92
- 6. **TypeScript**: Full type safety
93
- 7. **Expert Craftsmanship**: Every detail matters
131
+ 1. **Accessibility First**: Radix primitives, `:focus-visible` (not `:focus`), semantic HTML, 44px touch targets
132
+ 2. **Mobile-First**: Start mobile, layer responsive variants with `min-width` queries
133
+ 3. **Design Tokens**: Two-layer system — primitives (`--blue-500`) + semantic (`--color-primary: var(--blue-500)`); dark mode redefines semantic layer only
134
+ 4. **Dark Mode**: Not inverted light mode lighter surfaces create depth (no shadows); desaturate accents; base at `oklch(15-18% …)`
135
+ 5. **Container Queries**: Use `@container` for component layout, viewport queries for page layout
136
+ 6. **Performance**: `transform` + `opacity` only for animations; CSS purging; avoid dynamic class names
137
+ 7. **TypeScript**: Full type safety
138
+ 8. **Expert Craftsmanship**: Every detail matters — squint test for hierarchy validation
94
139
 
95
140
  ## Core Stack Summary
96
141
 
@@ -102,63 +147,88 @@ Bold maximalism and refined minimalism both work. Key is intentionality.
102
147
 
103
148
  ## Typography
104
149
 
105
- Choose beautiful, unique fonts. Pair distinctive display with refined body:
150
+ Consult `./references/design/typography-rules.md` for full fluid type system
151
+
152
+ Choose distinctive fonts. Pair display with body:
153
+
154
+ - **Preferred**: Instrument Sans, Plus Jakarta Sans, Outfit, Onest, Figtree, Urbanist (sans); Fraunces, Newsreader (editorial)
155
+ - **Fluid sizing**: `clamp(1rem, 0.5rem + 2vw, 1.5rem)` — never fixed `px` for body
156
+ - **Modular scale**: Pick one ratio (1.25 major third, 1.333 perfect fourth) — 5 sizes max
157
+ - **Measure**: `max-width: 65ch` for body text
158
+ - **OpenType**: `tabular-nums` for data, `diagonal-fractions` for recipes, `all-small-caps` for abbreviations
106
159
 
107
160
  ```css
108
161
  @theme {
109
- --font-display: "Playfair Display", serif;
110
- --font-body: "Source Sans 3", sans-serif;
162
+ --font-display: "Fraunces", serif;
163
+ --font-body: "Instrument Sans", sans-serif;
111
164
  }
112
165
  ```
113
166
 
114
167
  ## Color
115
168
 
116
- Use OKLCH for vivid colors. Dominant colors with sharp accents:
169
+ Consult `./references/design/color-system.md` for OKLCH deep guide
170
+
171
+ Use OKLCH for perceptually uniform colors. Two-layer token system:
117
172
 
118
173
  ```css
119
174
  @theme {
120
- --color-primary-500: oklch(0.55 0.22 264);
121
- --color-accent: oklch(0.75 0.18 45);
175
+ /* Primitives */
176
+ --blue-500: oklch(0.55 0.22 264);
177
+ --amber-400: oklch(0.75 0.18 80);
178
+ /* Semantic (redefine these for dark mode) */
179
+ --color-primary: var(--blue-500);
180
+ --color-accent: var(--amber-400);
181
+ /* Tinted neutrals — chroma 0.01 for subconscious brand cohesion */
182
+ --color-surface: oklch(0.97 0.01 264);
183
+ --color-surface-dark: oklch(0.16 0.01 264);
122
184
  }
123
185
  ```
124
186
 
125
187
  ## Motion
126
188
 
127
- **Primary**: Motion for React animations. **Fallback**: CSS `@starting-style` for simple enter/exit.
189
+ Consult `./references/animation/motion-core.md` for Motion API
190
+
191
+ **Timing**: 100-150ms instant feedback | 200-300ms state changes | 300-500ms layout | exit = 75% of enter
192
+
193
+ **Easing**: Exponential only — `cubic-bezier(0.16, 1, 0.3, 1)` for entrances. Never `ease`, never bounce/elastic.
194
+
195
+ **Performance**: Only animate `transform` and `opacity`. Height expand → `grid-template-rows: 0fr → 1fr`.
196
+
197
+ **Accessibility**: `prefers-reduced-motion` is mandatory (affects ~35% of adults over 40). Swap spatial animations for crossfades.
128
198
 
129
199
  ```tsx
130
200
  import { motion, AnimatePresence } from 'motion/react';
131
201
 
132
- // Basic animation
133
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
202
+ <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }}
203
+ transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }} />
134
204
 
135
- // Exit animations
136
205
  <AnimatePresence>
137
- {show && <motion.div exit={{ opacity: 0 }} />}
206
+ {show && <motion.div exit={{ opacity: 0 }} transition={{ duration: 0.2 }} />}
138
207
  </AnimatePresence>
208
+ ```
139
209
 
140
- // Layout animations
141
- <motion.div layout />
210
+ CSS stagger: `animation-delay: calc(var(--i, 0) * 50ms)` — cap total at 500ms.
142
211
 
143
- // Gestures
144
- <motion.button whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} />
145
- ```
212
+ ## Spatial Composition
146
213
 
147
- CSS fallback (no JS):
214
+ - **4pt spacing system**: 4, 8, 12, 16, 24, 32, 48, 64, 96px — not 8pt (too coarse at small gaps)
215
+ - **`gap` over margins** for sibling spacing (eliminates margin collapse)
216
+ - **Self-adjusting grids**: `repeat(auto-fit, minmax(280px, 1fr))` — responsive without breakpoints
217
+ - **Container queries** for components: `container-type: inline-size` on wrapper, `@container` on children
218
+ - **Optical text alignment**: `margin-left: -0.05em` for text that appears indented due to letterform whitespace
219
+ - Asymmetry, overlap, diagonal flow, grid-breaking elements. Generous negative space OR controlled density.
148
220
 
149
- ```css
150
- dialog[open] {
151
- opacity: 1;
152
- @starting-style {
153
- opacity: 0;
154
- transform: scale(0.95);
155
- }
156
- }
157
- ```
221
+ ## Interaction
158
222
 
159
- ## Spatial Composition
223
+ Consult `./references/design/interaction.md`
224
+
225
+ Design all 8 states: Default, Hover, Focus, Active, Disabled, Loading, Error, Success. Use `:focus-visible` not `:focus`. Native `<dialog>` + `inert` for modals. Popover API for tooltips/dropdowns. Skeleton screens over spinners. Undo over confirmation dialogs.
226
+
227
+ ## UX Writing
228
+
229
+ → Consult `./references/design/ux-writing.md`
160
230
 
161
- Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
231
+ Button labels: specific verb + object ("Save changes" not "OK"). Error formula: What happened + Why + How to fix. Empty states are onboarding opportunities. Plan for 30% text expansion (i18n).
162
232
 
163
233
  ## Backgrounds
164
234
 
@@ -1,171 +1,181 @@
1
- # Motion Core (formerly Framer Motion)
1
+ # Motion Core (motion/react)
2
2
 
3
3
  **Import**: `import { motion, AnimatePresence } from 'motion/react'`
4
4
 
5
- ## Basic Animation
5
+ ## Motion Principles
6
6
 
7
- ```tsx
8
- // Simple animate on mount
9
- <motion.div
10
- initial={{ opacity: 0, y: 20 }}
11
- animate={{ opacity: 1, y: 0 }}
12
- transition={{ duration: 0.6 }}
13
- />
7
+ - Animate for clarity, not decoration
8
+ - Use motion to explain state change and hierarchy
9
+ - Prefer subtle distance (`8-16px`) and opacity shifts
10
+ - Use consistent timing/easing system across the app
14
11
 
15
- // Shorthand (no transition object)
16
- <motion.div animate={{ scale: 1.1 }} />
17
- ```
12
+ ## Timing System
18
13
 
19
- ## Variants (Recommended Pattern)
14
+ | Use Case | Duration |
15
+ | ----------------------------- | ---------- |
16
+ | Instant feedback (hover/tap) | 100-150ms |
17
+ | State changes (menus/toggles) | 200-300ms |
18
+ | Layout transitions | 300-500ms |
19
+ | Large entrances | 500-800ms |
20
20
 
21
- ```tsx
22
- const variants = {
23
- hidden: { opacity: 0, y: 20 },
24
- visible: { opacity: 1, y: 0 }
25
- };
21
+ **Rule**: exit duration = ~75% of enter duration.
26
22
 
27
- <motion.div
28
- variants={variants}
29
- initial="hidden"
30
- animate="visible"
31
- />
32
- ```
23
+ ## Easing System
33
24
 
34
- ## Gestures
25
+ Use exponential easing by default:
35
26
 
36
27
  ```tsx
37
- <motion.button
38
- whileHover={{ scale: 1.05 }}
39
- whileTap={{ scale: 0.95 }}
40
- whileFocus={{ outline: '2px solid blue' }}
41
- />
28
+ const EASING_ENTER = [0.16, 1, 0.3, 1];
29
+ const EASING_EXIT = [0.4, 0, 1, 1];
30
+ ```
42
31
 
43
- // Drag
32
+ Avoid bounce/elastic easings for product UI.
33
+
34
+ ## Performance Rules
35
+
36
+ Animate only compositor-friendly properties:
37
+
38
+ - `transform`
39
+ - `opacity`
40
+
41
+ Avoid animating:
42
+
43
+ - `width`, `height`
44
+ - `top`, `left`
45
+ - `margin`, `padding`
46
+
47
+ ## Basic Pattern
48
+
49
+ ```tsx
44
50
  <motion.div
45
- drag
46
- dragConstraints={{ left: 0, right: 300 }}
47
- dragElastic={0.2}
51
+ initial={{ opacity: 0, y: 12 }}
52
+ animate={{ opacity: 1, y: 0 }}
53
+ transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
48
54
  />
49
55
  ```
50
56
 
51
- ## Layout Animations
57
+ ## Variants Pattern (Recommended)
52
58
 
53
59
  ```tsx
54
- // Automatic layout animation
55
- <motion.div layout />
56
-
57
- // Shared layout
58
- <motion.div layoutId="shared-element" />
60
+ const card = {
61
+ hidden: { opacity: 0, y: 12 },
62
+ visible: {
63
+ opacity: 1,
64
+ y: 0,
65
+ transition: { duration: 0.3, ease: [0.16, 1, 0.3, 1] },
66
+ },
67
+ };
59
68
 
60
- // Layout with spring
61
- <motion.div layout transition={{ type: 'spring', stiffness: 300 }} />
69
+ <motion.div variants={card} initial="hidden" animate="visible" />
62
70
  ```
63
71
 
72
+ Use variants for shared timing and maintainability.
73
+
64
74
  ## Exit Animations (AnimatePresence)
65
75
 
66
76
  ```tsx
67
- import { AnimatePresence } from 'motion/react';
68
-
69
- <AnimatePresence>
70
- {isVisible && (
77
+ <AnimatePresence mode="wait">
78
+ {open && (
71
79
  <motion.div
72
- key="modal"
73
- initial={{ opacity: 0 }}
74
- animate={{ opacity: 1 }}
75
- exit={{ opacity: 0 }}
80
+ key="panel"
81
+ initial={{ opacity: 0, y: 8 }}
82
+ animate={{ opacity: 1, y: 0 }}
83
+ exit={{ opacity: 0, y: 4 }}
84
+ transition={{ duration: 0.22, ease: [0.4, 0, 1, 1] }}
76
85
  />
77
86
  )}
78
87
  </AnimatePresence>
79
88
  ```
80
89
 
81
- ## Stagger Children
90
+ Always provide stable `key` values for exiting elements.
91
+
92
+ ## Stagger Patterns
82
93
 
83
94
  ```tsx
84
95
  const container = {
85
96
  hidden: { opacity: 0 },
86
97
  visible: {
87
98
  opacity: 1,
88
- transition: { staggerChildren: 0.1 }
89
- }
99
+ transition: {
100
+ staggerChildren: 0.05,
101
+ delayChildren: 0.05,
102
+ },
103
+ },
90
104
  };
91
105
 
92
106
  const item = {
93
- hidden: { opacity: 0, y: 20 },
94
- visible: { opacity: 1, y: 0 }
107
+ hidden: { opacity: 0, y: 8 },
108
+ visible: { opacity: 1, y: 0 },
95
109
  };
96
-
97
- <motion.ul variants={container} initial="hidden" animate="visible">
98
- {items.map(i => <motion.li key={i} variants={item} />)}
99
- </motion.ul>
100
110
  ```
101
111
 
102
- ## Transition Types
103
-
104
- ```tsx
105
- // Spring (default for physical properties)
106
- transition={{ type: 'spring', stiffness: 300, damping: 20 }}
112
+ Cap total stagger windows to ~500ms.
107
113
 
108
- // Tween
109
- transition={{ type: 'tween', duration: 0.5, ease: 'easeInOut' }}
114
+ ## Layout Animations
110
115
 
111
- // Inertia (for drag)
112
- transition={{ type: 'inertia', velocity: 50 }}
116
+ ```tsx
117
+ <motion.div layout />
113
118
  ```
114
119
 
115
- ## Common Easing
120
+ Use `layout` for reordering and size changes. Add spring only when needed:
116
121
 
117
122
  ```tsx
118
- ease: 'linear'
119
- ease: 'easeIn' | 'easeOut' | 'easeInOut'
120
- ease: 'circIn' | 'circOut' | 'circInOut'
121
- ease: 'backIn' | 'backOut' | 'backInOut'
122
- ease: [0.4, 0, 0.2, 1] // cubic-bezier
123
+ <motion.div layout transition={{ type: 'spring', stiffness: 320, damping: 28 }} />
123
124
  ```
124
125
 
125
- ## useAnimate Hook
126
+ ## Gestures
126
127
 
127
128
  ```tsx
128
- import { useAnimate } from 'motion/react';
129
-
130
- function Component() {
131
- const [scope, animate] = useAnimate();
132
-
133
- const handleClick = async () => {
134
- await animate(scope.current, { x: 100 });
135
- await animate(scope.current, { scale: 1.2 });
136
- };
137
-
138
- return <div ref={scope} onClick={handleClick} />;
139
- }
129
+ <motion.button
130
+ whileHover={{ scale: 1.02 }}
131
+ whileTap={{ scale: 0.98 }}
132
+ transition={{ duration: 0.12 }}
133
+ />
140
134
  ```
141
135
 
142
- ## Motion Values
136
+ Keep gesture amplitudes subtle (`0.98-1.03`).
143
137
 
144
- ```tsx
145
- import { useMotionValue, useTransform } from 'motion/react';
138
+ ## Height Expand/Collapse (No height animation)
146
139
 
147
- const x = useMotionValue(0);
148
- const opacity = useTransform(x, [0, 100], [1, 0]);
140
+ Use CSS grid technique:
141
+
142
+ ```css
143
+ .accordion-content {
144
+ display: grid;
145
+ grid-template-rows: 0fr;
146
+ transition: grid-template-rows 280ms cubic-bezier(0.16, 1, 0.3, 1);
147
+ }
149
148
 
150
- <motion.div style={{ x, opacity }} drag="x" />
149
+ .accordion-content[data-open='true'] {
150
+ grid-template-rows: 1fr;
151
+ }
152
+
153
+ .accordion-inner {
154
+ overflow: hidden;
155
+ }
151
156
  ```
152
157
 
153
- ## Integration with Tailwind
158
+ ## Reduced Motion (Mandatory)
154
159
 
155
- ```tsx
156
- // Motion handles animation, Tailwind handles styling
157
- <motion.div
158
- className="bg-blue-500 rounded-lg p-4"
159
- whileHover={{ scale: 1.05 }}
160
- transition={{ type: 'spring' }}
161
- />
160
+ ```css
161
+ @media (prefers-reduced-motion: reduce) {
162
+ * {
163
+ animation-duration: 0.01ms !important;
164
+ animation-iteration-count: 1 !important;
165
+ transition-duration: 0.01ms !important;
166
+ scroll-behavior: auto !important;
167
+ }
168
+ }
162
169
  ```
163
170
 
164
- ## Validation Checklist
171
+ For motion/react, switch spatial movement to opacity-only when reduced motion is enabled.
172
+
173
+ ## Quick Checklist
165
174
 
166
- - [ ] Import from `motion/react` (not `framer-motion`)
167
- - [ ] Use variants for complex animations
168
- - [ ] Wrap conditional renders with `AnimatePresence`
169
- - [ ] Add `key` prop to animated elements in `AnimatePresence`
170
- - [ ] Use `layout` for automatic layout animations
171
- - [ ] Prefer `whileHover`/`whileTap` over CSS `:hover`
175
+ - [ ] Uses `motion/react` import
176
+ - [ ] Timing follows 100/300/500ms system
177
+ - [ ] Exponential easing, no bounce/elastic
178
+ - [ ] Animates only `transform` and `opacity`
179
+ - [ ] Uses `AnimatePresence` for exit states
180
+ - [ ] Includes reduced motion support
181
+ - [ ] Stagger windows stay under 500ms
@@ -0,0 +1,111 @@
1
+ # Color System — OKLCH Deep Guide
2
+
3
+ ## Why OKLCH Over HSL
4
+
5
+ HSL is **not perceptually uniform** — `hsl(60, 100%, 50%)` (yellow) appears far brighter than `hsl(240, 100%, 50%)` (blue) at the same lightness. OKLCH fixes this: equal lightness steps _look_ equal.
6
+
7
+ ```css
8
+ /* HSL: looks inconsistent */
9
+ --blue: hsl(240, 70%, 50%);
10
+ --green: hsl(120, 70%, 50%); /* Appears much brighter */
11
+
12
+ /* OKLCH: looks consistent */
13
+ --blue: oklch(0.55 0.22 264);
14
+ --green: oklch(0.55 0.18 145); /* Same perceived brightness */
15
+ ```
16
+
17
+ ## Two-Layer Token Architecture
18
+
19
+ **Layer 1 — Primitives** (raw values, never used directly in components):
20
+
21
+ ```css
22
+ @theme {
23
+ --blue-100: oklch(0.95 0.03 264);
24
+ --blue-300: oklch(0.75 0.1 264);
25
+ --blue-500: oklch(0.55 0.22 264);
26
+ --blue-700: oklch(0.4 0.18 264);
27
+ --blue-900: oklch(0.25 0.12 264);
28
+ }
29
+ ```
30
+
31
+ **Layer 2 — Semantic** (what components reference; redefine for dark mode):
32
+
33
+ ```css
34
+ @theme {
35
+ --color-primary: var(--blue-500);
36
+ --color-primary-hover: var(--blue-700);
37
+ --color-surface: var(--neutral-50);
38
+ --color-surface-elevated: var(--neutral-0);
39
+ --color-text: var(--neutral-900);
40
+ --color-text-muted: var(--neutral-600);
41
+ --color-border: var(--neutral-200);
42
+ }
43
+
44
+ /* Dark mode: redefine ONLY semantic layer */
45
+ @media (prefers-color-scheme: dark) {
46
+ @theme {
47
+ --color-primary: var(--blue-300);
48
+ --color-primary-hover: var(--blue-100);
49
+ --color-surface: var(--neutral-900);
50
+ --color-surface-elevated: var(--neutral-800);
51
+ --color-text: var(--neutral-100);
52
+ --color-text-muted: var(--neutral-400);
53
+ --color-border: var(--neutral-700);
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## Tinted Neutrals
59
+
60
+ Never use pure gray (`chroma: 0`). Add `chroma: 0.01` hinted toward brand hue — barely visible but creates subconscious cohesion:
61
+
62
+ ```css
63
+ @theme {
64
+ /* Brand hue: 264 (blue) — neutrals subtly tinted */
65
+ --neutral-0: oklch(1 0.005 264);
66
+ --neutral-50: oklch(0.97 0.01 264);
67
+ --neutral-100: oklch(0.93 0.01 264);
68
+ --neutral-200: oklch(0.87 0.01 264);
69
+ --neutral-400: oklch(0.7 0.01 264);
70
+ --neutral-600: oklch(0.5 0.01 264);
71
+ --neutral-800: oklch(0.25 0.01 264);
72
+ --neutral-900: oklch(0.16 0.01 264);
73
+ }
74
+ ```
75
+
76
+ ## Dark Mode Rules
77
+
78
+ Dark mode is **not** inverted light mode:
79
+
80
+ | Aspect | Light Mode | Dark Mode |
81
+ | ------- | ---------------------------- | ---------------------------------------------------- |
82
+ | Depth | Shadows create elevation | Lighter surfaces create elevation |
83
+ | Base | `oklch(0.97+ …)` | `oklch(0.15-0.18 …)` — NOT pure black |
84
+ | Accents | Full saturation | Desaturate 10-20% to reduce glare |
85
+ | Text | Dark on light, high contrast | Light on dark, slightly reduced contrast for comfort |
86
+ | Borders | Darker than surface | Lighter than surface |
87
+
88
+ ```css
89
+ /* Dark surface elevation via lightness, not shadows */
90
+ --surface-0: oklch(0.15 0.01 264); /* Base */
91
+ --surface-1: oklch(0.18 0.01 264); /* Cards */
92
+ --surface-2: oklch(0.22 0.01 264); /* Modals */
93
+ --surface-3: oklch(0.26 0.01 264); /* Tooltips */
94
+ ```
95
+
96
+ ## 60-30-10 Rule
97
+
98
+ Applied to **visual weight**, not pixel count:
99
+
100
+ - 60% dominant (background, surface colors)
101
+ - 30% secondary (text, icons, subtle accents)
102
+ - 10% accent (CTAs, highlights, active states)
103
+
104
+ Accent works _because_ it's rare — overuse kills its power.
105
+
106
+ ## Common Mistakes
107
+
108
+ - **Alpha as palette**: Heavy `rgba()` / transparency means incomplete palette — define explicit overlay colors per context
109
+ - **Gray text on colored background**: Use a darker shade of the background color or apply the text color at reduced opacity
110
+ - **Same accent everywhere**: If everything is "primary blue," nothing stands out
111
+ - **Ignoring contrast in dark mode**: WCAG 4.5:1 for body text, 3:1 for large text and UI components
@@ -0,0 +1,149 @@
1
+ # Interaction Design
2
+
3
+ ## The 8 Interactive States
4
+
5
+ Every interactive element must design for ALL of these:
6
+
7
+ | State | When | Visual Treatment |
8
+ | -------- | --------------------- | --------------------------------------------- |
9
+ | Default | Resting | Base appearance |
10
+ | Hover | Cursor over (desktop) | Subtle shift — color, shadow, or translate |
11
+ | Focus | Keyboard navigation | `:focus-visible` ring (NOT `:focus`) |
12
+ | Active | Being pressed/clicked | Compressed/depressed feedback |
13
+ | Disabled | Not available | Reduced opacity (0.5) + `cursor: not-allowed` |
14
+ | Loading | Processing | Spinner or skeleton, disable interaction |
15
+ | Error | Validation failed | Red border + error message below |
16
+ | Success | Action completed | Green confirmation, brief |
17
+
18
+ ## Focus Management
19
+
20
+ ```css
21
+ /* Remove default focus for mouse, show for keyboard */
22
+ :focus:not(:focus-visible) {
23
+ outline: none;
24
+ }
25
+ :focus-visible {
26
+ outline: 2px solid var(--color-primary);
27
+ outline-offset: 2px;
28
+ }
29
+ ```
30
+
31
+ **Focus ring rules:**
32
+
33
+ - 2-3px thick
34
+ - Offset from element edge
35
+ - 3:1 minimum contrast against adjacent colors
36
+ - Consistent style across ALL interactive elements
37
+
38
+ ## Native Dialog + Inert
39
+
40
+ Use `<dialog>` with the `inert` attribute — eliminates complex focus-trapping JS:
41
+
42
+ ```html
43
+ <dialog id="modal">
44
+ <form method="dialog">
45
+ <h2>Confirm action</h2>
46
+ <button value="cancel">Cancel</button>
47
+ <button value="confirm">Confirm</button>
48
+ </form>
49
+ </dialog>
50
+
51
+ <main id="content">…</main>
52
+
53
+ <script>
54
+ const modal = document.getElementById("modal");
55
+ modal.showModal();
56
+ document.getElementById("content").inert = true;
57
+ </script>
58
+ ```
59
+
60
+ ## Popover API
61
+
62
+ For tooltips, dropdowns, and popovers — light-dismiss, proper stacking, accessible by default:
63
+
64
+ ```html
65
+ <button popovertarget="menu">Options</button>
66
+ <div id="menu" popover>
67
+ <button>Edit</button>
68
+ <button>Delete</button>
69
+ </div>
70
+ ```
71
+
72
+ No z-index wars. No portal wrappers. Built-in light dismiss.
73
+
74
+ ## Form Validation Timing
75
+
76
+ - **Validate on blur**, not on keystroke (exception: password strength meters)
77
+ - Show errors below the field, not in toast/alert
78
+ - Inline validation: red border + message appears when field loses focus and is invalid
79
+ - Clear error when user starts correcting
80
+
81
+ ## Loading Patterns
82
+
83
+ | Pattern | Use When | Why |
84
+ | ---------------- | --------------------------------- | -------------------------------------- |
85
+ | Skeleton screens | Page/component loading | Previews content shape, feels faster |
86
+ | Inline spinner | Button action processing | Keeps context, shows progress |
87
+ | Progress bar | Known duration (upload, download) | Sets expectations |
88
+ | Optimistic UI | Low-stakes actions (like, toggle) | Update immediately, sync in background |
89
+
90
+ **Avoid**: Full-page spinners, blocking modals for non-destructive actions.
91
+
92
+ ## Undo Over Confirmation
93
+
94
+ Users click through confirmation dialogs mindlessly. Prefer undo:
95
+
96
+ ```
97
+ ❌ "Are you sure you want to delete?" → [Cancel] [Delete]
98
+ ✅ Item deleted. [Undo] (5 second window)
99
+ ```
100
+
101
+ Exception: irreversible actions with severe consequences (account deletion, payment).
102
+
103
+ ## Touch Target Expansion
104
+
105
+ Visual size can be small; tap target must be 44x44px minimum:
106
+
107
+ ```css
108
+ .icon-button {
109
+ width: 24px;
110
+ height: 24px;
111
+ position: relative;
112
+ }
113
+
114
+ .icon-button::before {
115
+ content: "";
116
+ position: absolute;
117
+ inset: -10px; /* Expands tap area to 44x44 */
118
+ }
119
+ ```
120
+
121
+ ## Scroll Behavior
122
+
123
+ ```css
124
+ html {
125
+ scroll-behavior: smooth;
126
+ scroll-padding-top: 80px; /* Account for sticky header */
127
+ }
128
+
129
+ @media (prefers-reduced-motion: reduce) {
130
+ html {
131
+ scroll-behavior: auto;
132
+ }
133
+ }
134
+ ```
135
+
136
+ ## Z-Index Scale
137
+
138
+ Named semantic layers prevent z-index wars:
139
+
140
+ ```css
141
+ @theme {
142
+ --z-dropdown: 10;
143
+ --z-sticky: 20;
144
+ --z-modal-backdrop: 30;
145
+ --z-modal: 40;
146
+ --z-toast: 50;
147
+ --z-tooltip: 60;
148
+ }
149
+ ```
@@ -0,0 +1,106 @@
1
+ # Typography Rules
2
+
3
+ ## Core Principles
4
+
5
+ Typography carries hierarchy before color or decoration. Prioritize readability, rhythm, and intent.
6
+
7
+ - Use a single type scale ratio across the interface
8
+ - Keep body text highly readable before styling display text
9
+ - Limit font families: one display + one body (optional mono for code/data)
10
+ - Use weight and size for hierarchy before using color
11
+
12
+ ## Fluid Type with clamp()
13
+
14
+ Use fluid sizing for responsive typography without breakpoint jumps:
15
+
16
+ ```css
17
+ @theme {
18
+ --text-xs: clamp(0.75rem, 0.72rem + 0.15vw, 0.8125rem);
19
+ --text-sm: clamp(0.875rem, 0.84rem + 0.2vw, 0.9375rem);
20
+ --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
21
+ --text-lg: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
22
+ --text-xl: clamp(1.5rem, 1.35rem + 0.75vw, 2rem);
23
+ }
24
+ ```
25
+
26
+ Never use fixed `px` for body text.
27
+
28
+ ## Modular Scale
29
+
30
+ Pick one ratio and stick to it:
31
+
32
+ - **1.25** (major third) for practical UI
33
+ - **1.333** (perfect fourth) for editorial styles
34
+
35
+ Limit to 5-7 text sizes. Too many sizes destroys rhythm.
36
+
37
+ ## Line Length and Rhythm
38
+
39
+ - Body measure: `max-width: 65ch`
40
+ - Comfortable line height: `1.45-1.7` for body text
41
+ - Tight headings: `1.05-1.25`
42
+ - Keep spacing tied to text rhythm (4pt system)
43
+
44
+ ```css
45
+ .article {
46
+ font-size: var(--text-base);
47
+ line-height: 1.6;
48
+ max-width: 65ch;
49
+ }
50
+ ```
51
+
52
+ ## Font Selection
53
+
54
+ Avoid default AI fingerprints:
55
+
56
+ - Banned as primary display fonts: Inter, Roboto, Arial, Open Sans, Lato, Montserrat, Space Grotesk, system-ui
57
+
58
+ Preferred directions:
59
+
60
+ - Sans: Instrument Sans, Plus Jakarta Sans, Outfit, Onest, Figtree, Urbanist
61
+ - Editorial serif: Fraunces, Newsreader
62
+
63
+ ## OpenType Features
64
+
65
+ Use OpenType intentionally:
66
+
67
+ ```css
68
+ .data-table {
69
+ font-variant-numeric: tabular-nums;
70
+ }
71
+
72
+ .recipe {
73
+ font-variant-numeric: diagonal-fractions;
74
+ }
75
+
76
+ .abbrev {
77
+ font-variant-caps: all-small-caps;
78
+ letter-spacing: 0.04em;
79
+ }
80
+ ```
81
+
82
+ ## Dark Mode Typography
83
+
84
+ Light-on-dark text appears heavier. Adjust:
85
+
86
+ - Increase line height by `+0.05` to `+0.1`
87
+ - Reduce font weight if text feels too dense
88
+ - Avoid pure white text; use slightly tinted near-white
89
+
90
+ ```css
91
+ @media (prefers-color-scheme: dark) {
92
+ body {
93
+ line-height: 1.65;
94
+ color: oklch(0.93 0.01 264);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Quick Audit Checklist
100
+
101
+ - [ ] Body text uses `rem` or `em`, not `px`
102
+ - [ ] Type scale uses one ratio (1.25 or 1.333)
103
+ - [ ] Body lines capped around 65ch
104
+ - [ ] Headings and body have distinct line-height behavior
105
+ - [ ] OpenType features used for data/fractions/abbreviations where relevant
106
+ - [ ] Dark mode typography adjusted (not just color-inverted)
@@ -0,0 +1,99 @@
1
+ # UX Writing
2
+
3
+ ## Button Labels
4
+
5
+ Specific verb + object. Never generic:
6
+
7
+ | Bad | Good | Why |
8
+ | ---------- | --------------- | ------------------------------ |
9
+ | OK | Save changes | Says what happens |
10
+ | Submit | Create account | Describes the outcome |
11
+ | Yes | Delete message | Confirms what gets deleted |
12
+ | Cancel | Keep editing | Tells user what "cancel" means |
13
+ | Click here | Download report | Verb + object, no "click" |
14
+
15
+ ## Error Messages
16
+
17
+ Three-part formula — every time:
18
+
19
+ 1. **What happened** (not "Error occurred")
20
+ 2. **Why** (if known)
21
+ 3. **How to fix**
22
+
23
+ ```
24
+
25
+ Bad: "Error: Invalid input"
26
+ Bad: "Something went wrong"
27
+ Bad: "Oops! That didn't work"
28
+
29
+ Good: "Email address is invalid. Check for typos or use a different address."
30
+ Good: "Payment declined — your card issuer rejected the charge. Try a different card or contact your bank."
31
+ Good: "File too large (52 MB). Maximum size is 25 MB. Compress the file or split it into parts."
32
+ ```
33
+
34
+ **Never use humor for errors.** Users are frustrated — be helpful, not cute.
35
+
36
+ ## Empty States
37
+
38
+ Empty states are **onboarding opportunities**, not blank screens:
39
+
40
+ 1. **Acknowledge**: "No messages yet"
41
+ 2. **Explain value**: "Messages from your team will appear here"
42
+ 3. **Clear action**: [Start a conversation]
43
+
44
+ ```
45
+
46
+ Bad: (blank white space)
47
+ Bad: "No data"
48
+ Bad: "Nothing to show"
49
+
50
+ Good: "No projects yet — Create your first project to get started" [Create project]
51
+ ```
52
+
53
+ ## Confirmation vs Information
54
+
55
+ | Type | Pattern | Example |
56
+ | ----------- | ----------------------------------------- | -------------------------------------- |
57
+ | Success | Brief toast (auto-dismiss 3-5s) | "Changes saved" |
58
+ | Warning | Inline banner (persistent, dismissible) | "Your trial ends in 3 days" |
59
+ | Error | Inline at source (persistent until fixed) | "Password must be 8+ characters" |
60
+ | Destructive | Confirmation dialog (requires action) | "Delete account? This can't be undone" |
61
+
62
+ ## Consistent Terminology
63
+
64
+ Pick ONE term and use it everywhere:
65
+
66
+ | Pick one | Not these |
67
+ | ---------------- | ----------------------------------- |
68
+ | Delete | Remove, Trash, Erase, Destroy |
69
+ | Settings | Preferences, Options, Configuration |
70
+ | Log in / Log out | Sign in / Sign out (pick a pair) |
71
+ | Save | Submit, Apply, Update, Confirm |
72
+
73
+ ## Voice vs Tone
74
+
75
+ - **Voice** = consistent brand personality (always the same)
76
+ - **Tone** = adapts to moment:
77
+ - Success → celebratory, brief
78
+ - Error → empathetic, helpful
79
+ - Onboarding → encouraging, clear
80
+ - Warning → direct, specific
81
+
82
+ ## Internationalization
83
+
84
+ - **Plan for 30% text expansion** — German, French, Finnish expand significantly
85
+ - Design layouts with flexible text containers
86
+ - Never truncate translated text without tooltip/expansion
87
+ - Use ICU message format for plurals: `{count, plural, one {# item} other {# items}}`
88
+ - Right-to-left (RTL) support: use logical properties (`margin-inline-start` not `margin-left`)
89
+
90
+ ## Microcopy Checklist
91
+
92
+ - [ ] All buttons use verb + object
93
+ - [ ] All errors follow 3-part formula
94
+ - [ ] All empty states have acknowledgment + value + action
95
+ - [ ] Terminology is consistent across all screens
96
+ - [ ] No humor in error states
97
+ - [ ] No "click here" or "click below"
98
+ - [ ] No ALL CAPS for body text (small-caps for abbreviations only)
99
+ - [ ] Loading states have context ("Loading messages..." not "Loading...")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencodekit",
3
- "version": "0.18.9",
3
+ "version": "0.18.10",
4
4
  "description": "CLI tool for bootstrapping and managing OpenCodeKit projects",
5
5
  "keywords": [
6
6
  "agents",
File without changes