Merge pull request #38 from pbakaus/feat/typeset-arrange-skills

Add /typeset and /arrange skills
This commit is contained in:
Paul Bakaus
2026-03-16 16:11:42 -07:00
committed by GitHub
30 changed files with 1391 additions and 44 deletions
+3 -3
View File
@@ -2,7 +2,7 @@
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "impeccable",
"metadata": {
"description": "Design fluency for AI harnesses. 1 skill, 17 commands, and curated anti-patterns for impeccable frontend design."
"description": "Design fluency for AI harnesses. 1 skill, 20 commands, and curated anti-patterns for impeccable frontend design."
},
"owner": {
"name": "Paul Bakaus",
@@ -11,8 +11,8 @@
"plugins": [
{
"name": "impeccable",
"description": "Design vocabulary and skills for frontend development. Includes 17 commands (/polish, /distill, /audit, /bolder, /quieter, etc.) and an enhanced frontend-design skill with curated anti-patterns.",
"version": "1.3.0",
"description": "Design vocabulary and skills for frontend development. Includes 20 commands (/polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced frontend-design skill with curated anti-patterns.",
"version": "1.5.0",
"author": {
"name": "Paul Bakaus",
"email": "paul@paulbakaus.com"
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "impeccable",
"description": "Design vocabulary and skills for frontend development. Includes 18 skills (17 user-invokable: /polish, /distill, /audit, /bolder, /quieter, etc.) and an enhanced frontend-design skill with curated anti-patterns.",
"version": "1.3.0",
"description": "Design vocabulary and skills for frontend development. Includes 21 skills (20 user-invokable: /polish, /distill, /audit, /typeset, /overdrive, etc.) and an enhanced frontend-design skill with curated anti-patterns.",
"version": "1.5.0",
"author": {
"name": "Paul Bakaus",
"email": "paul@paulbakaus.com"
+127
View File
@@ -0,0 +1,127 @@
---
name: arrange
description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy to create intentional compositions.
user-invokable: true
args:
- name: target
description: The feature or component to improve layout for (optional)
required: false
---
Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
---
## Assess Current Layout
Analyze what's weak about the current spatial design:
1. **Spacing**:
- Is spacing consistent or arbitrary? (Random padding/margin values)
- Is all spacing the same? (Equal padding everywhere = no rhythm)
- Are related elements grouped tightly, with generous space between groups?
2. **Visual hierarchy**:
- Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
- Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
- Does whitespace guide the eye to what matters?
3. **Grid & structure**:
- Is there a clear underlying structure, or does the layout feel random?
- Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
- Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
4. **Rhythm & variety**:
- Does the layout have visual rhythm? (Alternating tight/generous spacing)
- Is every section structured the same way? (Monotonous repetition)
- Are there intentional moments of surprise or emphasis?
5. **Density**:
- Is the layout too cramped? (Not enough breathing room)
- Is the layout too sparse? (Excessive whitespace without purpose)
- Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
## Plan Layout Improvements
Consult the [spatial design reference](reference/spatial-design.md) from the frontend-design skill for detailed guidance on grids, rhythm, and container queries.
Create a systematic plan:
- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
- **Hierarchy strategy**: How will space communicate importance?
- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
- **Rhythm**: Where should spacing be tight vs generous?
## Improve Layout Systematically
### Establish a Spacing System
- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
- Apply `clamp()` for fluid spacing that breathes on larger screens
### Create Visual Rhythm
- **Tight grouping** for related elements (8-12px between siblings)
- **Generous separation** between distinct sections (48-96px)
- **Varied spacing** within sections — not every row needs the same gap
- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
### Choose the Right Layout Tool
- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
### Break Card Grid Monotony
- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
- Use cards only when content is truly distinct and actionable — never nest cards inside cards
- Vary card sizes, span columns, or mix cards with non-card content to break repetition
### Strengthen Visual Hierarchy
- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
- Create clear content groupings through proximity and separation.
### Manage Depth & Elevation
- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
- Use elevation to reinforce hierarchy, not as decoration
### Optical Adjustments
- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
**NEVER**:
- Use arbitrary spacing values outside your scale
- Make all spacing equal — variety creates hierarchy
- Wrap everything in cards — not everything needs a container
- Nest cards inside cards — use spacing and dividers for hierarchy within
- Use identical card grids everywhere (icon + heading + text, repeated)
- Center everything — left-aligned with asymmetry feels more designed
- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
- Use arbitrary z-index values (999, 9999) — build a semantic scale
## Verify Layout Improvements
- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
- **Hierarchy**: Is the most important content obvious within 2 seconds?
- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
- **Consistency**: Is the spacing system applied uniformly?
- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.
+2 -2
View File
@@ -71,7 +71,7 @@ For each issue, document:
- **Impact**: How it affects users
- **WCAG/Standard**: Which standard it violates (if applicable)
- **Recommendation**: How to fix it
- **Suggested command**: Which command to use (prefer: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /critique, /colorize — or other installed skills you're sure exist)
- **Suggested command**: Which command to use (prefer: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /arrange, /typeset, /critique, /colorize, /overdrive — or other installed skills you're sure exist)
#### Critical Issues
[Issues that block core functionality or violate WCAG A]
@@ -108,7 +108,7 @@ Create actionable plan:
### Suggested Commands for Fixes
Map issues to available commands. Prefer these: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /critique, /colorize. You may also suggest other installed skills you're sure exist, but never invent commands.
Map issues to available commands. Prefer these: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /arrange, /typeset, /critique, /colorize, /overdrive. You may also suggest other installed skills you're sure exist, but never invent commands.
Examples:
- "Use `/normalize` to align with design system (addresses N theming issues)"
+1 -1
View File
@@ -102,7 +102,7 @@ For each issue:
- **What**: Name the problem clearly
- **Why it matters**: How this hurts users or undermines goals
- **Fix**: What to do about it (be concrete)
- **Command**: Which command to use (prefer: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /critique, /colorize — or other installed skills you're sure exist)
- **Command**: Which command to use (prefer: /animate, /quieter, /optimize, /adapt, /clarify, /distill, /delight, /onboard, /normalize, /audit, /harden, /polish, /extract, /bolder, /arrange, /typeset, /critique, /colorize, /overdrive — or other installed skills you're sure exist)
### Minor Observations
Quick notes on smaller issues worth addressing.
+144
View File
@@ -0,0 +1,144 @@
---
name: overdrive
description: Push interfaces past conventional limits with technically ambitious implementations. Whether that's a shader, a 60fps virtual table, spring physics on a dialog, or scroll-driven reveals — make users ask "how did they do that?"
user-invokable: true
args:
- name: target
description: The feature or area to push into overdrive (optional)
required: false
---
Start your response with:
```
──────────── ⚡ OVERDRIVE ─────────────
》》》 Entering overdrive mode...
```
Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
### Propose Before Building
This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
2. **STOP and call the AskUserQuestion tool to clarify.** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
3. Only proceed with the direction the user confirms.
Skipping this step risks building something embarrassing that needs to be thrown away.
### Iterate with Browser Automation
Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
---
## Assess What "Extraordinary" Means Here
The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
### For visual/marketing surfaces
Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
### For functional UI
Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
### For performance-critical UI
The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
### For data-heavy interfaces
Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
## The Toolkit
Organized by what you're trying to achieve, not by technology name.
### Make transitions feel cinematic
- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
### Tie animation to scroll position
- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
### Render beyond CSS
- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
### Make data feel alive
- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
### Animate complex properties
- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
### Push performance boundaries
- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
### Interact with the device
- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
## Implement with Discipline
### Progressive enhancement is non-negotiable
Every technique must degrade gracefully. The experience without the enhancement must still be good.
```css
@supports (animation-timeline: scroll()) {
.hero { animation-timeline: scroll(); }
}
```
```javascript
if ('gpu' in navigator) { /* WebGPU */ }
else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
/* CSS-only fallback must still look good */
```
### Performance rules
- Target 60fps. If dropping below 50, simplify.
- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
- Pause off-screen rendering. Kill what you can't see.
- Test on real mid-range devices, not just your development machine.
### Polish is the difference
The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
**NEVER**:
- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
- Ship effects that cause jank on mid-range devices
- Use bleeding-edge APIs without a functional fallback
- Add sound without explicit user opt-in
- Use technical ambition to mask weak design fundamentals — fix those first with other skills
- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
## Verify the Result
- **The wow test**: Show it to someone who hasn't seen it. Do they react?
- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
- **The accessibility test**: Enable reduced motion. Still beautiful?
- **The context test**: Does this make sense for THIS brand and audience?
Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.
+117
View File
@@ -0,0 +1,117 @@
---
name: typeset
description: Improve typography by fixing font choices, hierarchy, sizing, weight consistency, and readability. Makes text feel intentional and polished.
user-invokable: true
args:
- name: target
description: The feature or component to improve typography for (optional)
required: false
---
Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
---
## Assess Current Typography
Analyze what's weak or generic about the current type:
1. **Font choices**:
- Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
- Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
- Are there too many font families? (More than 2-3 is almost always a mess)
2. **Hierarchy**:
- Can you tell headings from body from captions at a glance?
- Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
- Are weight contrasts strong enough? (Medium vs Regular is barely visible)
3. **Sizing & scale**:
- Is there a consistent type scale, or are sizes arbitrary?
- Does body text meet minimum readability? (16px+)
- Is fluid sizing used, or do sizes jump at breakpoints?
4. **Readability**:
- Are line lengths comfortable? (45-75 characters ideal)
- Is line-height appropriate for the font and context?
- Is there enough contrast between text and background?
5. **Consistency**:
- Are the same elements styled the same way throughout?
- Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
- Is letter-spacing intentional or default everywhere?
**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
## Plan Typography Improvements
Consult the [typography reference](reference/typography.md) from the frontend-design skill for detailed guidance on scales, pairing, and loading strategies.
Create a systematic plan:
- **Font selection**: Do fonts need replacing? What fits the brand/context?
- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
## Improve Typography Systematically
### Font Selection
If fonts need replacing:
- Choose fonts that reflect the brand personality
- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
### Establish Hierarchy
Build a clear type scale:
- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
- **Use fluid sizing**: `clamp(min, preferred, max)` for smooth scaling
### Fix Readability
- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
- Increase line-height slightly for light-on-dark text
- Ensure body text is at least 16px / 1rem
### Refine Details
- Use `tabular-nums` for data tables and numbers that should align
- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
- Set `font-kerning: normal` and consider OpenType features where appropriate
### Weight Consistency
- Define clear roles for each weight and stick to them
- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
- Load only the weights you actually use (each weight adds to page load)
**NEVER**:
- Use more than 2-3 font families
- Pick sizes arbitrarily — commit to a scale
- Set body text below 16px
- Use decorative/display fonts for body text
- Disable browser zoom (`user-scalable=no`)
- Use `px` for font sizes — use `rem` to respect user settings
- Default to Inter/Roboto/Open Sans when personality matters
- Pair fonts that are similar but not identical (two geometric sans-serifs)
## Verify Typography Improvements
- **Hierarchy**: Can you identify heading vs body vs caption instantly?
- **Readability**: Is body text comfortable to read in long passages?
- **Consistency**: Are same-role elements styled identically throughout?
- **Personality**: Does the typography reflect the brand?
- **Performance**: Are web fonts loading efficiently without layout shift?
- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.
+3
View File
@@ -19,3 +19,6 @@ Thumbs.db
# Environment
.env
.env.local
# Cloudflare
.wrangler/
+1 -1
View File
@@ -1,6 +1,6 @@
# Impeccable
The vocabulary you didn't know you needed. 1 skill, 17 commands, and curated anti-patterns for impeccable style. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.
The vocabulary you didn't know you needed. 1 skill, 20 commands, and curated anti-patterns for impeccable style. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.
## Repository Purpose
+15
View File
@@ -50,3 +50,18 @@ When bumping the version, update **all** of these locations to keep them in sync
- `.claude-plugin/plugin.json``version`
- `.claude-plugin/marketplace.json``plugins[0].version`
- `public/index.html` → hero version link text + new changelog entry
## Adding New Skills
When adding a new user-invokable skill, update the command count in **all** of these locations:
- `public/index.html` → meta descriptions, hero box, section lead
- `public/cheatsheet.html` → meta description, subtitle, `commandCategories`, `commandRelationships`
- `public/js/data.js``commandProcessSteps`, `commandCategories`, `commandRelationships`
- `public/js/components/framework-viz.js``commandSymbols`, `commandNumbers`
- `public/js/demos/commands/` → new demo file + import in `index.js`
- `README.md` → intro, command count, commands table
- `NOTICE.md` → steering commands count
- `AGENTS.md` → intro command count
- `.claude-plugin/plugin.json` → description
- `.claude-plugin/marketplace.json` → metadata description + plugin description
+1 -1
View File
@@ -13,5 +13,5 @@ The `frontend-design` skill in this project builds on Anthropic's original front
This project extends the original with:
- 7 domain-specific reference files (typography, color-and-contrast, spatial-design, motion-design, interaction-design, responsive-design, ux-writing)
- 17 steering commands
- 20 steering commands
- Expanded patterns and anti-patterns
+6 -3
View File
@@ -1,6 +1,6 @@
# Impeccable
The vocabulary you didn't know you needed. 1 skill, 17 commands, and curated anti-patterns for impeccable frontend design.
The vocabulary you didn't know you needed. 1 skill, 20 commands, and curated anti-patterns for impeccable frontend design.
> **Quick start:** Visit [impeccable.style](https://impeccable.style) to download ready-to-use bundles.
@@ -12,7 +12,7 @@ Every LLM learned from the same generic templates. Without guidance, you get the
Impeccable fights that bias with:
- **An expanded skill** with 7 domain-specific reference files ([view source](source/skills/frontend-design/))
- **17 steering commands** to audit, review, polish, distill, animate, and more
- **20 steering commands** to audit, review, polish, distill, animate, and more
- **Curated anti-patterns** that explicitly tell the AI what NOT to do
## What's Included
@@ -31,7 +31,7 @@ A comprehensive design skill with 7 domain-specific references ([view skill](sou
| [responsive-design](source/skills/frontend-design/reference/responsive-design.md) | Mobile-first, fluid design, container queries |
| [ux-writing](source/skills/frontend-design/reference/ux-writing.md) | Button labels, error messages, empty states |
### 17 Commands
### 20 Commands
| Command | What it does |
|---------|--------------|
@@ -52,6 +52,9 @@ A comprehensive design skill with 7 domain-specific references ([view skill](sou
| `/extract` | Pull into reusable components |
| `/adapt` | Adapt for different devices |
| `/onboard` | Design onboarding flows |
| `/typeset` | Fix font choices, hierarchy, sizing |
| `/arrange` | Fix layout, spacing, visual rhythm |
| `/overdrive` | Add technically extraordinary effects |
### Anti-Patterns
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "impeccable",
"version": "1.3.0",
"version": "1.5.0",
"author": "Paul Bakaus",
"dependencies": {
"archiver": "^7.0.1",
+13 -6
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impeccable Command Cheatsheet</title>
<meta name="description" content="Quick reference for all 17 Impeccable commands. Print-friendly cheatsheet for design fluency in AI harnesses.">
<meta name="description" content="Quick reference for all 20 Impeccable commands. Print-friendly cheatsheet for design fluency in AI harnesses.">
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -160,7 +160,7 @@
<body>
<header>
<h1>Impeccable Commands</h1>
<p class="subtitle">Quick reference for all 17 design commands</p>
<p class="subtitle">Quick reference for all 20 design commands</p>
<a href="/" class="back-link">&larr; Back to impeccable.style</a>
</header>
@@ -201,13 +201,16 @@
'delight': 'enhancement',
'extract': 'system',
'adapt': 'adaptation',
'onboard': 'enhancement'
'onboard': 'enhancement',
'typeset': 'enhancement',
'arrange': 'enhancement',
'overdrive': 'enhancement'
};
const commandRelationships = {
'teach-impeccable': { flow: 'One-time project context gathering' },
'audit': { leadsTo: ['normalize', 'harden', 'optimize', 'adapt', 'clarify'], flow: 'Technical quality audit' },
'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter'], flow: 'UX and design review' },
'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter', 'typeset', 'arrange'], flow: 'UX and design review' },
'normalize': { combinesWith: ['clarify', 'adapt'], flow: 'Align with design system' },
'polish': { flow: 'Final pass before shipping' },
'optimize': { flow: 'Performance improvements' },
@@ -221,7 +224,10 @@
'delight': { combinesWith: ['bolder', 'animate'], flow: 'Add personality' },
'extract': { flow: 'Create design system elements' },
'adapt': { combinesWith: ['normalize', 'clarify'], flow: 'Different devices/contexts' },
'onboard': { combinesWith: ['clarify', 'delight'], flow: 'Onboarding & empty states' }
'onboard': { combinesWith: ['clarify', 'delight'], flow: 'Onboarding & empty states' },
'typeset': { combinesWith: ['bolder', 'normalize'], flow: 'Fix typography' },
'arrange': { combinesWith: ['distill', 'adapt'], flow: 'Fix layout & spacing' },
'overdrive': { combinesWith: ['animate', 'delight'], flow: 'Technically extraordinary effects' }
};
async function loadCommands() {
@@ -261,8 +267,9 @@
}
}
const isBeta = cmd.id === 'overdrive';
html += `<div class="command">
<div class="command-name">/${cmd.id}</div>
<div class="command-name">/${cmd.id}${isBeta ? ' <span style="font-size:0.55rem;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:var(--color-accent,#c8956c);border:1px solid;border-radius:3px;padding:1px 5px;vertical-align:middle;margin-left:4px;">BETA</span>' : ''}</div>
<div class="command-info">
<div class="command-desc">${cmd.description}</div>
${metaHtml ? `<div class="command-meta">${metaHtml}</div>` : ''}
File diff suppressed because one or more lines are too long
+14
View File
@@ -78,6 +78,20 @@
font-weight: 500;
}
.beta-badge {
font-family: var(--font-body);
font-size: 0.55rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-accent);
border: 1px solid var(--color-accent);
border-radius: 3px;
padding: 1px 5px;
vertical-align: middle;
margin-left: 6px;
}
.manual-cmd-desc {
font-size: 1rem;
line-height: 1.6;
+18 -6
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Impeccable: The missing upgrade to Anthropic's frontend-design skill</title>
<meta name="description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
<meta name="description" content="1 skill, 20 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
<meta name="theme-color" content="#1a1a1a">
<link rel="canonical" href="https://impeccable.style">
@@ -12,7 +12,7 @@
<meta property="og:type" content="website">
<meta property="og:url" content="https://impeccable.style">
<meta property="og:title" content="Impeccable: Design skills for AI harnesses">
<meta property="og:description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
<meta property="og:description" content="1 skill, 20 commands, and curated anti-patterns for impeccable frontend design. Works with Cursor, Claude Code, Gemini CLI, and Codex CLI.">
<meta property="og:image" content="https://impeccable.style/og-image.jpg">
<!-- Twitter -->
@@ -20,7 +20,7 @@
<meta name="twitter:site" content="@pbakaus">
<meta name="twitter:creator" content="@pbakaus">
<meta name="twitter:title" content="Impeccable: Design skills for AI harnesses">
<meta name="twitter:description" content="1 skill, 17 commands, and curated anti-patterns for impeccable frontend design.">
<meta name="twitter:description" content="1 skill, 20 commands, and curated anti-patterns for impeccable frontend design.">
<meta name="twitter:image" content="https://impeccable.style/og-image.jpg">
<link rel="icon" type="image/svg+xml" href="./favicon.svg">
@@ -57,7 +57,7 @@
<div class="hero-included-items">
<span>Enhanced <em>frontend-design</em> skill + anti-patterns</span>
<span class="hero-included-sep">·</span>
<span>17 design skills: /polish, /audit, /distill, /bolder...</span>
<span>20 design skills: /polish, /audit, /typeset, /overdrive...</span>
</div>
</div>
@@ -78,7 +78,7 @@
</div>
</div>
</div>
<p class="hero-version-link"><a href="#changelog">v1.3.0 — OpenCode & Pi support</a></p>
<p class="hero-version-link"><a href="#changelog">v1.5.0 — /typeset, /arrange, /overdrive</a></p>
</div>
<!-- Right: Before/After Demo -->
@@ -162,7 +162,7 @@
<h2 class="section-title">The Framework</h2>
</div>
<div class="solution-content">
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 17 commands that form the language of design.</p>
<p class="section-lead" data-reveal>One comprehensive skill with deep expertise, plus 20 commands that form the language of design.</p>
<div class="solution-visual-interactive" id="framework-viz-container" data-reveal>
<!-- Subway map generated by JS -->
@@ -310,6 +310,18 @@
</div>
<div class="changelog-list" data-reveal>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.5.0</span>
<span class="changelog-date">March 16, 2026</span>
</div>
<ul class="changelog-items">
<li>3 new skills: <code>/typeset</code> (fix typography), <code>/arrange</code> (fix layout & spacing), <code>/overdrive</code> (technically extraordinary effects, beta)</li>
<li>Skills now auto-gather design context via <code>.impeccable.md</code> — run <code>/teach-impeccable</code> once, all skills benefit</li>
<li>Deep linking to commands (<code>#cmd-overdrive</code>, etc.)</li>
</ul>
</div>
<div class="changelog-entry">
<div class="changelog-version-header">
<span class="changelog-version">v1.3.0</span>
+27 -3
View File
@@ -3,7 +3,7 @@
* Clean grid visualization showing all commands organized by category
*/
import { commandCategories, commandRelationships } from '../data.js';
import { commandCategories, commandRelationships, betaCommands } from '../data.js';
// Colors now reference CSS custom properties for dark mode support
const categoryColors = {
@@ -42,7 +42,10 @@ const commandSymbols = {
delight: 'De',
bolder: 'Bo',
quieter: 'Qu',
onboard: 'On'
onboard: 'On',
typeset: 'Ty',
arrange: 'Ar',
overdrive: 'Od'
};
// Atomic numbers (just for visual interest)
@@ -63,7 +66,10 @@ const commandNumbers = {
delight: 13,
bolder: 14,
quieter: 15,
onboard: 16
onboard: 16,
typeset: 17,
arrange: 18,
overdrive: 19
};
export class PeriodicTable {
@@ -328,6 +334,24 @@ export class PeriodicTable {
name.textContent = `/${cmd}`;
el.appendChild(name);
// Beta badge
if (betaCommands.includes(cmd)) {
const badge = document.createElement('div');
badge.style.cssText = `
position: absolute;
top: 2px;
right: 3px;
font-family: var(--font-mono);
font-size: 5px;
letter-spacing: 0.05em;
color: ${colors.text};
opacity: 0.45;
text-transform: uppercase;
`;
badge.textContent = 'β';
el.appendChild(badge);
}
// Shared handler for activation (hover or focus)
const activate = () => {
// Visual feedback
+10 -3
View File
@@ -1,6 +1,6 @@
import { renderCommandDemo } from "../demo-renderer.js";
import { renderCommandDemo, initCommandDemo } from "../demo-renderer.js";
import { initSplitCompare } from "../effects/split-compare.js";
import { commandProcessSteps, commandCategories, commandRelationships } from "../data.js";
import { commandProcessSteps, commandCategories, commandRelationships, betaCommands } from "../data.js";
// Track current split instance and command for cleanup
let currentSplitInstance = null;
@@ -141,9 +141,11 @@ function renderManualEntry(cmd) {
}
}
const isBeta = betaCommands.includes(cmd.id);
return `
<div class="manual-entry" data-id="${cmd.id}" id="cmd-${cmd.id}">
<h3 class="manual-cmd-name">/${cmd.id}</h3>
<h3 class="manual-cmd-name">/${cmd.id}${isBeta ? ' <span class="beta-badge">BETA</span>' : ''}</h3>
<p class="manual-cmd-desc">${cmd.description}</p>
${relationshipHTML}
</div>
@@ -164,6 +166,7 @@ function setupDesktopScrollSpy(commands) {
const cmd = commands.find(c => c.id === cmdId);
if (cmd) {
updateTerminal(cmd, terminalContent, commands);
history.replaceState(null, '', `#cmd-${cmdId}`);
}
}
});
@@ -183,6 +186,7 @@ function setupDesktopScrollSpy(commands) {
const cmd = commands.find(c => c.id === cmdId);
if (cmd) {
updateTerminal(cmd, terminalContent, commands);
history.replaceState(null, '', `#cmd-${cmdId}`);
}
e.scrollIntoView({ behavior: 'smooth', block: 'center' });
@@ -223,6 +227,7 @@ function updateTerminal(cmd, container, allCommands) {
});
}
initCommandDemo(cmd.id, container);
}
// ============================================
@@ -293,6 +298,7 @@ function setupMobileInteractions(commands) {
maxPosition: 90
});
}
if (commands[0]) initCommandDemo(commands[0].id, demoArea);
// Pill click/tap handler
pills.forEach(pill => {
@@ -334,6 +340,7 @@ function setupMobileInteractions(commands) {
});
}
initCommandDemo(cmdId, demoArea);
});
});
}
+18 -4
View File
@@ -12,6 +12,11 @@ export const readyCommands = [
'normalize' // First command to be fully completed
];
// Commands marked as beta — shown with a badge in the UI
export const betaCommands = [
'overdrive'
];
// Consolidated frontend-design skill with reference domains
export const skillFocusAreas = {
'frontend-design': [
@@ -53,7 +58,10 @@ export const commandProcessSteps = {
'delight': ['Identify', 'Design', 'Implement'],
'extract': ['Identify', 'Abstract', 'Document'],
'adapt': ['Analyze', 'Adjust', 'Optimize'],
'onboard': ['Map', 'Design', 'Guide']
'onboard': ['Map', 'Design', 'Guide'],
'typeset': ['Assess', 'Select', 'Scale', 'Refine'],
'arrange': ['Assess', 'Grid', 'Rhythm', 'Balance'],
'overdrive': ['Assess', 'Choose', 'Build', 'Polish']
};
export const commandCategories = {
@@ -73,7 +81,10 @@ export const commandCategories = {
'delight': 'enhancement',
'extract': 'system',
'adapt': 'adaptation',
'onboard': 'enhancement'
'onboard': 'enhancement',
'typeset': 'enhancement',
'arrange': 'enhancement',
'overdrive': 'enhancement'
};
// Skill relationships - now consolidated into frontend-design skill
@@ -88,7 +99,7 @@ export const skillRelationships = {
export const commandRelationships = {
'teach-impeccable': { flow: 'Setup: One-time project context gathering' },
'audit': { leadsTo: ['normalize', 'harden', 'optimize', 'adapt', 'clarify'], flow: 'Diagnostic: Technical quality audit' },
'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter'], flow: 'Diagnostic: UX and design review' },
'critique': { leadsTo: ['polish', 'distill', 'bolder', 'quieter', 'typeset', 'arrange'], flow: 'Diagnostic: UX and design review' },
'normalize': { combinesWith: ['clarify', 'adapt'], flow: 'Quality: Align with design system' },
'polish': { flow: 'Quality: Final pass before shipping' },
'optimize': { flow: 'Quality: Performance improvements' },
@@ -102,6 +113,9 @@ export const commandRelationships = {
'delight': { combinesWith: ['bolder', 'animate'], flow: 'Enhancement: Add personality' },
'extract': { flow: 'System: Create design system elements' },
'adapt': { combinesWith: ['normalize', 'clarify'], flow: 'Adaptation: Different devices/contexts' },
'onboard': { combinesWith: ['clarify', 'delight'], flow: 'Enhancement: Onboarding & empty states' }
'onboard': { combinesWith: ['clarify', 'delight'], flow: 'Enhancement: Onboarding & empty states' },
'typeset': { combinesWith: ['bolder', 'normalize'], flow: 'Enhancement: Fix typography' },
'arrange': { combinesWith: ['distill', 'adapt'], flow: 'Enhancement: Fix layout & spacing' },
'overdrive': { combinesWith: ['animate', 'delight'], flow: 'Enhancement: Technically extraordinary effects' }
};
+13
View File
@@ -5,6 +5,19 @@
import { getCommandDemo } from './demos/commands/index.js';
import { getSkillDemo } from './demos/skills/index.js';
/**
* Initialize a command demo's JS after its HTML has been inserted into the DOM.
* Call this after innerHTML is set and split compare is initialized.
*/
export function initCommandDemo(commandId, container) {
const demo = getCommandDemo(commandId);
if (demo && typeof demo.init === 'function') {
const demoArea = container.querySelector('.split-after .split-content') || container;
console.log('[initCommandDemo]', commandId, 'demoArea:', demoArea);
demo.init(demoArea);
}
}
/**
* Render a command demo with split-screen comparison
*/
+47
View File
@@ -0,0 +1,47 @@
// Arrange command demo - shows monotonous equal spacing becoming rhythmic and intentional
export default {
id: 'arrange',
caption: 'Equal spacing everywhere → Intentional rhythm and hierarchy',
before: `
<div style="width: 100%; max-width: 240px; padding: 16px;">
<div style="text-align: center; margin-bottom: 16px;">
<div style="font-size: 14px; font-weight: bold; color: #333;">Team Members</div>
</div>
<div style="display: flex; flex-direction: column; gap: 16px;">
<div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
<div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
<div style="font-size: 13px; color: #333;">Alice Chen</div>
<div style="font-size: 12px; color: #888;">Designer</div>
</div>
<div style="padding: 16px; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center;">
<div style="width: 32px; height: 32px; background: #ddd; border-radius: 50%; margin: 0 auto 8px;"></div>
<div style="font-size: 13px; color: #333;">Bob Park</div>
<div style="font-size: 12px; color: #888;">Engineer</div>
</div>
</div>
</div>
`,
after: `
<div style="width: 100%; max-width: 240px; padding: 16px; font-family: 'Instrument Sans', sans-serif;">
<div style="font-size: 0.8125rem; font-weight: 600; color: var(--color-ink); margin-bottom: 16px;">Team Members</div>
<div style="display: flex; flex-direction: column; gap: 6px;">
<div style="display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--color-mist);">
<div style="width: 28px; height: 28px; background: var(--color-accent); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">AC</div>
<div style="flex: 1;">
<div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Alice Chen</div>
<div style="font-size: 0.6875rem; color: var(--color-ash);">Designer</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 12px; padding: 10px 0;">
<div style="width: 28px; height: 28px; background: color-mix(in oklch, var(--color-accent) 60%, var(--color-ink)); border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: white; font-size: 11px; font-weight: 600;">BP</div>
<div style="flex: 1;">
<div style="font-size: 0.8125rem; font-weight: 500; color: var(--color-ink);">Bob Park</div>
<div style="font-size: 0.6875rem; color: var(--color-ash);">Engineer</div>
</div>
</div>
</div>
</div>
`
};
+6
View File
@@ -16,6 +16,9 @@ import delight from "./delight.js";
import extract from "./extract.js";
import adapt from "./adapt.js";
import onboard from "./onboard.js";
import typeset from "./typeset.js";
import arrange from "./arrange.js";
import overdrive from "./overdrive.js";
export const commandDemos = {
normalize,
@@ -34,6 +37,9 @@ export const commandDemos = {
extract,
adapt,
onboard,
typeset,
arrange,
overdrive,
};
export function getCommandDemo(commandId) {
+342
View File
@@ -0,0 +1,342 @@
// Overdrive command demo - laser-etched signature on a premium dark surface
// Laser effect adapted from pbakaus/shaders laser-precision
export default {
id: 'overdrive',
caption: 'Static flat card → Laser-etched signature effect',
before: `
<div style="width: 100%; height: 100%; min-height: 200px; display: flex; align-items: center; justify-content: center; background: #f5f5f5; font-family: system-ui, sans-serif;">
<div style="text-align: center; padding: 20px;">
<div style="font-size: 12px; color: #666; font-style: italic; line-height: 1.6; max-width: 220px; margin-bottom: 16px;">It's time to spark your imagination. Welcome to the Impeccable Community.</div>
<div style="font-size: 12px; color: #aaa;">Paul Bakaus</div>
</div>
</div>
`,
after: `
<canvas class="od-burn" style="position: absolute; inset: 0; width: 100%; height: 100%; background: #0e0d0b;"></canvas>
<canvas class="od-sparks" style="position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;"></canvas>
<div style="position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; pointer-events: none; text-align: center;">
<p style="font-family: 'Cormorant Garamond', serif; font-size: 1.1rem; font-style: italic; font-weight: 400; color: rgba(240,230,210,0.85); line-height: 1.5; max-width: 260px; margin: 0 0 24px;">It's time to spark your imagination.<br>Welcome to the Impeccable Community.</p>
</div>
`,
init(container) {
const burnCanvas = container.querySelector('.od-burn');
const sparkCanvas = container.querySelector('.od-sparks');
if (!burnCanvas || !sparkCanvas) return;
const rect = burnCanvas.parentElement.getBoundingClientRect();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
// Size both canvases
for (const c of [burnCanvas, sparkCanvas]) {
c.width = Math.round(rect.width * dpr);
c.height = Math.round(rect.height * dpr);
}
const ctx = burnCanvas.getContext('2d'); // persistent burn trails
const sCtx = sparkCanvas.getContext('2d'); // cleared each frame
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
sCtx.setTransform(dpr, 0, 0, dpr, 0, 0);
const w = rect.width;
const h = rect.height;
// Fill background
ctx.fillStyle = '#0e0d0b';
ctx.fillRect(0, 0, w, h);
// ── Signature paths — two separate strokes ──
function buildSignaturePaths() {
function makePath(buildFn) {
const pts = [];
function bez(x0,y0, cx1,cy1, cx2,cy2, x1,y1, n) {
for (let i = 0; i <= n; i++) {
const t = i / n, mt = 1-t;
pts.push({
x: mt*mt*mt*x0 + 3*mt*mt*t*cx1 + 3*mt*t*t*cx2 + t*t*t*x1,
y: mt*mt*mt*y0 + 3*mt*mt*t*cy1 + 3*mt*t*t*cy2 + t*t*t*y1
});
}
}
buildFn(bez);
return pts;
}
const paul = makePath(bez => {
// P
bez(6,44, 5,32, 4,18, 8,8, 14);
bez(8,8, 16,5, 26,7, 26,16, 12);
bez(26,16, 26,22, 18,26, 14,28, 10);
// a
bez(14,28, 18,22, 23,20, 26,22, 8);
bez(26,22, 29,24, 28,30, 24,32, 6);
bez(24,32, 28,34, 30,30, 32,28, 5);
// u
bez(32,28, 34,36, 38,40, 42,32, 8);
bez(42,32, 44,26, 47,24, 48,28, 6);
// l
bez(48,28, 49,16, 50,6, 53,8, 10);
bez(53,8, 55,14, 56,28, 58,32, 8);
});
const bakaus = makePath(bez => {
// B
bez(66,44, 66,32, 67,16, 70,8, 14);
bez(70,8, 78,4, 83,10, 79,18, 12);
bez(79,18, 84,15, 87,24, 80,30, 12);
bez(80,30, 78,34, 80,36, 84,32, 5);
// akaus
bez(84,32, 89,24, 94,22, 97,26, 8);
bez(97,26, 99,30, 96,34, 100,30, 5);
bez(100,30, 101,20, 102,14, 104,16, 8);
bez(104,16, 106,24, 109,28, 107,32, 6);
bez(107,32, 105,36, 110,36, 113,30, 6);
bez(113,30, 118,24, 122,22, 125,28, 8);
bez(125,28, 128,36, 133,38, 137,30, 8);
bez(137,30, 139,26, 142,24, 144,28, 5);
bez(144,28, 154,24, 170,22, 195,28, 16);
});
// Scale both paths
const rawW = 200;
const scale = (w * 0.7) / rawW;
const ox = (w - rawW * scale) / 2;
const oy = h * 0.52;
const transform = p => ({ x: p.x * scale + ox, y: p.y * scale * 0.75 + oy });
return [paul.map(transform), bakaus.map(transform)];
}
const strokes = buildSignaturePaths();
// Precompute lengths for each stroke
function computeLengths(pts) {
const lens = [];
let total = 0;
for (let i = 1; i < pts.length; i++) {
const dx = pts[i].x - pts[i-1].x, dy = pts[i].y - pts[i-1].y;
const l = Math.sqrt(dx*dx + dy*dy);
lens.push(l); total += l;
}
return { lens, total };
}
const strokeData = strokes.map(pts => {
const { lens, total } = computeLengths(pts);
return { pts, lens, total };
});
function posAtStroke(stroke, dist) {
let d = 0;
for (let i = 0; i < stroke.lens.length; i++) {
if (d + stroke.lens[i] >= dist) {
const t = stroke.lens[i] > 0 ? (dist - d) / stroke.lens[i] : 0;
const p0 = stroke.pts[i], p1 = stroke.pts[i+1];
return { x: p0.x + (p1.x - p0.x) * t, y: p0.y + (p1.y - p0.y) * t };
}
d += stroke.lens[i];
}
return stroke.pts[stroke.pts.length - 1];
}
const totalLength = strokeData.reduce((s, d) => s + d.total, 0);
// ── State ──
let currentStroke = 0;
let drawnLength = 0;
const drawSpeed = totalLength / 3.0;
let prevTip = strokes[0][0];
let sparks = [];
let phase = 'drawing'; // drawing, lifting, holding, fading
let phaseTimer = 0;
let lastTime = 0;
// Track drawn points per stroke for smooth rendering
const allDrawnStrokes = [[], []];
function drawBurnTrail() {
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
function strokeSmooth(pts, color, width) {
if (pts.length < 2) return;
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
for (let i = 1; i < pts.length - 1; i++) {
const mx = (pts[i].x + pts[i+1].x) / 2;
const my = (pts[i].y + pts[i+1].y) / 2;
ctx.quadraticCurveTo(pts[i].x, pts[i].y, mx, my);
}
ctx.lineTo(pts[pts.length-1].x, pts[pts.length-1].y);
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.stroke();
}
// Draw all accumulated strokes
for (const pts of allDrawnStrokes) {
strokeSmooth(pts, 'rgba(180, 100, 30, 0.12)', 5);
strokeSmooth(pts, 'rgba(220, 140, 50, 0.3)', 2.5);
strokeSmooth(pts, 'rgba(255, 210, 130, 0.7)', 1.2);
strokeSmooth(pts, 'rgba(255, 248, 235, 0.6)', 0.4);
}
}
function emitSparks(x, y, count) {
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 50 + Math.random() * 140;
sparks.push({
x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed,
life: 0.15 + Math.random() * 0.35, maxLife: 0.15 + Math.random() * 0.35,
size: 0.3 + Math.random() * 1.0, bright: Math.random() > 0.4
});
}
}
function draw(timestamp) {
if (!document.contains(burnCanvas)) return;
// Actual frame delta time
if (!lastTime) lastTime = timestamp;
const dt = Math.min(0.05, (timestamp - lastTime) / 1000);
lastTime = timestamp;
switch (phase) {
case 'drawing': {
const sd = strokeData[currentStroke];
drawnLength += drawSpeed * dt;
if (drawnLength >= sd.total) {
drawnLength = sd.total;
emitSparks(prevTip.x, prevTip.y, 6);
if (currentStroke < strokes.length - 1) {
// Lift — pause briefly before starting next stroke
phase = 'lifting';
phaseTimer = 0;
} else {
phase = 'holding';
phaseTimer = 0;
}
}
const tip = posAtStroke(sd, drawnLength);
allDrawnStrokes[currentStroke].push({ x: tip.x, y: tip.y });
prevTip = tip;
if (Math.random() < 0.4) emitSparks(tip.x, tip.y, 1);
// Redraw full smooth trail
ctx.fillStyle = '#0e0d0b';
ctx.fillRect(0, 0, w, h);
drawBurnTrail();
break;
}
case 'lifting':
phaseTimer += dt;
if (phaseTimer >= 0.25) {
currentStroke++;
drawnLength = 0;
prevTip = strokes[currentStroke][0];
phase = 'drawing';
phaseTimer = 0;
}
break;
case 'holding':
phaseTimer += dt;
if (phaseTimer >= 3.5) { phase = 'fading'; phaseTimer = 0; }
break;
case 'fading':
phaseTimer += dt;
ctx.fillStyle = 'rgba(14, 13, 11, 0.04)';
ctx.fillRect(0, 0, w, h);
if (phaseTimer >= 2.0) {
ctx.fillStyle = '#0e0d0b';
ctx.fillRect(0, 0, w, h);
currentStroke = 0; drawnLength = 0;
prevTip = strokes[0][0];
sparks = [];
allDrawnStrokes[0].length = 0;
allDrawnStrokes[1].length = 0;
phase = 'drawing'; phaseTimer = 0;
}
break;
}
// Update sparks
for (let i = sparks.length - 1; i >= 0; i--) {
const s = sparks[i];
s.x += s.vx * dt; s.y += s.vy * dt;
s.vx *= 0.94; s.vy *= 0.94; s.vy += 100 * dt;
s.life -= dt;
if (s.life <= 0) sparks.splice(i, 1);
}
// Draw sparks + tip on overlay (cleared each frame)
sCtx.clearRect(0, 0, w, h);
for (const s of sparks) {
const t = s.life / s.maxLife;
const r = s.size * (0.3 + t * 0.7);
// Spark trail
const speed = Math.sqrt(s.vx*s.vx + s.vy*s.vy);
if (speed > 20) {
const tl = speed * 0.01;
sCtx.beginPath();
sCtx.moveTo(s.x, s.y);
sCtx.lineTo(s.x - s.vx/speed * tl, s.y - s.vy/speed * tl);
sCtx.strokeStyle = s.bright
? `rgba(255,255,240,${(t*0.4).toFixed(3)})`
: `rgba(255,180,60,${(t*0.3).toFixed(3)})`;
sCtx.lineWidth = r * 0.5;
sCtx.lineCap = 'round';
sCtx.stroke();
}
sCtx.beginPath();
sCtx.arc(s.x, s.y, r, 0, Math.PI * 2);
sCtx.fillStyle = s.bright
? `rgba(255,255,255,${(t*0.85).toFixed(3)})`
: `rgba(255,200,80,${(t*0.75).toFixed(3)})`;
sCtx.fill();
}
// Draw laser tip on overlay
if (phase === 'drawing' && drawnLength < strokeData[currentStroke].total) {
const tip = posAtStroke(strokeData[currentStroke], drawnLength);
const fl = 0.85 + Math.random() * 0.15;
// Wide heat bloom
const g0 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 35);
g0.addColorStop(0, `rgba(255,100,20,${0.15*fl})`);
g0.addColorStop(0.4, `rgba(200,60,10,${0.05*fl})`);
g0.addColorStop(1, 'rgba(150,40,10,0)');
sCtx.fillStyle = g0; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 35, 0, Math.PI*2); sCtx.fill();
// Amber corona
const g1 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 16);
g1.addColorStop(0, `rgba(255,180,60,${0.45*fl})`);
g1.addColorStop(0.5, `rgba(255,140,40,${0.15*fl})`);
g1.addColorStop(1, 'rgba(200,80,20,0)');
sCtx.fillStyle = g1; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 16, 0, Math.PI*2); sCtx.fill();
// White-hot core
const g2 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 6);
g2.addColorStop(0, `rgba(255,255,255,${0.95*fl})`);
g2.addColorStop(0.3, `rgba(255,250,240,${0.7*fl})`);
g2.addColorStop(0.6, `rgba(255,220,160,${0.3*fl})`);
g2.addColorStop(1, 'rgba(255,180,80,0)');
sCtx.fillStyle = g2; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 6, 0, Math.PI*2); sCtx.fill();
// Overexposed center
const g3 = sCtx.createRadialGradient(tip.x, tip.y, 0, tip.x, tip.y, 2.5);
g3.addColorStop(0, `rgba(255,255,255,${fl})`);
g3.addColorStop(1, 'rgba(255,255,255,0)');
sCtx.fillStyle = g3; sCtx.beginPath(); sCtx.arc(tip.x, tip.y, 2.5, 0, Math.PI*2); sCtx.fill();
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
}
};
+23
View File
@@ -0,0 +1,23 @@
// Typeset command demo - shows flat, hierarchyless text becoming intentional typography
export default {
id: 'typeset',
caption: 'No type hierarchy → Clear, intentional typography',
before: `
<div style="width: 100%; max-width: 240px; padding: 16px; font-family: Arial, sans-serif;">
<div style="font-size: 14px; font-weight: bold; color: #444; margin-bottom: 8px;">Project Update</div>
<div style="font-size: 14px; color: #444; margin-bottom: 8px;">Q1 Design Sprint</div>
<div style="font-size: 14px; color: #444; line-height: 1.4; margin-bottom: 8px;">The team completed the redesign of the dashboard. All components have been reviewed and approved by stakeholders.</div>
<div style="font-size: 14px; color: #444;">Updated 2 hours ago</div>
</div>
`,
after: `
<div style="width: 100%; max-width: 240px; padding: 16px; font-family: 'Instrument Sans', sans-serif;">
<div style="font-size: 0.625rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-ash); margin-bottom: 6px;">Project Update</div>
<div style="font-family: 'Cormorant Garamond', serif; font-size: 1.5rem; font-weight: 600; color: var(--color-ink); line-height: 1.1; margin-bottom: 12px;">Q1 Design Sprint</div>
<p style="font-size: 0.8125rem; color: color-mix(in oklch, var(--color-ink) 65%, transparent); line-height: 1.65; margin: 0 0 14px; max-width: 30ch;">The team completed the redesign of the dashboard. All components reviewed and approved.</p>
<div style="font-size: 0.6875rem; color: var(--color-ash); font-variant-numeric: tabular-nums;">Updated 2 hours ago</div>
</div>
`
};
+20 -3
View File
@@ -48,10 +48,27 @@ export function initSplitCompare(container, options = {}) {
}
function retriggerAnimations() {
// Find all elements with animations in the "after" content and re-trigger them
// Re-trigger CSS animations in the "after" content.
// If there's a canvas (e.g. overdrive shader), we can't clone-and-replace
// because that destroys JS-driven animations. In that case, retrigger
// individual elements. Otherwise, use the fast clone approach.
const afterContent = splitAfter.querySelector('.split-content');
if (afterContent) {
// Clone and replace to restart all CSS animations
if (!afterContent) return;
const hasCanvas = afterContent.querySelector('canvas, .od-burn, .od-sparks');
if (hasCanvas) {
// Safe path: retrigger CSS animations individually, skip canvas
afterContent.querySelectorAll('*').forEach(el => {
if (el.tagName === 'CANVAS') return;
const anim = getComputedStyle(el).animationName;
if (anim && anim !== 'none') {
el.style.animation = 'none';
el.offsetHeight;
el.style.animation = '';
}
});
} else {
// Fast path: clone and replace to restart all CSS animations
const clone = afterContent.cloneNode(true);
afterContent.parentNode.replaceChild(clone, afterContent);
}
+28 -4
View File
@@ -22,6 +22,21 @@ export function initHashTracking() {
let ticking = false;
function updateHash() {
// Don't override command deep links while user is in the commands section
if (currentHash.startsWith('cmd-')) {
const cmdEl = document.getElementById(currentHash);
if (cmdEl) {
const rect = cmdEl.getBoundingClientRect();
// Only clear the cmd hash if user scrolled well away from commands section
if (rect.top > window.innerHeight * 2 || rect.bottom < -window.innerHeight) {
currentHash = '';
} else {
ticking = false;
return;
}
}
}
const scrollY = window.scrollY;
const viewportHeight = window.innerHeight;
const triggerPoint = scrollY + viewportHeight * 0.3;
@@ -38,6 +53,9 @@ export function initHashTracking() {
}
});
// Don't set #hero — it's the default state, no hash needed
if (activeSection === 'hero') activeSection = '';
if (activeSection !== currentHash) {
currentHash = activeSection;
if (activeSection) {
@@ -59,17 +77,23 @@ export function initHashTracking() {
// Handle initial hash on page load - instant jump
if (window.location.hash) {
const target = document.querySelector(window.location.hash);
const hash = window.location.hash.slice(1);
const target = document.getElementById(hash);
if (target) {
currentHash = hash;
setTimeout(() => {
const offset = 40;
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top: targetPosition, behavior: 'auto' });
// If it's a command deep link, activate it
if (hash.startsWith('cmd-') && target.classList.contains('manual-entry')) {
target.click();
}
}, 100);
}
} else {
// No hash — don't set one on initial load
}
// Initial check
updateHash();
}
+127
View File
@@ -0,0 +1,127 @@
---
name: arrange
description: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy to create intentional compositions.
args:
- name: target
description: The feature or component to improve layout for (optional)
required: false
user-invokable: true
---
Assess and improve layout and spacing that feels monotonous, crowded, or structurally weak — turning generic arrangements into intentional, rhythmic compositions.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
---
## Assess Current Layout
Analyze what's weak about the current spatial design:
1. **Spacing**:
- Is spacing consistent or arbitrary? (Random padding/margin values)
- Is all spacing the same? (Equal padding everywhere = no rhythm)
- Are related elements grouped tightly, with generous space between groups?
2. **Visual hierarchy**:
- Apply the squint test: blur your (metaphorical) eyes — can you still identify the most important element, second most important, and clear groupings?
- Is hierarchy achieved effectively? (Space and weight alone can be enough — but is the current approach working?)
- Does whitespace guide the eye to what matters?
3. **Grid & structure**:
- Is there a clear underlying structure, or does the layout feel random?
- Are identical card grids used everywhere? (Icon + heading + text, repeated endlessly)
- Is everything centered? (Left-aligned with asymmetric layouts feels more designed, but not a hard and fast rule)
4. **Rhythm & variety**:
- Does the layout have visual rhythm? (Alternating tight/generous spacing)
- Is every section structured the same way? (Monotonous repetition)
- Are there intentional moments of surprise or emphasis?
5. **Density**:
- Is the layout too cramped? (Not enough breathing room)
- Is the layout too sparse? (Excessive whitespace without purpose)
- Does density match the content type? (Data-dense UIs need tighter spacing; marketing pages need more air)
**CRITICAL**: Layout problems are often the root cause of interfaces feeling "off" even when colors and fonts are fine. Space is a design material — use it with intention.
## Plan Layout Improvements
Consult the [spatial design reference](reference/spatial-design.md) from the frontend-design skill for detailed guidance on grids, rhythm, and container queries.
Create a systematic plan:
- **Spacing system**: Use a consistent scale — whether that's a framework's built-in scale (e.g., Tailwind), rem-based tokens, or a custom system. The specific values matter less than consistency.
- **Hierarchy strategy**: How will space communicate importance?
- **Layout approach**: What structure fits the content? Flex for 1D, Grid for 2D, named areas for complex page layouts.
- **Rhythm**: Where should spacing be tight vs generous?
## Improve Layout Systematically
### Establish a Spacing System
- Use a consistent spacing scale — framework scales (Tailwind, etc.), rem-based tokens, or a custom scale all work. What matters is that values come from a defined set, not arbitrary numbers.
- Name tokens semantically if using custom properties: `--space-xs` through `--space-xl`, not `--spacing-8`
- Use `gap` for sibling spacing instead of margins — eliminates margin collapse hacks
- Apply `clamp()` for fluid spacing that breathes on larger screens
### Create Visual Rhythm
- **Tight grouping** for related elements (8-12px between siblings)
- **Generous separation** between distinct sections (48-96px)
- **Varied spacing** within sections — not every row needs the same gap
- **Asymmetric compositions** — break the predictable centered-content pattern when it makes sense
### Choose the Right Layout Tool
- **Use Flexbox for 1D layouts**: Rows of items, nav bars, button groups, card contents, most component internals. Flex is simpler and more appropriate for the majority of layout tasks.
- **Use Grid for 2D layouts**: Page-level structure, dashboards, data-dense interfaces, anything where rows AND columns need coordinated control.
- **Don't default to Grid** when Flexbox with `flex-wrap` would be simpler and more flexible.
- Use `repeat(auto-fit, minmax(280px, 1fr))` for responsive grids without breakpoints.
- Use named grid areas (`grid-template-areas`) for complex page layouts — redefine at breakpoints.
### Break Card Grid Monotony
- Don't default to card grids for everything — spacing and alignment create visual grouping naturally
- Use cards only when content is truly distinct and actionable — never nest cards inside cards
- Vary card sizes, span columns, or mix cards with non-card content to break repetition
### Strengthen Visual Hierarchy
- Use the fewest dimensions needed for clear hierarchy. Space alone can be enough — generous whitespace around an element draws the eye. Some of the most sophisticated designs achieve rhythm with just space and weight. Add color or size contrast only when simpler means aren't sufficient.
- Be aware of reading flow — in LTR languages, the eye naturally scans top-left to bottom-right, but primary action placement depends on context (e.g., bottom-right in dialogs, top in navigation).
- Create clear content groupings through proximity and separation.
### Manage Depth & Elevation
- Create a semantic z-index scale (dropdown → sticky → modal-backdrop → modal → toast → tooltip)
- Build a consistent shadow scale (sm → md → lg → xl) — shadows should be subtle
- Use elevation to reinforce hierarchy, not as decoration
### Optical Adjustments
- If an icon looks visually off-center despite being geometrically centered, nudge it — but only if you're confident it actually looks wrong. Don't adjust speculatively.
**NEVER**:
- Use arbitrary spacing values outside your scale
- Make all spacing equal — variety creates hierarchy
- Wrap everything in cards — not everything needs a container
- Nest cards inside cards — use spacing and dividers for hierarchy within
- Use identical card grids everywhere (icon + heading + text, repeated)
- Center everything — left-aligned with asymmetry feels more designed
- Default to the hero metric layout (big number, small label, stats, gradient) as a template. If showing real user data, a prominent metric can work — but it should display actual data, not decorative numbers.
- Default to CSS Grid when Flexbox would be simpler — use the simplest tool for the job
- Use arbitrary z-index values (999, 9999) — build a semantic scale
## Verify Layout Improvements
- **Squint test**: Can you identify primary, secondary, and groupings with blurred vision?
- **Rhythm**: Does the page have a satisfying beat of tight and generous spacing?
- **Hierarchy**: Is the most important content obvious within 2 seconds?
- **Breathing room**: Does the layout feel comfortable, not cramped or wasteful?
- **Consistency**: Is the spacing system applied uniformly?
- **Responsiveness**: Does the layout adapt gracefully across screen sizes?
Remember: Space is the most underused design tool. A layout with the right rhythm and hierarchy can make even simple content feel polished and intentional.
+144
View File
@@ -0,0 +1,144 @@
---
name: overdrive
description: Push interfaces past conventional limits with technically ambitious implementations. Whether that's a shader, a 60fps virtual table, spring physics on a dialog, or scroll-driven reveals — make users ask "how did they do that?"
args:
- name: target
description: The feature or area to push into overdrive (optional)
required: false
user-invokable: true
---
Start your response with:
```
──────────── ⚡ OVERDRIVE ─────────────
》》》 Entering overdrive mode...
```
Push an interface past conventional limits. This isn't just about visual effects — it's about using the full power of the browser to make any part of an interface feel extraordinary: a table that handles a million rows, a dialog that morphs from its trigger, a form that validates in real-time with streaming feedback, a page transition that feels cinematic.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
**EXTRA IMPORTANT FOR THIS SKILL**: Context determines what "extraordinary" means. A particle system on a creative portfolio is impressive. The same particle system on a settings page is embarrassing. But a settings page with instant optimistic saves and animated state transitions? That's extraordinary too. Understand the project's personality and goals before deciding what's appropriate.
### Propose Before Building
This skill has the highest potential to misfire. Do NOT jump straight into implementation. You MUST:
1. **Think through 2-3 different directions** — consider different techniques, levels of ambition, and aesthetic approaches. For each direction, briefly describe what the result would look and feel like.
2. **{{ask_instruction}}** to present these directions and get the user's pick before writing any code. Explain trade-offs (browser support, performance cost, complexity).
3. Only proceed with the direction the user confirms.
Skipping this step risks building something embarrassing that needs to be thrown away.
### Iterate with Browser Automation
Technically ambitious effects almost never work on the first try. You MUST actively use browser automation tools to preview your work, visually verify the result, and iterate. Do not assume the effect looks right — check it. Expect multiple rounds of refinement. The gap between "technically works" and "looks extraordinary" is closed through visual iteration, not code alone.
---
## Assess What "Extraordinary" Means Here
The right kind of technical ambition depends entirely on what you're working with. Before choosing a technique, ask: **what would make a user of THIS specific interface say "wow, that's nice"?**
### For visual/marketing surfaces
Pages, hero sections, landing pages, portfolios — the "wow" is often sensory: a scroll-driven reveal, a shader background, a cinematic page transition, generative art that responds to the cursor.
### For functional UI
Tables, forms, dialogs, navigation — the "wow" is in how it FEELS: a dialog that morphs from the button that triggered it via View Transitions, a data table that renders 100k rows at 60fps via virtual scrolling, a form with streaming validation that feels instant, drag-and-drop with spring physics.
### For performance-critical UI
The "wow" is invisible but felt: a search that filters 50k items without a flicker, a complex form that never blocks the main thread, an image editor that processes in near-real-time. The interface just never hesitates.
### For data-heavy interfaces
Charts and dashboards — the "wow" is in fluidity: GPU-accelerated rendering via Canvas/WebGL for massive datasets, animated transitions between data states, force-directed graph layouts that settle naturally.
**The common thread**: something about the implementation goes beyond what users expect from a web interface. The technique serves the experience, not the other way around.
## The Toolkit
Organized by what you're trying to achieve, not by technology name.
### Make transitions feel cinematic
- **View Transitions API** (same-document: all browsers; cross-document: no Firefox) — shared element morphing between states. A list item expanding into a detail page. A button morphing into a dialog. This is the closest thing to native FLIP animations.
- **`@starting-style`** (all browsers) — animate elements from `display: none` to visible with CSS only, including entry keyframes
- **Spring physics** — natural motion with mass, tension, and damping instead of cubic-bezier. Libraries: motion (formerly Framer Motion), GSAP, or roll your own spring solver.
### Tie animation to scroll position
- **Scroll-driven animations** (`animation-timeline: scroll()`) — CSS-only, no JS. Parallax, progress bars, reveal sequences all driven by scroll position. (Chrome/Edge/Safari; Firefox: flag only — always provide a static fallback)
### Render beyond CSS
- **WebGL** (all browsers) — shader effects, post-processing, particle systems. Libraries: Three.js, OGL (lightweight), regl. Use for effects CSS can't express.
- **WebGPU** (Chrome/Edge; Safari partial; Firefox: flag only) — next-gen GPU compute. More powerful than WebGL but limited browser support. Always fall back to WebGL2.
- **Canvas 2D / OffscreenCanvas** — custom rendering, pixel manipulation, or moving heavy rendering off the main thread entirely via Web Workers + OffscreenCanvas.
- **SVG filter chains** — displacement maps, turbulence, morphology for organic distortion effects. CSS-animatable.
### Make data feel alive
- **Virtual scrolling** — render only visible rows for tables/lists with tens of thousands of items. No library required for simple cases; TanStack Virtual for complex ones.
- **GPU-accelerated charts** — Canvas or WebGL-rendered data visualization for datasets too large for SVG/DOM. Libraries: deck.gl, regl-based custom renderers.
- **Animated data transitions** — morph between chart states rather than replacing. D3's `transition()` or View Transitions for DOM-based charts.
### Animate complex properties
- **`@property`** (all browsers) — register custom CSS properties with types, enabling animation of gradients, colors, and complex values that CSS can't normally interpolate.
- **Web Animations API** (all browsers) — JavaScript-driven animations with the performance of CSS. Composable, cancellable, reversible. The foundation for complex choreography.
### Push performance boundaries
- **Web Workers** — move computation off the main thread. Heavy data processing, image manipulation, search indexing — anything that would cause jank.
- **OffscreenCanvas** — render in a Worker thread. The main thread stays free while complex visuals render in the background.
- **WASM** — near-native performance for computation-heavy features. Image processing, physics simulations, codecs.
### Interact with the device
- **Web Audio API** — spatial audio, audio-reactive visualizations, sonic feedback. Requires user gesture to start.
- **Device APIs** — orientation, ambient light, geolocation. Use sparingly and always with user permission.
**NOTE**: This skill is about enhancing how an interface FEELS, not changing what a product DOES. Adding real-time collaboration, offline support, or new backend capabilities are product decisions, not UI enhancements. Focus on making existing features feel extraordinary.
## Implement with Discipline
### Progressive enhancement is non-negotiable
Every technique must degrade gracefully. The experience without the enhancement must still be good.
```css
@supports (animation-timeline: scroll()) {
.hero { animation-timeline: scroll(); }
}
```
```javascript
if ('gpu' in navigator) { /* WebGPU */ }
else if (canvas.getContext('webgl2')) { /* WebGL2 fallback */ }
/* CSS-only fallback must still look good */
```
### Performance rules
- Target 60fps. If dropping below 50, simplify.
- Respect `prefers-reduced-motion` — always. Provide a beautiful static alternative.
- Lazy-initialize heavy resources (WebGL contexts, WASM modules) only when near viewport.
- Pause off-screen rendering. Kill what you can't see.
- Test on real mid-range devices, not just your development machine.
### Polish is the difference
The gap between "cool" and "extraordinary" is in the last 20% of refinement: the easing curve on a spring animation, the timing offset in a staggered reveal, the subtle secondary motion that makes a transition feel physical. Don't ship the first version that works — ship the version that feels inevitable.
**NEVER**:
- Ignore `prefers-reduced-motion` — this is an accessibility requirement, not a suggestion
- Ship effects that cause jank on mid-range devices
- Use bleeding-edge APIs without a functional fallback
- Add sound without explicit user opt-in
- Use technical ambition to mask weak design fundamentals — fix those first with other skills
- Layer multiple competing extraordinary moments — focus creates impact, excess creates noise
## Verify the Result
- **The wow test**: Show it to someone who hasn't seen it. Do they react?
- **The removal test**: Take it away. Does the experience feel diminished, or does nobody notice?
- **The device test**: Run it on a phone, a tablet, a Chromebook. Still smooth?
- **The accessibility test**: Enable reduced motion. Still beautiful?
- **The context test**: Does this make sense for THIS brand and audience?
Remember: "Technically extraordinary" isn't about using the newest API. It's about making an interface do something users didn't think a website could do.
+117
View File
@@ -0,0 +1,117 @@
---
name: typeset
description: Improve typography by fixing font choices, hierarchy, sizing, weight consistency, and readability. Makes text feel intentional and polished.
args:
- name: target
description: The feature or component to improve typography for (optional)
required: false
user-invokable: true
---
Assess and improve typography that feels generic, inconsistent, or poorly structured — turning default-looking text into intentional, well-crafted type.
## MANDATORY PREPARATION
Use the frontend-design skill — it contains design principles, anti-patterns, and the **Context Gathering Protocol**. Follow the protocol before proceeding — if no design context exists yet, you MUST run teach-impeccable first.
---
## Assess Current Typography
Analyze what's weak or generic about the current type:
1. **Font choices**:
- Are we using invisible defaults? (Inter, Roboto, Arial, Open Sans, system defaults)
- Does the font match the brand personality? (A playful brand shouldn't use a corporate typeface)
- Are there too many font families? (More than 2-3 is almost always a mess)
2. **Hierarchy**:
- Can you tell headings from body from captions at a glance?
- Are font sizes too close together? (14px, 15px, 16px = muddy hierarchy)
- Are weight contrasts strong enough? (Medium vs Regular is barely visible)
3. **Sizing & scale**:
- Is there a consistent type scale, or are sizes arbitrary?
- Does body text meet minimum readability? (16px+)
- Is fluid sizing used, or do sizes jump at breakpoints?
4. **Readability**:
- Are line lengths comfortable? (45-75 characters ideal)
- Is line-height appropriate for the font and context?
- Is there enough contrast between text and background?
5. **Consistency**:
- Are the same elements styled the same way throughout?
- Are font weights used consistently? (Not bold in one section, semibold in another for the same role)
- Is letter-spacing intentional or default everywhere?
**CRITICAL**: The goal isn't to make text "fancier" — it's to make it clearer, more readable, and more intentional. Good typography is invisible; bad typography is distracting.
## Plan Typography Improvements
Consult the [typography reference](reference/typography.md) from the frontend-design skill for detailed guidance on scales, pairing, and loading strategies.
Create a systematic plan:
- **Font selection**: Do fonts need replacing? What fits the brand/context?
- **Type scale**: Establish a modular scale (e.g., 1.25 ratio) with clear hierarchy
- **Weight strategy**: Which weights serve which roles? (Regular for body, Semibold for labels, Bold for headings — or whatever fits)
- **Spacing**: Line-heights, letter-spacing, and margins between typographic elements
## Improve Typography Systematically
### Font Selection
If fonts need replacing:
- Choose fonts that reflect the brand personality
- Pair with genuine contrast (serif + sans, geometric + humanist) — or use a single family in multiple weights
- Ensure web font loading doesn't cause layout shift (`font-display: swap`, metric-matched fallbacks)
### Establish Hierarchy
Build a clear type scale:
- **5 sizes cover most needs**: caption, secondary, body, subheading, heading
- **Use a consistent ratio** between levels (1.25, 1.333, or 1.5)
- **Combine dimensions**: Size + weight + color + space for strong hierarchy — don't rely on size alone
- **Use fluid sizing**: `clamp(min, preferred, max)` for smooth scaling
### Fix Readability
- Set `max-width` on text containers using `ch` units (`max-width: 65ch`)
- Adjust line-height per context: tighter for headings (1.1-1.2), looser for body (1.5-1.7)
- Increase line-height slightly for light-on-dark text
- Ensure body text is at least 16px / 1rem
### Refine Details
- Use `tabular-nums` for data tables and numbers that should align
- Apply proper `letter-spacing`: slightly open for small caps and uppercase, default or tight for large display text
- Use semantic token names (`--text-body`, `--text-heading`), not value names (`--font-16`)
- Set `font-kerning: normal` and consider OpenType features where appropriate
### Weight Consistency
- Define clear roles for each weight and stick to them
- Don't use more than 3-4 weights (Regular, Medium, Semibold, Bold is plenty)
- Load only the weights you actually use (each weight adds to page load)
**NEVER**:
- Use more than 2-3 font families
- Pick sizes arbitrarily — commit to a scale
- Set body text below 16px
- Use decorative/display fonts for body text
- Disable browser zoom (`user-scalable=no`)
- Use `px` for font sizes — use `rem` to respect user settings
- Default to Inter/Roboto/Open Sans when personality matters
- Pair fonts that are similar but not identical (two geometric sans-serifs)
## Verify Typography Improvements
- **Hierarchy**: Can you identify heading vs body vs caption instantly?
- **Readability**: Is body text comfortable to read in long passages?
- **Consistency**: Are same-role elements styled identically throughout?
- **Personality**: Does the typography reflect the brand?
- **Performance**: Are web fonts loading efficiently without layout shift?
- **Accessibility**: Does text meet WCAG contrast ratios? Is it zoomable to 200%?
Remember: Typography is the foundation of interface design — it carries the majority of information. Getting it right is the highest-leverage improvement you can make.