feat(skills): port 17 generic frontend skills from ~/.claude/skills/ (a11y-audit, design-system, figma-to-code, form-builder, frontend-design, landing-page, motion-design, perf-audit, responsive-audit, scroll-animation, seo-audit, site-builder, site-teardown, ui-component, web-assets, web-deploy, web-effects)
This commit is contained in:
parent
8c60085862
commit
fd81aae515
17 changed files with 2629 additions and 0 deletions
102
skills/a11y-audit/SKILL.md
Normal file
102
skills/a11y-audit/SKILL.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
name: a11y-audit
|
||||
description: Use when auditing accessibility — WCAG 2.2 AA compliance, contrast checks, keyboard navigation, screen reader support, prefers-reduced-motion. Triggers on "accessibility", "a11y", "wcag", "screen reader", "contrast check".
|
||||
arguments:
|
||||
- name: command
|
||||
description: "Command: scan, fix, contrast, checklist, report"
|
||||
required: false
|
||||
- name: target
|
||||
description: URL or file path to audit
|
||||
required: false
|
||||
---
|
||||
|
||||
# Accessibility Audit — WCAG 2.2 AA
|
||||
|
||||
## Legal Context
|
||||
|
||||
- **EAA (EU):** In force since June 2025. Penalties: up to 100K EUR / 4% revenue
|
||||
- **ADA (US):** References WCAG 2.2 AA
|
||||
- **Standard:** WCAG 2.2 AA is minimum for any commercial site targeting US/EU
|
||||
|
||||
## Top 10 Violations
|
||||
|
||||
1. Missing alt text on images
|
||||
2. Low contrast text (4.5:1 normal, 3:1 large text)
|
||||
3. Keyboard traps in menus
|
||||
4. Missing form labels
|
||||
5. Skipped heading levels
|
||||
6. No skip links
|
||||
7. Non-semantic HTML (`<div>` instead of `<nav>`, `<main>`)
|
||||
8. Missing video captions
|
||||
9. Invisible focus styles
|
||||
10. Touch targets <24x24px (WCAG 2.2 new)
|
||||
|
||||
**Automated tools catch only 30-40%.** Manual audit required.
|
||||
|
||||
## Automated Testing
|
||||
|
||||
```bash
|
||||
# Lighthouse CLI
|
||||
npx lighthouse <url> --output=json --only-categories=accessibility
|
||||
# axe-core
|
||||
npx @axe-core/cli <url> --tags wcag2a,wcag2aa,wcag22aa
|
||||
```
|
||||
|
||||
Playwright integration:
|
||||
```javascript
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
const results = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag22aa']).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
```
|
||||
|
||||
## CSS Media Queries
|
||||
|
||||
### prefers-reduced-motion
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animated { animation: fade-in 0.2s ease; transition: opacity 0.2s ease; }
|
||||
.parallax { transform: none !important; }
|
||||
.scroll-animation { animation: none; }
|
||||
}
|
||||
```
|
||||
Replace motion (slide, scale) with non-motion (fade, opacity). Keep transitions <200ms.
|
||||
|
||||
### prefers-color-scheme / prefers-contrast / forced-colors
|
||||
Always support dark mode, high contrast, and Windows forced colors.
|
||||
|
||||
## WCAG 2.2 New Criteria
|
||||
|
||||
- **2.5.8:** Touch targets min 24x24 CSS px
|
||||
- **2.4.11/12:** Focus not obscured by sticky elements
|
||||
- **3.3.7:** No redundant entry (don't re-ask info)
|
||||
- **3.3.8:** No cognitive tests for auth (allow password managers)
|
||||
- **2.5.7:** Dragging has non-drag alternative
|
||||
|
||||
## Semantic HTML Reference
|
||||
|
||||
```html
|
||||
<a href="#main" class="skip-link">Skip to content</a>
|
||||
<header><nav aria-label="Main">...</nav></header>
|
||||
<main id="main">
|
||||
<section aria-labelledby="heading"><h2 id="heading">...</h2></section>
|
||||
</main>
|
||||
<footer>...</footer>
|
||||
```
|
||||
|
||||
## Manual Checklist
|
||||
|
||||
- [ ] Keyboard-only: Tab through entire page, verify focus order
|
||||
- [ ] Skip link visible on focus
|
||||
- [ ] All interactive elements: visible focus indicator
|
||||
- [ ] Heading hierarchy: one h1, no skipped levels
|
||||
- [ ] All images: meaningful alt OR aria-hidden="true" (decorative)
|
||||
- [ ] Color contrast: 4.5:1 normal, 3:1 large (18px+ bold or 24px+)
|
||||
- [ ] Forms: visible labels, errors linked with aria-describedby
|
||||
- [ ] ARIA landmarks: header, nav, main, footer
|
||||
- [ ] Touch targets: 24x24px minimum
|
||||
- [ ] Animations: respect prefers-reduced-motion
|
||||
- [ ] Dark mode: all elements visible and contrasted
|
||||
- [ ] Video: captions present, controls accessible
|
||||
- [ ] `lang` attribute on `<html>`
|
||||
- [ ] Link text descriptive (not "click here")
|
||||
- [ ] Errors announced to screen readers (aria-live)
|
||||
61
skills/design-system/SKILL.md
Normal file
61
skills/design-system/SKILL.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
name: design-system
|
||||
description: Use when building a design system — tokens, base components, Tailwind config, dark mode, docs
|
||||
arguments:
|
||||
- name: name
|
||||
description: Design system / project name
|
||||
required: true
|
||||
- name: style
|
||||
description: "Visual direction: minimal, playful, corporate, editorial"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Design System Workflow
|
||||
|
||||
## Step 1: Define Tokens
|
||||
Design tokens (adapt to existing project if any):
|
||||
|
||||
### Colors
|
||||
- Primary: main brand color + 50-950 scale
|
||||
- Secondary: accent color + scale
|
||||
- Neutral: gray scale for text, borders, backgrounds
|
||||
- Semantic: success, warning, error, info
|
||||
- Surface: background levels (0, 1, 2, 3)
|
||||
|
||||
### Typography
|
||||
- Font families: heading, body, mono
|
||||
- Size scale: xs, sm, base, lg, xl, 2xl, 3xl, 4xl
|
||||
- Weight: normal, medium, semibold, bold
|
||||
- Line heights: tight, normal, relaxed
|
||||
|
||||
### Spacing
|
||||
- Scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24
|
||||
|
||||
### Other
|
||||
- Border radius: none, sm, md, lg, full
|
||||
- Shadows: sm, md, lg, xl
|
||||
- Transitions: fast (150ms), normal (300ms), slow (500ms)
|
||||
|
||||
## Step 2: Tailwind Config
|
||||
- For Tailwind 4: declare tokens in CSS via `@theme { ... }` (no `tailwind.config.ts`)
|
||||
- For Tailwind 3: generate `tailwind.config.ts` with all tokens
|
||||
- CSS custom properties for runtime theming
|
||||
- Dark mode: `class` strategy with token overrides
|
||||
|
||||
## Step 3: Base Components
|
||||
Core primitives (adjust to framework):
|
||||
- Button (primary, secondary, ghost, destructive + sizes)
|
||||
- Input (text, textarea, select + states)
|
||||
- Card (surface levels, padding variants)
|
||||
- Badge (status colors + sizes)
|
||||
- Typography components (Heading, Text, Label)
|
||||
|
||||
## Step 4: Dark Mode
|
||||
- Map all color tokens to dark equivalents
|
||||
- Test contrast ratios (WCAG AA minimum: 4.5:1 text, 3:1 large text)
|
||||
- Surface hierarchy inverts (dark bg, lighter cards)
|
||||
|
||||
## Step 5: Documentation
|
||||
- Token reference table
|
||||
- Component API (props, variants, examples)
|
||||
- Usage guidelines (do's and don'ts)
|
||||
55
skills/figma-to-code/SKILL.md
Normal file
55
skills/figma-to-code/SKILL.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
name: figma-to-code
|
||||
description: Use when converting Figma designs to code — screenshot, context, tokens, responsive implementation
|
||||
arguments:
|
||||
- name: url
|
||||
description: Figma URL (figma.com/design/... or figma.com/make/...)
|
||||
required: true
|
||||
---
|
||||
|
||||
# Figma to Code Workflow
|
||||
|
||||
## Step 1: Extract from Figma
|
||||
- Parse URL to get fileKey and nodeId
|
||||
- Call `get_design_context` with fileKey and nodeId (Figma MCP / REST)
|
||||
- Call `get_screenshot` for visual reference
|
||||
- Review returned code, tokens, and component mappings
|
||||
|
||||
## Step 2: Analyze Design
|
||||
From the Figma output, identify:
|
||||
- **Layout:** flex/grid structure, spacing, alignment
|
||||
- **Typography:** font family, size, weight, line-height, color
|
||||
- **Colors:** map to project's design tokens or CSS variables
|
||||
- **Components:** map to existing project components
|
||||
- **Responsive hints:** auto-layout direction, min/max widths
|
||||
- **Interactions:** hover states, transitions, animations
|
||||
|
||||
## Step 3: Map to Project
|
||||
- Match Figma colors → project design tokens
|
||||
- Match Figma fonts → project typography scale
|
||||
- Match Figma components → existing project components
|
||||
- Identify gaps: new components or tokens needed
|
||||
- If Code Connect mappings exist, use them directly
|
||||
|
||||
## Step 4: Implement
|
||||
- Use project's stack (React, Next.js, Astro, SvelteKit, etc.)
|
||||
- Mobile-first responsive implementation
|
||||
- Match pixel-perfect on design breakpoint
|
||||
- Adapt gracefully to other breakpoints
|
||||
- Use design system components where they match
|
||||
|
||||
### Responsive Breakpoints
|
||||
- Mobile: 375px (default)
|
||||
- Tablet: 768px
|
||||
- Desktop: 1024px
|
||||
- Wide: 1280px
|
||||
|
||||
## Step 5: Verify
|
||||
- Compare screenshot with implementation side-by-side
|
||||
- Check all breakpoints
|
||||
- Verify interactive states (hover, focus, active)
|
||||
- Accessibility check (contrast, keyboard nav, ARIA)
|
||||
- Cross-browser check (Chrome, Safari, Firefox)
|
||||
|
||||
## Step 6: Commit
|
||||
- `feat: implement <component/page> from Figma design`
|
||||
111
skills/form-builder/SKILL.md
Normal file
111
skills/form-builder/SKILL.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
name: form-builder
|
||||
description: Use when building forms — multi-step wizards, Zod validation, anti-spam (Turnstile), serverless backends, file upload, progressive enhancement. Triggers on "form", "contact form", "wizard", "form validation", "turnstile".
|
||||
arguments:
|
||||
- name: command
|
||||
description: "Command: create, validate, backend, spam, analytics, audit"
|
||||
required: false
|
||||
- name: type
|
||||
description: "Form type: contact, multi-step, file-upload, survey"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Form Construction & Submission
|
||||
|
||||
Progressive enhancement by default. Forms MUST work without JavaScript.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
User → Client Validation (Zod) → Submit
|
||||
↓ (JS disabled: standard POST)
|
||||
Server Action/Worker → Server Validation (same Zod schema)
|
||||
↓
|
||||
Anti-spam (Turnstile) → Process → Email (Resend) / Webhook / D1
|
||||
```
|
||||
|
||||
## Validation: Zod v4 + react-hook-form v7
|
||||
|
||||
**Single schema shared between client and server (SSoT):**
|
||||
|
||||
```typescript
|
||||
// schemas/contact.ts
|
||||
import { z } from 'zod';
|
||||
export const contactSchema = z.object({
|
||||
name: z.string().min(2, 'Name must be at least 2 characters'),
|
||||
email: z.string().email('Invalid email address'),
|
||||
company: z.string().optional(),
|
||||
message: z.string().min(10).max(5000),
|
||||
budget: z.enum(['<5k', '5k-15k', '15k-50k', '50k+']),
|
||||
});
|
||||
export type ContactFormData = z.infer<typeof contactSchema>;
|
||||
```
|
||||
|
||||
**Client form:**
|
||||
```tsx
|
||||
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<ContactFormData>({
|
||||
resolver: zodResolver(contactSchema),
|
||||
});
|
||||
// method="POST" action="/api/contact" — works without JS
|
||||
// noValidate — use Zod, not browser
|
||||
// aria-describedby + aria-invalid + role="alert" for a11y
|
||||
```
|
||||
|
||||
**WARNING:** react-hook-form v8 in beta with breaking changes. Stick to v7.
|
||||
|
||||
## Multi-Step Wizard
|
||||
|
||||
- Schema per step, merged for final validation
|
||||
- `sessionStorage` for persistence across refreshes
|
||||
- Progress indicator, back navigation, summary before submit
|
||||
- Validate current step before "Next"
|
||||
|
||||
## Anti-Spam
|
||||
|
||||
### Cloudflare Turnstile (DEFAULT — free, unlimited, privacy-friendly)
|
||||
```html
|
||||
<div class="cf-turnstile" data-sitekey="YOUR_KEY"></div>
|
||||
```
|
||||
Server: verify via `challenges.cloudflare.com/turnstile/v0/siteverify`
|
||||
|
||||
### Honeypot (always layer with Turnstile)
|
||||
```html
|
||||
<div style="position:absolute;left:-9999px" aria-hidden="true">
|
||||
<input type="text" name="website" tabindex="-1" autocomplete="off" />
|
||||
</div>
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
5 submissions/IP/hour via Cloudflare KV.
|
||||
|
||||
## Backends
|
||||
|
||||
| Backend | Best For |
|
||||
|---------|----------|
|
||||
| CF Worker + Resend | Email notifications (DEFAULT) |
|
||||
| Webhook | Slack/Discord/Zapier/n8n |
|
||||
| D1 | Persistent storage + analytics |
|
||||
| R2 presigned URL | File uploads (>5MB use multipart) |
|
||||
|
||||
## Form Types
|
||||
|
||||
| Type | Fields | Anti-Spam | Backend |
|
||||
|------|--------|-----------|---------|
|
||||
| Contact | name, email, message, budget? | Turnstile + honeypot | Resend + webhook |
|
||||
| Multi-step | per-step schemas | Turnstile on final | D1 + Resend |
|
||||
| File upload | name, email, file(s) | Turnstile + rate limit | R2 presigned |
|
||||
| Survey | rating, category, text | honeypot + rate limit | D1 |
|
||||
|
||||
## Audit Checklist
|
||||
|
||||
- [ ] All fields: visible `<label>`, aria-describedby for errors
|
||||
- [ ] Works without JS (method + action set)
|
||||
- [ ] Server validation matches client (same Zod schema)
|
||||
- [ ] Anti-spam: honeypot minimum, Turnstile preferred
|
||||
- [ ] Rate limiting on endpoint
|
||||
- [ ] File uploads: presigned URLs (not Worker proxy)
|
||||
- [ ] Input types match data (email, tel, url)
|
||||
- [ ] Autocomplete attributes set
|
||||
- [ ] Submit disabled during submission
|
||||
- [ ] Success/error announced to screen readers
|
||||
- [ ] Mobile: 44x44px touch targets, appropriate keyboards
|
||||
144
skills/frontend-design/SKILL.md
Normal file
144
skills/frontend-design/SKILL.md
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
---
|
||||
name: frontend-design
|
||||
description: Use when designing web UI before coding — anti-AI-slop aesthetic philosophy, typography pairing, color theory, spatial composition, motion guidelines, design archetypes. Triggers on "design", "UI design", "frontend design", "anti-slop", "make it look premium", "design thinking".
|
||||
arguments:
|
||||
- name: archetype
|
||||
description: "Archetype: editorial, swiss, brutalist, minimal, maximalist, retro-futuristic, organic, industrial, art-deco, lo-fi (auto-suggest if omitted)"
|
||||
required: false
|
||||
- name: differentiator
|
||||
description: "The ONE thing someone will remember about this design"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Frontend Design — Think Before You Code
|
||||
|
||||
> Design-first, code-second. Every implementation starts with a design decision, not a div.
|
||||
|
||||
## Phase Gate (MANDATORY before writing any UI code)
|
||||
|
||||
1. **Purpose** — What problem? Who uses it? (1 sentence)
|
||||
2. **Archetype** — Pick from 10 below (sets the aesthetic DNA)
|
||||
3. **Differentiator** — "The one thing someone remembers" (1 sentence)
|
||||
4. **Anti-references** — Name 3 sites/patterns this is NOT
|
||||
5. **Tokens** — Define palette + fonts + spacing in CSS variables
|
||||
|
||||
Skip this gate = skip the skill. Code without design intent = AI slop.
|
||||
|
||||
## Hard Bans (Anti-AI-Slop)
|
||||
|
||||
**Typography:**
|
||||
- Inter, Roboto, Arial, system font stacks
|
||||
- Space Grotesk (overused in AI-generated sites)
|
||||
- Same font for heading and body
|
||||
|
||||
**Color:**
|
||||
- Purple gradients on white backgrounds
|
||||
- Evenly distributed palettes (everything gets equal weight)
|
||||
- Pure #000 or #fff without tinting
|
||||
|
||||
**Layout:**
|
||||
- Centered card grids as default composition
|
||||
- Hero → Cards → Testimonials → Footer (the template trap)
|
||||
- Even spacing everywhere (no rhythm)
|
||||
|
||||
**Motion:**
|
||||
- `linear` easing on UI transitions
|
||||
- `scale(0)` animation origins
|
||||
- Default `ease` without custom cubic-bezier
|
||||
|
||||
## 10 Archetypes
|
||||
|
||||
| # | Name | Typography | Color | Layout | Motion |
|
||||
|---|------|-----------|-------|--------|--------|
|
||||
| 1 | **Editorial** | Serif display + sans body | Warm neutrals + 1 accent | Asymmetric columns, pull quotes | Subtle parallax, text reveals |
|
||||
| 2 | **Swiss** | Geometric sans (Helvetica Now, Neue Haas) | Black/white + 1 primary | Strict grid, mathematical spacing | Minimal, precision timing |
|
||||
| 3 | **Brutalist** | Monospace or system | Raw, high contrast | Exposed structure, raw HTML aesthetic | Glitch, intentional jank |
|
||||
| 4 | **Minimal** | 1 refined sans, extreme weight contrast | 2 colors max + neutral | Massive whitespace, single column | Fade only, ultra-slow |
|
||||
| 5 | **Maximalist** | Mixed display fonts, decorative | Saturated, 4+ colors | Layered, overlapping, collage | Everything moves, scroll-driven |
|
||||
| 6 | **Retro-Futuristic** | Futuristic display + mono | Neon on dark, CRT glow | Scanlines, terminal aesthetic | Typing effects, flicker |
|
||||
| 7 | **Organic** | Rounded sans + handwritten accent | Earth tones, muted | Curved containers, blob shapes | Fluid, spring physics |
|
||||
| 8 | **Industrial** | Condensed bold sans | Dark grays + safety yellow/orange | Dense info, data-heavy | Mechanical, step-based |
|
||||
| 9 | **Art Deco** | Geometric display, high contrast weight | Gold/brass + deep navy/black | Symmetrical, ornamental borders | Elegant reveals, fade + scale |
|
||||
| 10 | **Lo-Fi** | Hand-drawn or pixel font | Paper/notebook palette | Sketch-like borders, tape/sticker elements | Wobbly, imperfect |
|
||||
|
||||
## Typography Rules
|
||||
|
||||
- Max 2 fonts: 1 display (headings) + 1 body (text)
|
||||
- Use `clamp()` for fluid scaling: `font-size: clamp(1rem, 2.5vw, 1.5rem)`
|
||||
- Body `line-height`: 1.4-1.6 | Display `line-height`: 1.0-1.2
|
||||
- 3-5 clear hierarchy levels with dramatic size contrast (4:1 heading-to-body)
|
||||
- Tune `letter-spacing` per size: tighter for large, looser for small caps
|
||||
- `font-feature-settings` for ligatures, tabular numbers where needed
|
||||
|
||||
## Color System (OKLCH)
|
||||
|
||||
```css
|
||||
@theme {
|
||||
--brand-hue: 250;
|
||||
--color-primary: oklch(0.6 0.2 var(--brand-hue));
|
||||
--color-surface: oklch(0.995 0.005 var(--brand-hue));
|
||||
--color-text: oklch(0.15 0.02 var(--brand-hue));
|
||||
--color-muted: oklch(0.55 0.01 var(--brand-hue));
|
||||
--color-accent: oklch(0.7 0.25 calc(var(--brand-hue) + 30));
|
||||
--color-border: oklch(0.9 0.01 var(--brand-hue));
|
||||
}
|
||||
```
|
||||
|
||||
**60-30-10 rule:** 60% dominant (surface/bg), 30% secondary (text/containers), 10% accent (CTAs, highlights).
|
||||
|
||||
OKLCH = perceptually uniform. One `--brand-hue` controls entire palette.
|
||||
|
||||
## Spatial Composition
|
||||
|
||||
- Consistent scale: `--space-xs: 0.25rem` through `--space-3xl: 4rem`
|
||||
- Whitespace is structural, not leftover
|
||||
- At least ONE grid-breaking moment per page (full-bleed, overlap, offset)
|
||||
- 8px base grid for alignment
|
||||
- Dramatic rhythm changes between sections (dense → spacious → dense)
|
||||
|
||||
## Visual Depth & Texture
|
||||
|
||||
- Noise/grain via SVG `<feTurbulence>` filter or CSS pseudo-element
|
||||
- Multi-value `box-shadow` for realistic depth
|
||||
- `backdrop-filter: blur()` for glass effects
|
||||
- `clip-path` for non-rectangular shapes
|
||||
- Background: gradients, patterns, grain — never flat solid white
|
||||
|
||||
## Motion Guidelines
|
||||
|
||||
- Custom `cubic-bezier()` per element — never default `ease`
|
||||
- Staggered page-load: 50-100ms increments between elements
|
||||
- Duration: productivity UI <300ms, creative 200-500ms
|
||||
- Spring physics for interactive elements (bounce: 0, no jello)
|
||||
- Exit animations subtler than enter
|
||||
- `prefers-reduced-motion`: replace motion with fade, keep <200ms
|
||||
- Keyboard-initiated actions: NO animation
|
||||
|
||||
### Enter Animation Recipe (Motion/Framer Motion)
|
||||
|
||||
```jsx
|
||||
initial={{ opacity: 0, y: 8, filter: "blur(4px)" }}
|
||||
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
|
||||
transition={{ type: "spring", duration: 0.45, bounce: 0 }}
|
||||
```
|
||||
|
||||
## Output Contract
|
||||
|
||||
Every frontend-design invocation MUST produce:
|
||||
1. **Stated direction** — archetype + differentiator + anti-references
|
||||
2. **Design tokens** — CSS custom properties (colors, type, spacing)
|
||||
3. **Typography selection** — 2 fonts with Google Fonts / Fontsource links
|
||||
4. **Working code** — implementation matching the stated direction
|
||||
5. **Responsiveness** — mobile-first, tested at 375px and 1280px
|
||||
|
||||
## The Blur Test
|
||||
|
||||
At 20% visibility, the layout silhouette should be distinguishable from anti-references. If blurred Stripe and blurred Your-Page look the same → composition is not distinctive.
|
||||
|
||||
## Diverge-Kill-Mutate
|
||||
|
||||
If output feels generic:
|
||||
1. **Diverge** — generate 3 structurally different variants (different spatial logic, not color swaps)
|
||||
2. **Kill** — binary: alive or dead. NO blending (blending = averaging = AI slop)
|
||||
3. **Mutate** — within survivor, introduce named "breaks" (violations of convention)
|
||||
4. **Repeat** — each cycle moves further from center
|
||||
188
skills/landing-page/SKILL.md
Normal file
188
skills/landing-page/SKILL.md
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
---
|
||||
name: landing-page
|
||||
description: Use when creating a landing page — orchestrates design, copy, assets, animations, SEO. Supports recipe system for specific page types (apple-product, saas, portfolio, ecommerce). Triggers on "landing page", "create page", "website".
|
||||
arguments:
|
||||
- name: product
|
||||
description: Product/service name and brief description
|
||||
required: true
|
||||
- name: recipe
|
||||
description: "Recipe: apple-product, saas, portfolio, ecommerce, agency, startup (auto-suggest if omitted)"
|
||||
required: false
|
||||
- name: goal
|
||||
description: "Page goal: signups, downloads, waitlist, sales, portfolio showcase"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Landing Page Orchestrator
|
||||
|
||||
Creates premium landing pages by composing specialized skills.
|
||||
|
||||
## Step 1: Design Direction
|
||||
|
||||
Invoke `/frontend-design` with product context:
|
||||
- Suggest archetype based on recipe (see matrix below)
|
||||
- Define differentiator, anti-references, tokens
|
||||
- Output: design direction + CSS custom properties
|
||||
|
||||
## Step 2: Research & Copy
|
||||
|
||||
- Understand product: features, audience, value proposition
|
||||
- WebSearch 3-5 competitors for positioning
|
||||
- Write copy: headline (<10 words, benefit-driven), subheadline, CTAs, feature descriptions
|
||||
- Tone matches archetype from Step 1
|
||||
|
||||
## Step 3: Page Structure
|
||||
|
||||
Adapt structure to recipe (see below). Core sections:
|
||||
1. **Hero** — headline, subheadline, CTA, visual
|
||||
2. **Problem** — pain point (empathy)
|
||||
3. **Solution** — how product solves it (3 features max)
|
||||
4. **Social proof** — testimonials, metrics, logos
|
||||
5. **How it works** — 3-step process
|
||||
6. **Pricing** (if applicable)
|
||||
7. **FAQ** (3-5 questions)
|
||||
8. **Final CTA** — repeat conversion action
|
||||
|
||||
## Step 4: Implementation
|
||||
|
||||
- Framework: Astro 6 (default for marketing) or project's stack
|
||||
- Invoke skills per recipe (see matrix)
|
||||
- Mobile-first responsive design
|
||||
- Performance: lazy load below-fold, optimize all assets
|
||||
|
||||
## Step 5: Quality Pipeline
|
||||
|
||||
Sequential audit chain:
|
||||
1. `/web-assets audit` — image formats, sizes, fonts
|
||||
2. `/a11y-audit scan` — WCAG 2.2 AA compliance
|
||||
3. `/seo-audit` — meta, headings, schema, OG tags
|
||||
4. `/responsive-audit` — 6 breakpoints
|
||||
5. `/perf-audit` — Lighthouse >90
|
||||
|
||||
## Step 6: Deploy
|
||||
|
||||
`/web-deploy deploy` — Cloudflare Pages (default)
|
||||
|
||||
---
|
||||
|
||||
## Recipe System
|
||||
|
||||
### `apple-product` — Premium Product Reveal
|
||||
|
||||
**Archetype:** Minimal or Swiss
|
||||
**Skills invoked:** ai-animation, scroll-animation, video-gen, 3d-scene, web-assets, motion-design
|
||||
|
||||
**Structure:**
|
||||
1. Hero: product floating in space, minimal text
|
||||
2. Video scrub section: product rotation/reveal on scroll (frame sequence or 3D)
|
||||
3. Feature deep-dives: pin + scrub with parallax text
|
||||
4. Specs grid: bento layout with micro-animations
|
||||
5. CTA: clean, single action
|
||||
|
||||
**Key techniques:**
|
||||
- Frame sequence (120-180 WebP frames) or Three.js model with ScrollControls
|
||||
- GSAP ScrollTrigger pin + scrub
|
||||
- Lenis smooth scroll
|
||||
- Staggered text reveals with blur-in animation
|
||||
- Dark background, dramatic lighting
|
||||
|
||||
### `saas` — SaaS Product Landing
|
||||
|
||||
**Archetype:** Minimal or Editorial
|
||||
**Skills invoked:** motion-design, ui-component, web-assets, form-builder
|
||||
|
||||
**Structure:**
|
||||
1. Hero: headline + product screenshot/video + CTA
|
||||
2. Logo bar: client/integration logos
|
||||
3. Features: bento grid (3-6 cards) with hover micro-interactions
|
||||
4. Demo: embedded video or interactive preview
|
||||
5. Testimonials: carousel or grid with photos
|
||||
6. Pricing: 2-3 tier comparison table
|
||||
7. FAQ: accordion
|
||||
8. CTA: signup form (Turnstile + Zod)
|
||||
|
||||
**Key techniques:**
|
||||
- Bento grid layout with staggered entrance
|
||||
- View Transitions for page navigation
|
||||
- Dark/light mode toggle
|
||||
- Micro-interactions on every card (hover scale, shadow elevation)
|
||||
- Auto-animate for list/grid transitions
|
||||
|
||||
### `portfolio` — Creative Portfolio
|
||||
|
||||
**Archetype:** Editorial or Maximalist
|
||||
**Skills invoked:** scroll-animation, web-effects, motion-design, 3d-scene
|
||||
|
||||
**Structure:**
|
||||
1. Hero: kinetic typography (name/title animates on load)
|
||||
2. Project showcase: horizontal scroll or masonry grid
|
||||
3. Project detail: image distortion on hover (WebGL)
|
||||
4. About: asymmetric editorial layout
|
||||
5. Contact: minimal form
|
||||
|
||||
**Key techniques:**
|
||||
- Custom cursor that reacts to content
|
||||
- Image distortion on hover (curtains.js displacement)
|
||||
- GSAP horizontal scroll for project gallery
|
||||
- SVG line drawing for decorative elements
|
||||
- Kinetic typography with SplitText
|
||||
|
||||
### `ecommerce` — Product E-Commerce
|
||||
|
||||
**Archetype:** Minimal or Organic
|
||||
**Skills invoked:** ui-component, web-assets, form-builder, motion-design
|
||||
|
||||
**Structure:**
|
||||
1. Hero: product lifestyle image + CTA
|
||||
2. Product grid: filterable with auto-animate transitions
|
||||
3. Product detail: gallery + variant selector + add-to-cart
|
||||
4. Reviews: social proof grid
|
||||
5. Related products: horizontal scroll
|
||||
6. Trust: shipping, returns, secure payment badges
|
||||
|
||||
**Key techniques:**
|
||||
- Image zoom on hover
|
||||
- Variant selector with instant preview update
|
||||
- Add-to-cart animation (fly to cart icon)
|
||||
- Skeleton loading states
|
||||
- Optimistic UI updates
|
||||
|
||||
### `agency` — Creative Agency
|
||||
|
||||
**Archetype:** Brutalist or Swiss
|
||||
**Skills invoked:** scroll-animation, web-effects, 3d-scene, motion-design
|
||||
|
||||
**Structure:**
|
||||
1. Hero: bold statement + reel/showreel video
|
||||
2. Services: icon grid with hover reveals
|
||||
3. Case studies: full-bleed image + overlay text
|
||||
4. Team: grid with playful hover effects
|
||||
5. Process: timeline with scroll-linked progress
|
||||
6. Contact: multi-step form
|
||||
|
||||
**Key techniques:**
|
||||
- Full-screen video hero (AV1 + H.264 fallback)
|
||||
- Noise/grain texture overlay
|
||||
- Scroll-driven timeline with pin sections
|
||||
- Magnetic cursor on interactive elements
|
||||
- Page transitions with View Transitions API
|
||||
|
||||
### `startup` — Early-Stage Startup
|
||||
|
||||
**Archetype:** Minimal or Retro-Futuristic
|
||||
**Skills invoked:** motion-design, form-builder, web-assets
|
||||
|
||||
**Structure:**
|
||||
1. Hero: problem statement + waitlist CTA
|
||||
2. Pain points: 3 illustrated scenarios
|
||||
3. Solution: how it works (3 steps)
|
||||
4. Early metrics/traction (if available)
|
||||
5. Founder story (optional)
|
||||
6. Waitlist form with social proof counter
|
||||
|
||||
**Key techniques:**
|
||||
- Simple fade-in animations (AutoAnimate)
|
||||
- Email capture with Turnstile
|
||||
- Social proof: "Join 1,234 others" counter
|
||||
- Minimal JavaScript, maximum speed
|
||||
- Ship fast: Astro + Tailwind + Cloudflare Pages
|
||||
347
skills/motion-design/SKILL.md
Normal file
347
skills/motion-design/SKILL.md
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
---
|
||||
name: motion-design
|
||||
description: Use when implementing motion design — page transitions, element animations, micro-interactions, layout animations. Covers Motion (ex Framer Motion), View Transitions API, auto-animate, SVG animation (Rive, Lottie), and accessibility.
|
||||
arguments:
|
||||
- name: type
|
||||
description: "Type: page-transition, micro-interaction, layout-animation, svg-animation, loading, hover (auto-detect if omitted)"
|
||||
required: false
|
||||
- name: framework
|
||||
description: "Framework: react, next, astro, vue, svelte, vanilla (auto-detect if omitted)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Motion Design Skill
|
||||
|
||||
## Decision Matrix — Pick Library
|
||||
|
||||
| Need | Library | Bundle | Why |
|
||||
|------|---------|--------|-----|
|
||||
| React component animations | Motion 12 | ~32KB gzip | Best React DX, layout animations |
|
||||
| Page transitions (MPA) | View Transitions API | 0KB | Native browser API |
|
||||
| Page transitions (Astro) | Astro View Transitions | 0KB | Built-in, zero JS |
|
||||
| Zero-config list animations | AutoAnimate | ~2KB gzip | One line, FLIP-based |
|
||||
| Interactive vector graphics | Rive | ~78KB WASM | State machines, 60fps |
|
||||
| After Effects exports | Lottie/dotLottie | ~50KB runtime | Huge asset library |
|
||||
| SVG path morphing | GSAP MorphSVG | included in gsap | Now free, best morph engine |
|
||||
| Line drawing | CSS stroke-dasharray | 0KB | Pure CSS, no library |
|
||||
|
||||
---
|
||||
|
||||
## 1. Motion (ex Framer Motion)
|
||||
|
||||
**Install:** `npm i motion`
|
||||
**Bundle:** ~32KB min+gzip
|
||||
|
||||
### Core API
|
||||
|
||||
```jsx
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
|
||||
// Basic animation
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
Content
|
||||
</motion.div>
|
||||
|
||||
// Layout animation (FLIP under the hood)
|
||||
<motion.div layout layoutId="card-expand">
|
||||
{isExpanded ? <ExpandedCard /> : <CompactCard />}
|
||||
</motion.div>
|
||||
|
||||
// AnimatePresence — exit animations
|
||||
<AnimatePresence mode="wait">
|
||||
{items.map(item => (
|
||||
<motion.div
|
||||
key={item.id}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.9 }}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
```
|
||||
|
||||
### Gestures
|
||||
|
||||
```jsx
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
transition={{ type: "spring", stiffness: 400, damping: 17 }}
|
||||
>
|
||||
Click me
|
||||
</motion.button>
|
||||
|
||||
// Drag
|
||||
<motion.div
|
||||
drag="x"
|
||||
dragConstraints={{ left: -200, right: 200 }}
|
||||
dragElastic={0.1}
|
||||
/>
|
||||
```
|
||||
|
||||
### Scroll-Linked
|
||||
|
||||
```jsx
|
||||
import { useScroll, useTransform, motion } from "motion/react";
|
||||
|
||||
function ParallaxHero() {
|
||||
const { scrollYProgress } = useScroll();
|
||||
const y = useTransform(scrollYProgress, [0, 1], [0, -300]);
|
||||
const opacity = useTransform(scrollYProgress, [0, 0.5], [1, 0]);
|
||||
|
||||
return (
|
||||
<motion.div style={{ y, opacity }}>
|
||||
Hero Content
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### AnimateView (View Transitions integration)
|
||||
|
||||
```jsx
|
||||
import { AnimateView } from "motion/react";
|
||||
|
||||
<AnimateView>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
</Routes>
|
||||
</AnimateView>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. View Transitions API
|
||||
|
||||
### Vanilla Implementation
|
||||
|
||||
```js
|
||||
// Single-document transition
|
||||
document.startViewTransition(() => {
|
||||
container.innerHTML = newContent;
|
||||
});
|
||||
```
|
||||
|
||||
```css
|
||||
::view-transition-old(root) { animation: fade-out 0.2s ease-out; }
|
||||
::view-transition-new(root) { animation: fade-in 0.3s ease-in; }
|
||||
|
||||
.hero-image { view-transition-name: hero; }
|
||||
```
|
||||
|
||||
### Astro Integration (Built-in)
|
||||
|
||||
```astro
|
||||
---
|
||||
import { ViewTransitions } from "astro:transitions";
|
||||
---
|
||||
<html>
|
||||
<head><ViewTransitions /></head>
|
||||
<body><slot /></body>
|
||||
</html>
|
||||
|
||||
<img transition:name="hero" src="/hero.jpg" />
|
||||
<h1 transition:animate="slide">Page Title</h1>
|
||||
```
|
||||
|
||||
Built-in animation presets: `fade`, `slide`, `morph`, `none`.
|
||||
|
||||
---
|
||||
|
||||
## 3. AutoAnimate
|
||||
|
||||
**Install:** `npm i @formkit/auto-animate`
|
||||
**Zero config.** Uses FLIP technique internally.
|
||||
|
||||
```jsx
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
|
||||
function TodoList({ items }) {
|
||||
const [parent] = useAutoAnimate();
|
||||
return (
|
||||
<ul ref={parent}>
|
||||
{items.map(item => <li key={item.id}>{item.text}</li>)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
// Vanilla JS
|
||||
import autoAnimate from "@formkit/auto-animate";
|
||||
autoAnimate(document.getElementById("list"));
|
||||
```
|
||||
|
||||
**Best for:** List reordering, add/remove items, accordion expand/collapse.
|
||||
|
||||
---
|
||||
|
||||
## 4. SVG Animation
|
||||
|
||||
### Rive
|
||||
|
||||
```jsx
|
||||
import Rive from "@rive-app/react-canvas";
|
||||
|
||||
<Rive
|
||||
src="/animations/hero.riv"
|
||||
stateMachines="MainState"
|
||||
style={{ width: 400, height: 400 }}
|
||||
/>
|
||||
```
|
||||
|
||||
**Key features:** State Machines, layout engine, scroll-linked via data inputs.
|
||||
**When to use:** Interactive illustrations, mascots, loading states, onboarding flows.
|
||||
|
||||
### Lottie / dotLottie
|
||||
|
||||
```jsx
|
||||
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
|
||||
|
||||
<DotLottieReact
|
||||
src="/animations/hero.lottie"
|
||||
loop
|
||||
autoplay
|
||||
style={{ width: 300, height: 300 }}
|
||||
/>
|
||||
```
|
||||
|
||||
**Rive vs Lottie:**
|
||||
| Factor | Rive | Lottie |
|
||||
|--------|------|--------|
|
||||
| Interactivity | Built-in state machine | Manual JS coding |
|
||||
| Design tool | Rive editor | After Effects + plugin |
|
||||
| File size | Smaller (binary) | Larger (JSON) |
|
||||
| Asset ecosystem | Growing | Massive marketplace |
|
||||
|
||||
### SVG Morphing
|
||||
|
||||
**GSAP MorphSVG** (now free with gsap):
|
||||
```js
|
||||
gsap.to("#star", { morphSVG: "#circle", duration: 1, ease: "power2.inOut" });
|
||||
```
|
||||
|
||||
**SVG points limit:** Keep under 200 points for smooth 60fps morphing.
|
||||
|
||||
### Line Drawing (Pure CSS)
|
||||
|
||||
```css
|
||||
.svg-line {
|
||||
stroke-dasharray: 1000;
|
||||
stroke-dashoffset: 1000;
|
||||
animation: draw 2s ease forwards;
|
||||
}
|
||||
@keyframes draw { to { stroke-dashoffset: 0; } }
|
||||
```
|
||||
|
||||
Get path length: `document.querySelector("path").getTotalLength()`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Micro-Interaction Patterns
|
||||
|
||||
### Button Hover/Tap
|
||||
|
||||
```jsx
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.03, boxShadow: "0 4px 20px rgba(0,0,0,0.15)" }}
|
||||
whileTap={{ scale: 0.97 }}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 25 }}
|
||||
/>
|
||||
```
|
||||
|
||||
### Toast/Notification Enter
|
||||
|
||||
```jsx
|
||||
<AnimatePresence>
|
||||
{toast && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 50, scale: 0.9 }}
|
||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||
exit={{ opacity: 0, y: 20, scale: 0.95 }}
|
||||
transition={{ type: "spring", damping: 20 }}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
```
|
||||
|
||||
### Staggered List
|
||||
|
||||
```jsx
|
||||
const container = { animate: { transition: { staggerChildren: 0.06 } } };
|
||||
const item = { initial: { opacity: 0, y: 15 }, animate: { opacity: 1, y: 0 } };
|
||||
|
||||
<motion.ul variants={container} initial="initial" animate="animate">
|
||||
{items.map(i => <motion.li key={i} variants={item} />)}
|
||||
</motion.ul>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Timing & Easing Reference
|
||||
|
||||
### Duration Guidelines
|
||||
|
||||
| Element | Duration | Easing |
|
||||
|---------|----------|--------|
|
||||
| Button hover | 150-200ms | ease-out |
|
||||
| Tooltip appear | 100-150ms | ease-out |
|
||||
| Modal enter | 200-300ms | ease-out / spring |
|
||||
| Modal exit | 150-200ms | ease-in |
|
||||
| Page transition | 200-400ms | ease-in-out |
|
||||
| Layout shift | 200-350ms | ease-out / spring |
|
||||
| Scroll reveal | 400-600ms | ease-out |
|
||||
|
||||
### Spring Presets (Motion)
|
||||
|
||||
```js
|
||||
// Snappy UI feedback
|
||||
{ type: "spring", stiffness: 500, damping: 25 }
|
||||
// Smooth layout
|
||||
{ type: "spring", stiffness: 300, damping: 30 }
|
||||
// Bouncy/playful
|
||||
{ type: "spring", stiffness: 400, damping: 10 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Accessibility
|
||||
|
||||
### prefers-reduced-motion
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```jsx
|
||||
// Motion respects prefers-reduced-motion by default
|
||||
const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
```
|
||||
|
||||
### Guidelines
|
||||
|
||||
- Never rely on animation alone to convey information
|
||||
- Ensure all animated content is accessible via keyboard
|
||||
- Provide static fallback for critical content
|
||||
- Test with reduced motion enabled in OS settings
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Identify animation type** — page transition, reveal, micro-interaction, SVG
|
||||
2. **Pick library** — use Decision Matrix above
|
||||
3. **Define timing** — use Duration Guidelines, spring presets
|
||||
4. **Implement** — start with `initial` + `animate` states
|
||||
5. **Add exit** — wrap in AnimatePresence for unmount animations
|
||||
6. **Add a11y** — prefers-reduced-motion, keyboard testing
|
||||
7. **Performance audit** — Chrome DevTools, check for layout thrashing
|
||||
51
skills/perf-audit/SKILL.md
Normal file
51
skills/perf-audit/SKILL.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
name: perf-audit
|
||||
description: Use when auditing performance — baseline, profile, identify top 3 bottlenecks, fix, remeasure
|
||||
arguments:
|
||||
- name: target
|
||||
description: "What to audit: endpoint, page, function, or 'full'"
|
||||
required: true
|
||||
---
|
||||
|
||||
# Performance Audit Workflow
|
||||
|
||||
## Step 1: Establish Baseline
|
||||
- Measure current performance:
|
||||
- API: response time (p50, p95, p99), throughput
|
||||
- Frontend: LCP, FID, CLS, bundle size
|
||||
- Function: execution time, memory usage
|
||||
- Record numbers BEFORE any changes
|
||||
- Use project's existing tools or:
|
||||
- Python: `time`, `cProfile`, `memory_profiler`
|
||||
- JS/TS: `performance.now()`, Lighthouse, `webpack-bundle-analyzer`
|
||||
- API: `curl -w @-` timing, `ab`, `wrk`
|
||||
|
||||
## Step 2: Profile
|
||||
- Identify WHERE time is spent:
|
||||
- Database queries (N+1, missing indexes, full scans)
|
||||
- Network calls (sequential vs parallel, caching)
|
||||
- CPU (algorithmic complexity, unnecessary computation)
|
||||
- Memory (leaks, large allocations, unnecessary copies)
|
||||
- I/O (file reads, disk writes)
|
||||
|
||||
## Step 3: Identify Top 3 Bottlenecks
|
||||
- Rank by impact (% of total time)
|
||||
- Focus on top 3 — don't optimize everything
|
||||
- For each: document what, why slow, potential fix
|
||||
|
||||
## Step 4: Checkpoint
|
||||
- `checkpoint: before perf-audit $target`
|
||||
|
||||
## Step 5: Fix (One at a Time)
|
||||
- Fix #1 bottleneck → measure → confirm improvement
|
||||
- Fix #2 bottleneck → measure → confirm improvement
|
||||
- Fix #3 bottleneck → measure → confirm improvement
|
||||
- After each fix: run tests — no regressions
|
||||
|
||||
## Step 6: Final Measurement
|
||||
- Re-run baseline measurements
|
||||
- Compare before/after
|
||||
- Report: metric, before, after, improvement %
|
||||
|
||||
## Step 7: Commit
|
||||
- `perf: optimize $target — <summary of improvements>`
|
||||
65
skills/responsive-audit/SKILL.md
Normal file
65
skills/responsive-audit/SKILL.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
---
|
||||
name: responsive-audit
|
||||
description: Use when auditing responsive design — 6 breakpoints, layout, touch targets, overflow, images
|
||||
arguments:
|
||||
- name: target
|
||||
description: Page or component path to audit
|
||||
required: true
|
||||
---
|
||||
|
||||
# Responsive Audit Workflow
|
||||
|
||||
## Step 1: Identify Target
|
||||
- Read the target file(s)
|
||||
- Understand the layout structure (flex, grid, absolute, etc.)
|
||||
- List all breakpoint-dependent styles
|
||||
|
||||
## Step 2: Audit Each Breakpoint
|
||||
|
||||
### Mobile (375px)
|
||||
- [ ] Single column layout where appropriate
|
||||
- [ ] Touch targets min 44x44px
|
||||
- [ ] No horizontal scroll
|
||||
- [ ] Font size min 16px for body text
|
||||
- [ ] Adequate spacing between interactive elements
|
||||
|
||||
### Small Mobile (320px)
|
||||
- [ ] No content overflow or truncation breaking layout
|
||||
- [ ] Navigation still usable
|
||||
- [ ] Forms still fillable
|
||||
|
||||
### Tablet (768px)
|
||||
- [ ] Layout adapts (2-column where appropriate)
|
||||
- [ ] Images scale properly
|
||||
- [ ] Navigation adapts (hamburger → tabs or vice versa)
|
||||
|
||||
### Desktop (1024px)
|
||||
- [ ] Full layout utilizes space
|
||||
- [ ] Max content width set (not stretching to infinity)
|
||||
- [ ] Sidebar/aside content visible if applicable
|
||||
|
||||
### Wide (1280px)
|
||||
- [ ] Content centered or max-width contained
|
||||
- [ ] No excessive whitespace
|
||||
- [ ] Images don't pixelate
|
||||
|
||||
### Ultra-wide (1920px+)
|
||||
- [ ] Layout doesn't break
|
||||
- [ ] Content doesn't stretch uncomfortably
|
||||
|
||||
## Step 3: Common Issues Check
|
||||
- [ ] Images: `srcset` / responsive sizing, proper aspect ratios
|
||||
- [ ] Typography: readable at all sizes, proper line lengths (45-75 chars)
|
||||
- [ ] Spacing: consistent with design system tokens
|
||||
- [ ] Overflow: no `overflow: hidden` hiding important content
|
||||
- [ ] Z-index: modals/dropdowns work on all sizes
|
||||
- [ ] Inputs: don't zoom on iOS (font-size >= 16px)
|
||||
|
||||
## Step 4: Issues Report
|
||||
For each issue:
|
||||
- Breakpoint where it occurs
|
||||
- File and line number
|
||||
- Screenshot description or CSS selector
|
||||
- Suggested fix
|
||||
|
||||
Prioritize: broken layout > usability > polish
|
||||
304
skills/scroll-animation/SKILL.md
Normal file
304
skills/scroll-animation/SKILL.md
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
---
|
||||
name: scroll-animation
|
||||
description: Use when building scroll-driven animations — GSAP ScrollTrigger, CSS scroll-timeline, frame sequences, parallax, pin/scrub effects. Covers Apple-style scroll playback, progress-linked animations, and smooth scroll integration.
|
||||
arguments:
|
||||
- name: technique
|
||||
description: "Technique: gsap, css-native, frame-sequence, parallax, hybrid (auto-detect if omitted)"
|
||||
required: false
|
||||
- name: framework
|
||||
description: "Framework: react, next, astro, vue, svelte, vanilla (auto-detect if omitted)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Scroll Animation Skill
|
||||
|
||||
## Decision Matrix — Pick Technique
|
||||
|
||||
| Need | Technique | Why |
|
||||
|------|-----------|-----|
|
||||
| Pin + scrub + snap | GSAP ScrollTrigger | Most mature, free since Webflow acquisition |
|
||||
| Simple fade/slide on scroll | CSS `animation-timeline` | Zero JS, native performance |
|
||||
| Apple-style frame playback | Canvas frame sequence | Smoothest result for product reveals |
|
||||
| Parallax layers | CSS or GSAP | CSS for simple, GSAP for complex |
|
||||
| Smooth scroll feel | Lenis + GSAP | Industry standard combo |
|
||||
|
||||
---
|
||||
|
||||
## 1. GSAP ScrollTrigger
|
||||
|
||||
**License:** 100% FREE including all plugins
|
||||
**Install:** `npm i gsap`
|
||||
|
||||
### Core API
|
||||
|
||||
```js
|
||||
import gsap from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
// Pin + Scrub
|
||||
gsap.to(".hero-content", {
|
||||
y: -100,
|
||||
opacity: 0,
|
||||
scrollTrigger: {
|
||||
trigger: ".hero",
|
||||
start: "top top",
|
||||
end: "bottom top",
|
||||
pin: true,
|
||||
scrub: 1,
|
||||
snap: { snapTo: 1 / 4, duration: 0.3, ease: "power1.inOut" }
|
||||
}
|
||||
});
|
||||
|
||||
// Batch — stagger elements entering viewport
|
||||
ScrollTrigger.batch(".card", {
|
||||
onEnter: (elements) => {
|
||||
gsap.to(elements, { opacity: 1, y: 0, stagger: 0.1 });
|
||||
},
|
||||
start: "top 85%"
|
||||
});
|
||||
```
|
||||
|
||||
### React Integration (useGSAP hook)
|
||||
|
||||
```jsx
|
||||
import { useGSAP } from "@gsap/react";
|
||||
import gsap from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
function Section({ children }) {
|
||||
const container = useRef(null);
|
||||
|
||||
useGSAP(() => {
|
||||
gsap.from(".animate-in", {
|
||||
y: 50,
|
||||
opacity: 0,
|
||||
stagger: 0.2,
|
||||
scrollTrigger: { trigger: container.current, start: "top 80%" }
|
||||
});
|
||||
}, { scope: container });
|
||||
|
||||
return <section ref={container}>{children}</section>;
|
||||
}
|
||||
```
|
||||
|
||||
**Key:** `useGSAP` = drop-in for `useEffect`, auto-cleanup via `gsap.context()`.
|
||||
|
||||
### Astro Integration
|
||||
|
||||
```astro
|
||||
<section id="scroll-section">
|
||||
<div class="pin-target">Content</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
import gsap from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
gsap.to(".pin-target", {
|
||||
x: 500,
|
||||
scrollTrigger: { trigger: "#scroll-section", pin: true, scrub: true }
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Performance Best Practices
|
||||
|
||||
- Use `will-change: transform` on pinned elements
|
||||
- Prefer `transform` and `opacity` — GPU-composited, no layout recalc
|
||||
- `scrub: 1` (or higher) smooths jank vs `scrub: true` (instant)
|
||||
- `invalidateOnRefresh: true` for responsive layouts
|
||||
- Call `ScrollTrigger.refresh()` after dynamic content loads
|
||||
- Avoid animating `width`, `height`, `top`, `left` — triggers reflow
|
||||
|
||||
---
|
||||
|
||||
## 2. CSS Scroll-Driven Animations (Native)
|
||||
|
||||
### Scroll Progress Timeline
|
||||
|
||||
```css
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-on-scroll {
|
||||
animation: fade-in linear both;
|
||||
animation-timeline: scroll();
|
||||
animation-range: entry 0% entry 100%;
|
||||
}
|
||||
```
|
||||
|
||||
### View Progress Timeline
|
||||
|
||||
```css
|
||||
.reveal {
|
||||
animation: fade-in linear both;
|
||||
animation-timeline: view();
|
||||
animation-range: entry 25% cover 50%;
|
||||
}
|
||||
```
|
||||
|
||||
### Progressive Enhancement
|
||||
|
||||
```css
|
||||
@supports (animation-timeline: scroll()) {
|
||||
.animate { animation-timeline: scroll(); }
|
||||
}
|
||||
/* Fallback: use IntersectionObserver + classList toggle */
|
||||
```
|
||||
|
||||
### What CSS Can Replace from GSAP
|
||||
|
||||
| Feature | CSS Native | Still Need GSAP |
|
||||
|---------|-----------|-----------------|
|
||||
| Fade/slide on scroll | Yes | No |
|
||||
| Progress-linked animation | Yes | No |
|
||||
| View-enter/exit triggers | Yes | No |
|
||||
| Pin element | No | Yes |
|
||||
| Snap to sections | No (scroll-snap is separate) | Yes (integrated) |
|
||||
| Batch stagger | No | Yes |
|
||||
| Timeline sequencing | Limited | Yes |
|
||||
| Complex easing curves | Limited | Yes |
|
||||
| JS callbacks on progress | No | Yes |
|
||||
|
||||
**Rule of thumb:** CSS for simple reveal animations. GSAP for anything with pin, snap, stagger, or JS logic.
|
||||
|
||||
---
|
||||
|
||||
## 3. Lenis Smooth Scroll
|
||||
|
||||
**Install:** `npm i lenis`
|
||||
**Bundle:** ~14KB min+gzip (no dependencies)
|
||||
|
||||
```js
|
||||
import Lenis from "lenis";
|
||||
|
||||
const lenis = new Lenis({
|
||||
duration: 1.2,
|
||||
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
||||
orientation: "vertical",
|
||||
smoothWheel: true,
|
||||
});
|
||||
|
||||
// Connect to GSAP ticker for sync
|
||||
gsap.ticker.add((time) => { lenis.raf(time * 1000); });
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
|
||||
// Connect to ScrollTrigger
|
||||
lenis.on("scroll", ScrollTrigger.update);
|
||||
```
|
||||
|
||||
**When to use:** Agency-style smooth scroll feel. Pairs with GSAP ScrollTrigger.
|
||||
**When NOT to use:** Content-heavy sites, accessibility-first projects.
|
||||
|
||||
---
|
||||
|
||||
## 4. Frame Sequence on Scroll (Apple-Style)
|
||||
|
||||
### Pipeline
|
||||
|
||||
```
|
||||
Video (MP4/MOV)
|
||||
→ FFmpeg frame extraction (PNG)
|
||||
→ Convert to WebP (90% size reduction vs PNG)
|
||||
→ Canvas playback synced to scroll
|
||||
```
|
||||
|
||||
### FFmpeg Extraction
|
||||
|
||||
```bash
|
||||
ffmpeg -i source.mp4 -vf "fps=30,scale=1280:720" frames/frame_%04d.png
|
||||
for f in frames/*.png; do cwebp -q 80 "$f" -o "${f%.png}.webp"; done
|
||||
```
|
||||
|
||||
### Optimal Parameters
|
||||
|
||||
| Parameter | Desktop | Mobile |
|
||||
|-----------|---------|--------|
|
||||
| Frame count | 120-180 | 60-90 |
|
||||
| Resolution | 1920x1080 | 960x540 |
|
||||
| Format | WebP q80 | WebP q75 |
|
||||
| Total budget | 2-4 MB | 1-2 MB |
|
||||
|
||||
### Canvas Implementation
|
||||
|
||||
```js
|
||||
const canvas = document.getElementById("sequence-canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const frameCount = 150;
|
||||
const frames = [];
|
||||
|
||||
function preloadFrames() {
|
||||
for (let i = 1; i <= frameCount; i++) {
|
||||
const img = new Image();
|
||||
img.src = `/frames/frame_${String(i).padStart(4, "0")}.webp`;
|
||||
frames.push(img);
|
||||
}
|
||||
}
|
||||
|
||||
gsap.to({ frame: 0 }, {
|
||||
frame: frameCount - 1,
|
||||
snap: "frame",
|
||||
ease: "none",
|
||||
scrollTrigger: {
|
||||
trigger: "#sequence-section", start: "top top", end: "+=3000", pin: true, scrub: 0.5,
|
||||
},
|
||||
onUpdate: function() {
|
||||
const index = Math.round(this.targets()[0].frame);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
if (frames[index]?.complete) {
|
||||
ctx.drawImage(frames[index], 0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Alternative: Video Scrub
|
||||
|
||||
```js
|
||||
const video = document.getElementById("scrub-video");
|
||||
|
||||
gsap.to(video, {
|
||||
currentTime: video.duration,
|
||||
ease: "none",
|
||||
scrollTrigger: { trigger: "#video-section", start: "top top", end: "+=4000", pin: true, scrub: true }
|
||||
});
|
||||
```
|
||||
|
||||
**Tradeoff:** Video scrub = smaller payload, less smooth on mobile. Frame sequence = more bytes, smoother everywhere.
|
||||
|
||||
---
|
||||
|
||||
## Accessibility
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*, *::before, *::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
if (prefersReduced) { ScrollTrigger.getAll().forEach(st => st.kill()); }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Define scroll sections** — wireframe which content pins, reveals, or plays
|
||||
2. **Pick technique** — use Decision Matrix above
|
||||
3. **Implement with GSAP** — pin/scrub/snap for complex, CSS for simple reveals
|
||||
4. **Add Lenis** — only if smooth scroll feel is required
|
||||
5. **Test performance** — Chrome DevTools Performance panel, aim for <16.6ms/frame
|
||||
6. **Add a11y** — `prefers-reduced-motion`, keyboard nav still works
|
||||
7. **Test mobile** — reduce frame counts, disable heavy effects on low-end
|
||||
49
skills/seo-audit/SKILL.md
Normal file
49
skills/seo-audit/SKILL.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
name: seo-audit
|
||||
description: Use when auditing SEO — technical + content analysis via WebFetch and code inspection
|
||||
arguments:
|
||||
- name: url
|
||||
description: URL or project path to audit
|
||||
required: true
|
||||
---
|
||||
|
||||
# SEO Audit Workflow
|
||||
|
||||
## Step 1: Technical SEO
|
||||
Fetch and analyze the page:
|
||||
- **Meta tags:** title (<60 chars), description (<155 chars), viewport, robots
|
||||
- **Headings:** proper H1-H6 hierarchy, single H1
|
||||
- **URLs:** clean, descriptive, no query params for content pages
|
||||
- **Canonical:** present and correct
|
||||
- **Sitemap:** exists at /sitemap.xml
|
||||
- **Robots.txt:** exists, not blocking important pages
|
||||
- **HTTPS:** enforced, no mixed content
|
||||
- **Mobile:** responsive meta tag, no horizontal scroll
|
||||
|
||||
## Step 2: Performance Impact
|
||||
- Image optimization: format (WebP/AVIF), size, lazy loading, alt text
|
||||
- Core Web Vitals indicators:
|
||||
- LCP: largest element load time
|
||||
- CLS: layout shift from images/fonts without dimensions
|
||||
- FID/INP: heavy JS blocking interaction
|
||||
- Bundle size check if applicable
|
||||
|
||||
## Step 3: Content SEO
|
||||
- Keyword presence in: title, H1, first paragraph, URL
|
||||
- Content length (>300 words for ranking)
|
||||
- Internal links (to other pages on same domain)
|
||||
- External links (to authoritative sources)
|
||||
- Structured data (JSON-LD): Article, Product, FAQ, etc.
|
||||
- Open Graph + Twitter Card meta tags
|
||||
|
||||
## Step 4: Issues Report
|
||||
Format as prioritized list:
|
||||
- **Critical:** blocks indexing or ranking (missing title, noindex, broken canonical)
|
||||
- **Important:** significant ranking impact (no meta description, missing alt text, slow LCP)
|
||||
- **Nice-to-have:** minor improvements (schema markup, additional links)
|
||||
|
||||
Each issue: what's wrong, where, how to fix, impact level.
|
||||
|
||||
## Step 5: Action Items
|
||||
- Generate fix list ordered by impact
|
||||
- For code changes: specific file + line + suggested edit
|
||||
330
skills/site-builder/SKILL.md
Normal file
330
skills/site-builder/SKILL.md
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
---
|
||||
name: site-builder
|
||||
description: Build a website from block recipes via interactive wizard. Asks stack/type/style/sections via AskUserQuestion, generates one section at a time, enforces WYSIWYD (what you see in the mock is byte-identical to what gets deployed) via the mock-render primitive.
|
||||
---
|
||||
|
||||
# /site-builder — WYSIWYD website builder
|
||||
|
||||
> **Core promise:** every section you approve in the preview IS the file that gets deployed. No "approximately like this". Byte-for-byte.
|
||||
|
||||
## When to use
|
||||
|
||||
Triggers: `/site-builder`, "create website", "build a site", "landing page", "portfolio site", "SaaS site", "docs site".
|
||||
|
||||
## Output contract
|
||||
|
||||
Produces a complete website project as local code:
|
||||
|
||||
```
|
||||
<project-root>/
|
||||
├── src/
|
||||
│ ├── pages/index.astro (or app/page.tsx for Next)
|
||||
│ ├── sections/
|
||||
│ │ ├── Nav.astro — one file per section (Constructor Pattern)
|
||||
│ │ ├── Hero.astro
|
||||
│ │ ├── Features.astro
|
||||
│ │ ├── Pricing.astro
|
||||
│ │ └── ...
|
||||
│ ├── layouts/Base.astro
|
||||
│ └── tokens.css — CSS custom properties
|
||||
├── public/
|
||||
│ └── <brand assets>
|
||||
├── astro.config.mjs (or next.config.js)
|
||||
├── package.json
|
||||
├── site-state.json — mock-render lock file (WYSIWYD)
|
||||
└── mocks/
|
||||
├── Hero.png — locked screenshots per section
|
||||
├── Features.png
|
||||
└── ...
|
||||
```
|
||||
|
||||
Every `sections/*.astro` is independently regeneratable — editing one never touches the others.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node 20+ with `npx` available
|
||||
- `mock-render` Rust primitive installed (built by `install.sh` into `$HOME/.claude/agents/_primitives/_rust/target/release/mock-render`)
|
||||
- Playwright installed (`npx playwright install chromium` — the skill will prompt if missing)
|
||||
|
||||
## Phase 0 — Intake via AskUserQuestion
|
||||
|
||||
Send questions in AskUserQuestion calls (max 4 per call; use 2 calls if more).
|
||||
|
||||
### Call 1 — 4 questions
|
||||
|
||||
- **Site archetype?** SaaS landing / Multi-page marketing / Portfolio / Docs site
|
||||
- **Framework?** Astro 6 (marketing default) / Next.js 16 (SaaS/app) / Static HTML
|
||||
- **Visual archetype?** Premium minimalist / Dark moody tech / Editorial long-form / Brutalist anti-design
|
||||
- **Motion tier?** None / Subtle (Motion LazyMotion) / Rich (GSAP ScrollTrigger + Motion) / Experimental (3D + shaders)
|
||||
|
||||
### Call 2 — 3 follow-up questions
|
||||
|
||||
- **Deploy target?** Cloudflare Pages (recommended) / Vercel / Local only
|
||||
- **Brand assets?** User provides / Generate with AI / Minimal (text logo + neutral palette)
|
||||
- **Include a contact form?** Yes (wire via /form-builder) / No
|
||||
|
||||
## Phase 1 — Section selection
|
||||
|
||||
After Phase 0, pick SECTIONS based on site type.
|
||||
|
||||
Defaults per archetype:
|
||||
|
||||
| Archetype | Default sections |
|
||||
|---|---|
|
||||
| SaaS landing | Nav, Hero, LogoBar, Features, Testimonials, Pricing, FAQ, CTA, Footer |
|
||||
| Multi-page marketing | Nav, Hero, Features, CTA, Footer — plus routes `/about`, `/pricing`, `/contact` |
|
||||
| Portfolio | Nav, Hero, Features (case grid), Testimonials, Contact, Footer |
|
||||
| Docs site | NavSidebar, content layout, Footer-minimal |
|
||||
|
||||
Ask via AskUserQuestion: "Pick sections to include" with multi-select — show the defaults checked, user can add/remove.
|
||||
|
||||
For each section selected, ask: "Variant?" (A/B/C).
|
||||
|
||||
## Phase 2 — Foundation
|
||||
|
||||
Create project scaffold (Astro example):
|
||||
|
||||
```bash
|
||||
npm create astro@latest <project-root> -- --template minimal --typescript strict --no-install --no-git
|
||||
cd <project-root>
|
||||
npm install
|
||||
npm install motion @radix-ui/react-tabs lucide-astro # per block deps
|
||||
```
|
||||
|
||||
Write `src/tokens.css` using answers from Phase 0:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-bg: ...;
|
||||
--color-fg: ...;
|
||||
--color-accent: ...;
|
||||
--color-border: ...;
|
||||
--radius-card: 0.75rem;
|
||||
--space-section: clamp(4rem, 8vw, 8rem);
|
||||
--font-display: ...;
|
||||
--font-body: ...;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) { :root { ... } }
|
||||
```
|
||||
|
||||
If user chose "brand assets: I'll provide", ask free-text for logo path + 2-3 hex colors.
|
||||
|
||||
Commit checkpoint: `checkpoint: scaffold + tokens`.
|
||||
|
||||
## Phase 3 — WYSIWYD block-by-block build (THE CORE LOOP)
|
||||
|
||||
For EACH section in the approved list:
|
||||
|
||||
### 3.1 Generate the section file
|
||||
|
||||
Write `<project-root>/src/sections/<Name>.astro`:
|
||||
- Copy from Phase 0 brand answers (or placeholder with user's domain words)
|
||||
- Tokens from `src/tokens.css` (no hardcoded colors)
|
||||
- Motion hooks matching Phase 0 motion tier
|
||||
- Brand assets if provided
|
||||
|
||||
**Anti-patterns to enforce:**
|
||||
- No "AI-powered X" headlines
|
||||
- No centered gradient + default Inter + purple/blue palette combo
|
||||
- No 5+ CTAs / tiers / features on one block
|
||||
|
||||
### 3.2 Render mock
|
||||
|
||||
Start dev server (once per session):
|
||||
```bash
|
||||
npm run dev & # Astro on :4321 or Next on :3000
|
||||
```
|
||||
|
||||
Wait for port to respond:
|
||||
```bash
|
||||
for i in {1..30}; do
|
||||
curl -s http://localhost:4321 > /dev/null && break
|
||||
sleep 1
|
||||
done
|
||||
```
|
||||
|
||||
Screenshot via the `mock-render` primitive:
|
||||
|
||||
```bash
|
||||
$HOME/.claude/agents/_primitives/_rust/target/release/mock-render screenshot \
|
||||
"http://localhost:4321/_block-preview?block=<Name>" \
|
||||
--out "<project-root>/mocks/<Name>.png" \
|
||||
--viewport 1440x900
|
||||
```
|
||||
|
||||
Create a simple `_block-preview.astro` route that imports and renders one section by query param — include this in the scaffold.
|
||||
|
||||
### 3.3 Show user + get approval
|
||||
|
||||
Display the screenshot. Ask via AskUserQuestion:
|
||||
|
||||
- Approve — lock and move on
|
||||
- Iterate — tell me what to change
|
||||
- Switch variant (A/B/C)
|
||||
- Swap block entirely
|
||||
|
||||
### 3.4 Act on approval
|
||||
|
||||
**Approve:**
|
||||
```bash
|
||||
$HOME/.claude/agents/_primitives/_rust/target/release/mock-render lock \
|
||||
--project <project-root> \
|
||||
--section src/sections/<Name>.astro \
|
||||
--screenshot mocks/<Name>.png
|
||||
```
|
||||
Commit: `feat: lock <Name> section`. Move to next section.
|
||||
|
||||
**Iterate:** Ask free-text "What to change?", apply surgical edit to `<Name>.astro` only. Re-render. Loop.
|
||||
|
||||
**Switch variant:** Re-run 3.1 with different variant flag.
|
||||
|
||||
**Swap block:** Go back to Phase 1 for this section slot.
|
||||
|
||||
### 3.5 WYSIWYD invariant check before any later write
|
||||
|
||||
Before writing to ANY already-locked section:
|
||||
|
||||
```bash
|
||||
$HOME/.claude/agents/_primitives/_rust/target/release/mock-render verify \
|
||||
--project <project-root> \
|
||||
--section src/sections/<Name>.astro
|
||||
# Exit 0: file unchanged since lock, OK to proceed
|
||||
# Exit 2: DRIFT — re-render + re-approve before continuing
|
||||
```
|
||||
|
||||
This invariant means the final deploy is guaranteed to look like the last screenshot the user approved.
|
||||
|
||||
## Phase 4 — Audit (parallel)
|
||||
|
||||
After all sections locked, run 4 audits in parallel:
|
||||
|
||||
```
|
||||
/a11y-audit scan src/
|
||||
/seo-audit <project-root>
|
||||
/responsive-audit src/pages/index.astro
|
||||
/perf-audit src/
|
||||
```
|
||||
|
||||
Report findings grouped by severity. For each fix proposed:
|
||||
1. Run `mock-render verify` on the affected section
|
||||
2. If verify passes AND fix is minor (e.g., add `alt=""`, tweak meta tag) — apply
|
||||
3. If fix alters layout — ask user to re-approve (back to 3.2 for that section)
|
||||
4. If fix spans multiple sections — STOP, report, let user decide
|
||||
|
||||
## Phase 5 — Preview
|
||||
|
||||
Spin up a preview URL before production deploy:
|
||||
|
||||
- Cloudflare Pages: `npx wrangler pages deploy <build-dir> --project-name=<slug>-preview`
|
||||
- Vercel: `npx vercel --preview` (returns preview URL)
|
||||
- Local: `npm run preview` on :4321
|
||||
|
||||
Send URL to user.
|
||||
|
||||
## Phase 6 — Deploy
|
||||
|
||||
Only after user explicitly confirms preview:
|
||||
|
||||
```
|
||||
/web-deploy deploy --target=<chosen-in-Phase-0> --project=<project-root>
|
||||
```
|
||||
|
||||
Final output:
|
||||
- Production URL
|
||||
- Git commit SHA
|
||||
- Screenshot grid of all locked sections (from `mocks/*.png`)
|
||||
|
||||
## State file — site-state.json
|
||||
|
||||
Maintained by `mock-render lock/verify`. Shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": {
|
||||
"Hero": {"path": "src/sections/Hero.astro", "sha256": "6a48...", "locked": true, "screenshot": "mocks/Hero.png"},
|
||||
"Features": {"path": "src/sections/Features.astro", "sha256": "...", "locked": true, "screenshot": "mocks/Features.png"},
|
||||
"Pricing": {"path": "src/sections/Pricing.astro", "sha256": "...", "locked": false, "screenshot": null}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Commands:
|
||||
- `mock-render lock` — freeze current hash after user approves mock
|
||||
- `mock-render verify` — assert source unchanged before any new write
|
||||
- `mock-render status` — list sections, lock state, drift check
|
||||
- `mock-render screenshot` — Playwright wrapper
|
||||
|
||||
## Handoffs (sub-skills called)
|
||||
|
||||
| Sub-skill | When |
|
||||
|---|---|
|
||||
| `/frontend-design` | Phase 2 — if archetype picked but user wants custom tokens |
|
||||
| `/design-inspiration` | Phase 0 alt — if user wants to see refs before picking style |
|
||||
| `/site-teardown` | Phase 0 alt — if user provides a ref site URL to clone-style |
|
||||
| `/ai-animation` | Phase 3 — video-bg hero or scroll loop |
|
||||
| `/scroll-animation` | Phase 3 — rich/experimental motion tier with pin/scrub |
|
||||
| `/3d-scene` | Phase 3 — experimental motion tier with R3F hero |
|
||||
| `/web-effects` | Phase 3 — shader bg, particles |
|
||||
| `/motion-design` | Phase 3 — subtle motion tier (Motion library) |
|
||||
| `/form-builder` | Phase 3 — if contact form yes (Phase 0 Call 2 Q3) |
|
||||
| `/ui-component` | Phase 3 — novel primitive not in blocks |
|
||||
| `/web-assets` | Phase 4 — image/font/video optimization |
|
||||
| `/a11y-audit` | Phase 4 — parallel |
|
||||
| `/seo-audit` | Phase 4 — parallel |
|
||||
| `/responsive-audit` | Phase 4 — parallel |
|
||||
| `/perf-audit` | Phase 4 — parallel |
|
||||
| `/web-deploy` | Phase 6 — production |
|
||||
|
||||
## Forbidden
|
||||
|
||||
- Generating a section file without immediately rendering a screenshot of it
|
||||
- Approving a section without calling `mock-render lock`
|
||||
- Editing a locked section file without first running `mock-render verify`
|
||||
- Cascading edits that touch multiple sections at once (violates Constructor Pattern for UI)
|
||||
- Deploying before user approves the preview URL (Phase 5)
|
||||
- AI-slop anti-patterns
|
||||
- Hardcoded colors / fonts / spacing outside `tokens.css`
|
||||
- Breaking the WYSIWYD invariant — the last screenshot the user approved MUST match what's deployed
|
||||
|
||||
## Anti-patterns (AI slop guards)
|
||||
|
||||
Enforced at generation time — block the section and regenerate if detected:
|
||||
|
||||
1. Generic centered hero + gradient + "AI-powered X" subhead
|
||||
2. Stock 3D isometric illustrations
|
||||
3. Lorem-ipsum-tier feature copy
|
||||
4. Every section animated (motion fatigue)
|
||||
5. 5+ pricing tiers
|
||||
6. No specific outcome claim (numbers like "47 seconds", "12x faster")
|
||||
7. Default stack tell: Inter + Slate/Zinc + rounded-lg + Lucide — pick one deviation
|
||||
|
||||
## Output report format
|
||||
|
||||
```
|
||||
=== /SITE-BUILDER REPORT ===
|
||||
Project: <project-root>
|
||||
Stack: <Astro 6 / Next 16 / static>
|
||||
Archetype: <SaaS / multi-page / portfolio / docs>
|
||||
Style: <premium / dark-tech / editorial / brutalist>
|
||||
Motion tier: <none / subtle / rich / experimental>
|
||||
|
||||
Sections built: <N>
|
||||
- Nav locked, 23 KB screenshot
|
||||
- Hero locked, 87 KB screenshot
|
||||
- ...
|
||||
|
||||
WYSIWYD check: all locked, 0 drift
|
||||
Audits: a11y=pass seo=2-minor perf=LCP 1.2s responsive=pass
|
||||
Preview: <url>
|
||||
Deploy: <url or "pending user confirm">
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- `$HOME/.claude/agents/_primitives/_rust/mock-render/` — WYSIWYD enforcer (Rust)
|
||||
- `skills/landing-page/SKILL.md` — predecessor (single-page only)
|
||||
- `skills/frontend-design/SKILL.md` — archetype philosophy
|
||||
- `skills/motion-design/SKILL.md` — motion library choices
|
||||
- `skills/scroll-animation/SKILL.md` — GSAP / scroll-timeline patterns
|
||||
- `skills/web-deploy/SKILL.md` — CF Pages / Vercel deploy
|
||||
230
skills/site-teardown/SKILL.md
Normal file
230
skills/site-teardown/SKILL.md
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
---
|
||||
name: site-teardown
|
||||
description: "Deconstruct any live website into reusable recipe — extract HTML, CSS, JS, design tokens, animations. Use when user says: teardown, deconstruct, clone site, reverse engineer, how is this site built."
|
||||
arguments:
|
||||
- name: url
|
||||
description: URL of the website to deconstruct
|
||||
required: true
|
||||
- name: depth
|
||||
description: "quick = tokens + screenshots only, full = complete teardown (default: full)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Site Teardown — Deconstruct Any Website into a Reusable Recipe
|
||||
|
||||
Extracts design tokens, layout structure, animation techniques, and library stack from a live website.
|
||||
Output: structured recipe that can be fed into `/frontend-design`, `/landing-page`, `/design-system`.
|
||||
|
||||
## Phase 1 — Navigate & Screenshot
|
||||
|
||||
```
|
||||
1. browser_navigate → {url}
|
||||
2. browser_resize → width: 1280, height: 900
|
||||
3. browser_take_screenshot → fullPage: true, filename: "teardown-desktop.png"
|
||||
4. browser_resize → width: 375, height: 812
|
||||
5. browser_take_screenshot → fullPage: true, filename: "teardown-mobile.png"
|
||||
6. browser_resize → width: 1280, height: 900 (restore)
|
||||
```
|
||||
|
||||
Save screenshots to `teardown/{domain}/` in the project directory (relative to `$PWD`).
|
||||
|
||||
## Phase 2 — Extract HTML Structure
|
||||
|
||||
Run `browser_evaluate` with:
|
||||
|
||||
```javascript
|
||||
() => {
|
||||
const sections = Array.from(document.querySelectorAll('section, [class*="section"], main > div'));
|
||||
const nav = document.querySelector('nav, header');
|
||||
const footer = document.querySelector('footer');
|
||||
const headings = Array.from(document.querySelectorAll('h1, h2, h3')).map(h => ({
|
||||
tag: h.tagName, text: h.textContent.trim().slice(0, 80)
|
||||
}));
|
||||
return {
|
||||
title: document.title,
|
||||
sectionCount: sections.length,
|
||||
hasNav: !!nav,
|
||||
navType: nav?.classList?.toString() || 'unknown',
|
||||
hasFooter: !!footer,
|
||||
headings,
|
||||
bodyClasses: document.body.classList.toString(),
|
||||
htmlLang: document.documentElement.lang
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Also extract full HTML for deep analysis:
|
||||
```javascript
|
||||
() => document.documentElement.outerHTML
|
||||
```
|
||||
Save to `teardown/{domain}/index.html`.
|
||||
|
||||
## Phase 3 — Extract Design Tokens
|
||||
|
||||
Run `browser_evaluate` to extract computed styles from key elements:
|
||||
|
||||
```javascript
|
||||
() => {
|
||||
const get = (sel) => {
|
||||
const el = document.querySelector(sel);
|
||||
return el ? getComputedStyle(el) : null;
|
||||
};
|
||||
const body = get('body');
|
||||
const h1 = get('h1');
|
||||
const btn = get('a[class*="btn"], button[class*="btn"], .cta, a[class*="cta"]');
|
||||
const card = get('[class*="card"], [class*="Card"]');
|
||||
const props = {};
|
||||
const root = getComputedStyle(document.documentElement);
|
||||
for (const name of ['--primary', '--secondary', '--accent', '--background', '--foreground',
|
||||
'--radius', '--font-sans', '--font-mono', '--brand']) {
|
||||
const val = root.getPropertyValue(name).trim();
|
||||
if (val) props[name] = val;
|
||||
}
|
||||
return {
|
||||
colors: {
|
||||
background: body?.backgroundColor,
|
||||
text: body?.color,
|
||||
heading: h1?.color,
|
||||
button: btn ? { bg: btn.backgroundColor, text: btn.color, radius: btn.borderRadius } : null,
|
||||
card: card ? { bg: card.backgroundColor, border: card.borderColor, radius: card.borderRadius, shadow: card.boxShadow } : null
|
||||
},
|
||||
typography: {
|
||||
bodyFont: body?.fontFamily,
|
||||
bodySize: body?.fontSize,
|
||||
h1Font: h1?.fontFamily,
|
||||
h1Size: h1?.fontSize,
|
||||
h1Weight: h1?.fontWeight,
|
||||
lineHeight: body?.lineHeight
|
||||
},
|
||||
spacing: {
|
||||
bodyPadding: body?.padding,
|
||||
sectionPadding: get('section')?.padding
|
||||
},
|
||||
customProperties: props
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Output:** Save as `teardown/{domain}/tokens.json`.
|
||||
|
||||
## Phase 4 — Fetch CSS & JS Sources
|
||||
|
||||
### 4a. Collect resource URLs
|
||||
|
||||
```javascript
|
||||
() => {
|
||||
const css = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).map(l => l.href);
|
||||
const js = Array.from(document.querySelectorAll('script[src]')).map(s => s.src);
|
||||
const inlineStyles = document.querySelectorAll('style').length;
|
||||
return { css, js, inlineStyleBlocks: inlineStyles };
|
||||
}
|
||||
```
|
||||
|
||||
### 4b. Fetch each CSS file via WebFetch
|
||||
|
||||
For each CSS URL: `WebFetch` with prompt:
|
||||
> "Extract ALL design-relevant CSS from this stylesheet: custom properties (--vars), @keyframes, @font-face, color values, gradient definitions, backdrop-filter, box-shadow patterns, border-radius values, transition/animation properties. Return as structured list."
|
||||
|
||||
### 4c. Detect JS libraries
|
||||
|
||||
```javascript
|
||||
() => ({
|
||||
gsap: typeof gsap !== 'undefined',
|
||||
ScrollTrigger: typeof ScrollTrigger !== 'undefined',
|
||||
lenis: !!document.querySelector('[data-lenis-prevent]') || typeof Lenis !== 'undefined',
|
||||
framerMotion: !!document.querySelector('[data-framer-component-type]'),
|
||||
three: typeof THREE !== 'undefined',
|
||||
curtains: typeof Curtains !== 'undefined',
|
||||
particles: typeof tsParticles !== 'undefined',
|
||||
aos: typeof AOS !== 'undefined',
|
||||
locomotive: !!document.querySelector('[data-scroll-container]'),
|
||||
swiper: typeof Swiper !== 'undefined',
|
||||
tailwind: !!document.querySelector('[class*="bg-"], [class*="text-"], [class*="flex"]'),
|
||||
react: typeof __NEXT_DATA__ !== 'undefined' || !!document.getElementById('__next'),
|
||||
astro: !!document.querySelector('[data-astro-source-file]'),
|
||||
vue: !!document.getElementById('__nuxt') || !!document.querySelector('[data-v-]')
|
||||
})
|
||||
```
|
||||
|
||||
### 4d. Network analysis (supplementary)
|
||||
|
||||
`browser_network_requests` with `filter: "\\.css$|\\.js$"`, `static: false` — cross-reference with DOM-extracted URLs.
|
||||
|
||||
## Phase 5 — Animation Catalog
|
||||
|
||||
```javascript
|
||||
() => {
|
||||
const anims = [];
|
||||
const allEls = document.querySelectorAll('*');
|
||||
const seen = new Set();
|
||||
allEls.forEach(el => {
|
||||
const s = getComputedStyle(el);
|
||||
if (s.animationName && s.animationName !== 'none' && !seen.has(s.animationName)) {
|
||||
seen.add(s.animationName);
|
||||
anims.push({ type: 'css-animation', name: s.animationName, duration: s.animationDuration });
|
||||
}
|
||||
if (s.transition && s.transition !== 'all 0s ease 0s' && s.transition !== 'none') {
|
||||
const key = s.transition.slice(0, 60);
|
||||
if (!seen.has(key)) { seen.add(key); anims.push({ type: 'transition', value: s.transition.slice(0, 120) }); }
|
||||
}
|
||||
});
|
||||
const canvases = document.querySelectorAll('canvas').length;
|
||||
const videos = document.querySelectorAll('video').length;
|
||||
const svgAnims = document.querySelectorAll('animate, animateTransform').length;
|
||||
return { animations: anims, canvasCount: canvases, videoCount: videos, svgAnimations: svgAnims };
|
||||
}
|
||||
```
|
||||
|
||||
**Output:** Save analysis as `teardown/{domain}/animations.md`.
|
||||
|
||||
If `depth=quick` → STOP here with tokens + screenshots only.
|
||||
|
||||
## Phase 6 — Compile Recipe
|
||||
|
||||
Assemble `teardown/{domain}/recipe.md`:
|
||||
|
||||
```markdown
|
||||
# Site Teardown: {domain}
|
||||
Date: {date}
|
||||
|
||||
## Layout Structure
|
||||
{section map from Phase 2}
|
||||
|
||||
## Design Tokens
|
||||
{from Phase 3 — colors, typography, spacing}
|
||||
|
||||
## Tech Stack
|
||||
- Framework: {React/Next/Astro/Vue from Phase 4c}
|
||||
- CSS: {Tailwind/custom/styled-components}
|
||||
- Animation: {GSAP/Framer Motion/CSS/AOS from Phase 4c}
|
||||
- Scroll: {Lenis/Locomotive/native from Phase 4c}
|
||||
- 3D/WebGL: {Three.js/curtains.js/none from Phase 4c}
|
||||
|
||||
## Animation Techniques
|
||||
{catalog from Phase 5}
|
||||
|
||||
## Reproduction Steps
|
||||
1. Set up {framework} project with {css approach}
|
||||
2. Apply design tokens: {token summary}
|
||||
3. Implement layout: {section sequence}
|
||||
4. Add animations: {technique list with skill references}
|
||||
5. Optimize: /web-assets → /a11y-audit → /perf-audit
|
||||
|
||||
## Recommended Skills
|
||||
- /frontend-design archetype={suggested}
|
||||
- /scroll-animation technique={if GSAP detected}
|
||||
- /web-effects effect={if WebGL detected}
|
||||
- /motion-design {if Framer Motion detected}
|
||||
```
|
||||
|
||||
## Chaining
|
||||
|
||||
| Direction | Skill | How |
|
||||
|-----------|-------|-----|
|
||||
| FROM | `/design-inspiration` | User picks best reference → teardown |
|
||||
| FROM | `/competitor-analysis` | Deep-dive competitor's site |
|
||||
| TO | `/frontend-design` | Feed tokens → suggest archetype |
|
||||
| TO | `/landing-page` | Use recipe as template |
|
||||
| TO | `/design-system` | Generate token system from extracted tokens |
|
||||
| TO | `/scroll-animation` | Reproduce detected scroll effects |
|
||||
| TO | `/web-effects` | Reproduce detected WebGL/particle effects |
|
||||
66
skills/ui-component/SKILL.md
Normal file
66
skills/ui-component/SKILL.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
name: ui-component
|
||||
description: Use when building a UI component — API design, variants, accessibility, animations, tests
|
||||
arguments:
|
||||
- name: component
|
||||
description: Component name and description
|
||||
required: true
|
||||
- name: framework
|
||||
description: "Framework: react, next, astro, svelte, vue (auto-detect if omitted)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# UI Component Workflow
|
||||
|
||||
## Step 1: Research
|
||||
- Check if component exists in project already (Glob/Grep)
|
||||
- Check existing component library for similar components
|
||||
- Review design system tokens if available
|
||||
- Identify the component's role and variations needed
|
||||
|
||||
## Step 2: API Design (Props First)
|
||||
Define before implementing:
|
||||
```
|
||||
interface ComponentProps {
|
||||
// Required props
|
||||
// Optional props with defaults
|
||||
// Event handlers
|
||||
// Composition slots (children, render props)
|
||||
// Style overrides (className, style)
|
||||
}
|
||||
```
|
||||
- Keep API minimal — only props that are actually needed
|
||||
- Use discriminated unions for variant props
|
||||
- Sensible defaults for all optional props
|
||||
|
||||
## Step 3: Implementation
|
||||
- Follow project's component patterns exactly
|
||||
- Compose from existing primitives when possible
|
||||
- Variants via props, not separate components
|
||||
|
||||
### Accessibility
|
||||
- Semantic HTML elements
|
||||
- ARIA attributes where needed
|
||||
- Keyboard navigation (Tab, Enter, Escape, Arrow keys)
|
||||
- Focus management and visible focus styles
|
||||
- Screen reader announcements for dynamic content
|
||||
- Color contrast WCAG AA (4.5:1 text, 3:1 large/UI)
|
||||
|
||||
### Animations
|
||||
- Use CSS transitions/animations over JS when possible
|
||||
- Respect `prefers-reduced-motion`
|
||||
- Consistent timing from design system tokens
|
||||
- Enter/exit animations for conditional rendering
|
||||
|
||||
## Step 4: Tests
|
||||
- Render test (mounts without error)
|
||||
- Props test (each variant renders correctly)
|
||||
- Interaction test (click, hover, keyboard)
|
||||
- Accessibility test (axe-core or similar)
|
||||
|
||||
## Step 5: Examples
|
||||
- Default usage
|
||||
- All variants
|
||||
- With different content lengths
|
||||
- Responsive behavior
|
||||
- Dark mode
|
||||
110
skills/web-assets/SKILL.md
Normal file
110
skills/web-assets/SKILL.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
name: web-assets
|
||||
description: Use when optimizing images, fonts, and video for web — AVIF pipeline, responsive srcset, font subsetting, video codec selection, Sharp.js processing. Triggers on "optimize images", "web assets", "image pipeline", "font optimization".
|
||||
arguments:
|
||||
- name: command
|
||||
description: "Command: optimize, picture, fonts, video, audit, pipeline"
|
||||
required: false
|
||||
- name: target
|
||||
description: Directory or file path to process
|
||||
required: false
|
||||
---
|
||||
|
||||
# Image & Asset Optimization Pipeline
|
||||
|
||||
Optimize images, fonts, and video for premium web performance.
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
| Asset | Tool | Format | Quality |
|
||||
|-------|------|--------|---------|
|
||||
| Photos | Sharp.js | AVIF primary, WebP fallback | avif:50, webp:75, jpg:80 |
|
||||
| Icons | SVG sprite | `<symbol>` + `<use>` | N/A |
|
||||
| Fonts | glyphhanger | WOFF2 only, subset | variable font preferred |
|
||||
| Video | FFmpeg | AV1 > H.265 > H.264 | CRF 28-32 |
|
||||
| AI-generated images | External generator (e.g. fal.ai) + Sharp | Process through Sharp | per above |
|
||||
|
||||
## Image Pipeline (Sharp.js)
|
||||
|
||||
```bash
|
||||
npm ls sharp 2>/dev/null || npm install sharp
|
||||
```
|
||||
|
||||
Breakpoints: 400, 640, 768, 1024, 1280, 1920px. Max 2560px for Retina.
|
||||
|
||||
```javascript
|
||||
const sharp = require('sharp');
|
||||
const WIDTHS = [400, 640, 768, 1024, 1280, 1920];
|
||||
const FORMATS = ['avif', 'webp', 'jpg'];
|
||||
const QUALITY = { avif: 50, webp: 75, jpg: 80 };
|
||||
|
||||
async function processImage(inputPath, outputDir) {
|
||||
const name = path.parse(inputPath).name;
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
for (const width of WIDTHS) {
|
||||
for (const format of FORMATS) {
|
||||
await sharp(inputPath)
|
||||
.resize(width, null, { withoutEnlargement: true })
|
||||
.toFormat(format, { quality: QUALITY[format] })
|
||||
.toFile(path.join(outputDir, `${name}-${width}.${format}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Picture Element
|
||||
|
||||
```html
|
||||
<picture>
|
||||
<source type="image/avif"
|
||||
srcset="img/hero-400.avif 400w, img/hero-768.avif 768w, img/hero-1280.avif 1280w, img/hero-1920.avif 1920w"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw" />
|
||||
<source type="image/webp"
|
||||
srcset="img/hero-400.webp 400w, img/hero-768.webp 768w, img/hero-1280.webp 1280w, img/hero-1920.webp 1920w"
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 80vw, 60vw" />
|
||||
<img src="img/hero-1280.jpg" alt="Descriptive alt text"
|
||||
width="1280" height="720" loading="lazy" decoding="async" />
|
||||
</picture>
|
||||
```
|
||||
|
||||
Hero/LCP image: `fetchpriority="high"`, NO `loading="lazy"`.
|
||||
|
||||
## Font Optimization
|
||||
|
||||
- Variable fonts = industry standard. WOFF2 only (97%+ support)
|
||||
- Subset with glyphhanger: `glyphhanger --US_ASCII --subset=font.woff2 --formats=woff2` (60%+ reduction)
|
||||
- `font-display: swap` + preload critical: `<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin />`
|
||||
|
||||
## SVG Sprites
|
||||
|
||||
```html
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
|
||||
<symbol id="icon-arrow" viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></symbol>
|
||||
</svg>
|
||||
<svg class="icon" aria-hidden="true"><use href="/sprites.svg#icon-arrow"/></svg>
|
||||
```
|
||||
|
||||
## Video
|
||||
|
||||
AV1 primary (30-50% better than H.264), H.265 fallback, H.264 universal. Always set poster, width/height.
|
||||
|
||||
```html
|
||||
<video autoplay muted loop playsinline poster="hero-poster.avif" preload="none" width="1920" height="1080">
|
||||
<source src="hero.av1.mp4" type='video/mp4; codecs="av01.0.08M.08"' />
|
||||
<source src="hero.h265.mp4" type='video/mp4; codecs="hvc1"' />
|
||||
<source src="hero.h264.mp4" type="video/mp4" />
|
||||
</video>
|
||||
```
|
||||
|
||||
Lazy load via IntersectionObserver (no native `loading="lazy"` for `<video>`).
|
||||
|
||||
## Audit Checklist
|
||||
|
||||
- [ ] All images: AVIF + WebP + fallback, responsive srcset
|
||||
- [ ] All `<img>`: explicit width/height (prevents CLS)
|
||||
- [ ] Hero/LCP: `fetchpriority="high"`, no lazy loading
|
||||
- [ ] Below-fold: `loading="lazy" decoding="async"`
|
||||
- [ ] Fonts: WOFF2, subsetted, font-display: swap, critical preloaded
|
||||
- [ ] Icons: SVG sprites (not individual files or icon fonts)
|
||||
- [ ] Video: AV1 > H.265 > H.264 cascade, poster image
|
||||
- [ ] No images >500KB, total page <1.5MB ideal
|
||||
101
skills/web-deploy/SKILL.md
Normal file
101
skills/web-deploy/SKILL.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
name: web-deploy
|
||||
description: Use when deploying websites — Cloudflare Pages, Vercel, edge functions, caching strategy, Core Web Vitals, CI/CD pipeline, DNS setup. Triggers on "deploy", "hosting", "cloudflare pages", "web vitals", "caching strategy".
|
||||
arguments:
|
||||
- name: command
|
||||
description: "Command: init, deploy, perf, cache, dns, ci, compare"
|
||||
required: false
|
||||
- name: framework
|
||||
description: "Framework: astro, next, sveltekit, react-router (auto-detect if omitted)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Web Deployment & Performance
|
||||
|
||||
Default target: Cloudflare Pages. Default framework: Astro 6.
|
||||
|
||||
## Platform Decision
|
||||
|
||||
| Platform | Free Tier | Pro Price | Best For |
|
||||
|----------|-----------|-----------|----------|
|
||||
| **Cloudflare Pages** | Unlimited BW, 500 builds/mo | $5/mo | Content sites, marketing (DEFAULT) |
|
||||
| Vercel | 100GB BW, 100 deploys/day | $20/user/mo | Next.js full-stack apps |
|
||||
| Netlify | 100GB BW, 300 build min | $19/user/mo | Static + built-in forms |
|
||||
|
||||
Cloudflare ecosystem: Workers, D1, R2, KV, Turnstile, Analytics — all free tier.
|
||||
|
||||
## Framework Decision
|
||||
|
||||
| Framework | Zero JS | Islands | Best For |
|
||||
|-----------|---------|---------|----------|
|
||||
| **Astro 6** | Yes | Yes | Content/marketing (DEFAULT) |
|
||||
| Next.js 16 | No | No | Full-stack React apps |
|
||||
| SvelteKit | Compiles | No | Animation-heavy, mobile-first |
|
||||
|
||||
Astro 6 static output: typical LCP <500ms on CF Pages.
|
||||
|
||||
## CDN Caching Strategy
|
||||
|
||||
| Asset Type | Cache-Control |
|
||||
|-----------|---------------|
|
||||
| Hashed JS/CSS/fonts | `public, max-age=31536000, immutable` |
|
||||
| HTML pages | `public, max-age=0, s-maxage=3600, stale-while-revalidate=86400` |
|
||||
| API/dynamic | `public, s-maxage=60, stale-while-revalidate=300` |
|
||||
| Images | `public, max-age=86400, s-maxage=604800` |
|
||||
|
||||
## Core Web Vitals
|
||||
|
||||
| Metric | Good | Key Fix |
|
||||
|--------|------|---------|
|
||||
| LCP | <2.5s | Preload hero: `fetchpriority="high"`, inline critical CSS, preload fonts |
|
||||
| INP | <200ms | Break tasks >50ms, `requestIdleCallback`, defer 3rd-party |
|
||||
| CLS | <0.1 | Width/height on all images/video, `aspect-ratio`, font size-adjust |
|
||||
|
||||
## GitHub Actions CI/CD
|
||||
|
||||
```yaml
|
||||
name: Deploy
|
||||
on: { push: { branches: [main] } }
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: 22, cache: npm }
|
||||
- run: npm ci && npm run build && npm test
|
||||
- uses: cloudflare/wrangler-action@v3
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy dist --project-name=my-site
|
||||
```
|
||||
|
||||
Secrets: `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID`.
|
||||
|
||||
## Cloudflare DNS + SSL
|
||||
|
||||
1. Add domain, change nameservers
|
||||
2. `A @ <ip> Proxied` + `CNAME www @ Proxied`
|
||||
3. SSL: Full (Strict), Always HTTPS, HSTS
|
||||
4. www→apex redirect rule (301)
|
||||
|
||||
## Edge Functions
|
||||
|
||||
| Feature | CF Workers | Vercel Edge |
|
||||
|---------|-----------|-------------|
|
||||
| Locations | 330+ | 30+ |
|
||||
| Cold start | <1ms | <50ms |
|
||||
| Free | 100K req/day | 1M/month |
|
||||
|
||||
## Deploy Checklist
|
||||
|
||||
- [ ] Build succeeds, tests pass
|
||||
- [ ] Lighthouse Performance >90
|
||||
- [ ] Core Web Vitals green
|
||||
- [ ] Caching headers per asset type
|
||||
- [ ] SSL/HTTPS enforced
|
||||
- [ ] www/apex redirect
|
||||
- [ ] Error pages (404, 500) configured
|
||||
- [ ] Security headers: CSP, X-Frame-Options, Referrer-Policy
|
||||
- [ ] Environment variables in dashboard
|
||||
315
skills/web-effects/SKILL.md
Normal file
315
skills/web-effects/SKILL.md
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
---
|
||||
name: web-effects
|
||||
description: Use when building visual web effects — WebGL shaders, image distortion, particles, noise/grain, hover effects, displacement maps. Covers curtains.js, OGL, tsParticles, custom WebGL, and CSS-only effects.
|
||||
arguments:
|
||||
- name: effect
|
||||
description: "Effect: distortion, particles, noise, hover, displacement, gradient, blur (auto-detect if omitted)"
|
||||
required: false
|
||||
- name: approach
|
||||
description: "Approach: css-only, webgl, canvas, library (auto-detect by complexity)"
|
||||
required: false
|
||||
---
|
||||
|
||||
# Web Effects Skill
|
||||
|
||||
## Decision Matrix — Pick Approach
|
||||
|
||||
| Effect | CSS Only | Canvas 2D | WebGL (library) | Custom WebGL |
|
||||
|--------|----------|-----------|-----------------|--------------|
|
||||
| Image hover distortion | No | No | curtains.js | Possible |
|
||||
| Particles (decorative) | Limited | Possible | tsParticles | Best perf |
|
||||
| Noise/grain overlay | Yes | Yes | Shader | Overkill |
|
||||
| Gradient animation | Yes | Possible | Unnecessary | No |
|
||||
| Blur/glassmorphism | Yes | No | No | No |
|
||||
| Displacement on scroll | No | No | curtains.js/OGL | Possible |
|
||||
| Liquid/fluid effects | No | No | OGL | Yes |
|
||||
| Image reveal/transition | CSS clip-path | Canvas | curtains.js | Possible |
|
||||
|
||||
**Rule:** Start with CSS. Escalate to Canvas/WebGL only when CSS cannot achieve the effect.
|
||||
|
||||
---
|
||||
|
||||
## 1. Curtains.js — DOM-Driven WebGL
|
||||
|
||||
**Bundle:** ~30KB min+gzip
|
||||
**What it does:** Converts HTML images/videos/canvases into WebGL textured planes that stay positioned with DOM layout.
|
||||
|
||||
**Best for:** Image hover distortion, displacement effects, WebGL transitions between slides.
|
||||
|
||||
```js
|
||||
import { Curtains, Plane } from "curtainsjs";
|
||||
|
||||
const curtains = new Curtains({ container: "#canvas" });
|
||||
|
||||
const plane = new Plane(curtains, document.querySelector(".image-wrapper"), {
|
||||
vertexShader: vertexShaderSource,
|
||||
fragmentShader: fragmentShaderSource,
|
||||
uniforms: {
|
||||
uMouse: { name: "uMouse", type: "2f", value: [0, 0] },
|
||||
uTime: { name: "uTime", type: "1f", value: 0 },
|
||||
}
|
||||
});
|
||||
|
||||
plane.onRender(() => { plane.uniforms.uTime.value++; });
|
||||
|
||||
document.querySelector(".image-wrapper").addEventListener("mousemove", (e) => {
|
||||
const rect = e.target.getBoundingClientRect();
|
||||
plane.uniforms.uMouse.value = [
|
||||
(e.clientX - rect.left) / rect.width,
|
||||
1 - (e.clientY - rect.top) / rect.height
|
||||
];
|
||||
});
|
||||
```
|
||||
|
||||
### Displacement Shader (Hover Distortion)
|
||||
|
||||
```glsl
|
||||
precision mediump float;
|
||||
varying vec2 vTextureCoord;
|
||||
uniform sampler2D uSampler0;
|
||||
uniform sampler2D uDisplacement;
|
||||
uniform vec2 uMouse;
|
||||
|
||||
void main() {
|
||||
vec2 uv = vTextureCoord;
|
||||
vec4 disp = texture2D(uDisplacement, uv);
|
||||
float dist = distance(uv, uMouse);
|
||||
float strength = smoothstep(0.3, 0.0, dist) * 0.05;
|
||||
uv += disp.rg * strength;
|
||||
gl_FragColor = texture2D(uSampler0, uv);
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** `gpu-curtains` is a WebGPU successor worth watching.
|
||||
|
||||
---
|
||||
|
||||
## 2. OGL — Minimal WebGL
|
||||
|
||||
**Bundle:** ~8KB gzip, zero dependencies
|
||||
**What it does:** Thin WebGL abstraction, you write your own shaders.
|
||||
|
||||
**Best for:** Custom shader effects, fullscreen post-processing, when curtains.js is too opinionated.
|
||||
|
||||
```js
|
||||
import { Renderer, Camera, Program, Mesh, Plane } from "ogl";
|
||||
|
||||
const renderer = new Renderer();
|
||||
const gl = renderer.gl;
|
||||
document.body.appendChild(gl.canvas);
|
||||
|
||||
const camera = new Camera(gl);
|
||||
camera.position.z = 1;
|
||||
|
||||
const geometry = new Plane(gl);
|
||||
|
||||
const program = new Program(gl, {
|
||||
vertex: `
|
||||
attribute vec3 position;
|
||||
attribute vec2 uv;
|
||||
varying vec2 vUv;
|
||||
void main() { vUv = uv; gl_Position = vec4(position, 1.0); }
|
||||
`,
|
||||
fragment: `
|
||||
precision highp float;
|
||||
varying vec2 vUv;
|
||||
uniform float uTime;
|
||||
void main() {
|
||||
gl_FragColor = vec4(vec3(sin(uTime + vUv.x * 6.28) * 0.5 + 0.5), 1.0);
|
||||
}
|
||||
`,
|
||||
uniforms: { uTime: { value: 0 } }
|
||||
});
|
||||
|
||||
const mesh = new Mesh(gl, { geometry, program });
|
||||
|
||||
function update(t) {
|
||||
requestAnimationFrame(update);
|
||||
program.uniforms.uTime.value = t * 0.001;
|
||||
renderer.render({ scene: mesh, camera });
|
||||
}
|
||||
requestAnimationFrame(update);
|
||||
```
|
||||
|
||||
**OGL vs Three.js:** OGL is 8KB vs Three.js ~150KB. Use OGL for shader effects where you do not need a scene graph, models, or physics.
|
||||
|
||||
---
|
||||
|
||||
## 3. Particles
|
||||
|
||||
### tsParticles (Library)
|
||||
|
||||
**Install:** `npm i tsparticles`
|
||||
**Bundle:** ~40KB min+gzip (core), modular
|
||||
**Frameworks:** React, Vue, Svelte, Angular, Solid, vanilla
|
||||
|
||||
```jsx
|
||||
import Particles from "@tsparticles/react";
|
||||
import { loadSlim } from "@tsparticles/slim";
|
||||
|
||||
function Background() {
|
||||
const init = useCallback(async (engine) => { await loadSlim(engine); }, []);
|
||||
|
||||
return (
|
||||
<Particles
|
||||
init={init}
|
||||
options={{
|
||||
particles: {
|
||||
number: { value: 50 },
|
||||
size: { value: { min: 1, max: 3 } },
|
||||
move: { enable: true, speed: 0.5 },
|
||||
opacity: { value: { min: 0.1, max: 0.5 } },
|
||||
links: { enable: true, distance: 150, opacity: 0.2 },
|
||||
},
|
||||
detectRetina: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom WebGL Particles (Performance-Critical)
|
||||
|
||||
When you need 10K+ particles at 60fps, do everything in shaders:
|
||||
|
||||
```glsl
|
||||
attribute vec3 position;
|
||||
attribute vec2 velocity;
|
||||
attribute float life;
|
||||
uniform float uTime;
|
||||
uniform float uDelta;
|
||||
|
||||
void main() {
|
||||
vec3 pos = position + vec3(velocity * uDelta, 0.0);
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
|
||||
gl_PointSize = mix(3.0, 0.0, life);
|
||||
}
|
||||
```
|
||||
|
||||
**Decision:** tsParticles for <1000 particles with config flexibility. Custom WebGL for >1000 particles or specific visual needs.
|
||||
|
||||
---
|
||||
|
||||
## 4. CSS-Only Effects
|
||||
|
||||
### Animated Gradient
|
||||
|
||||
```css
|
||||
.gradient-bg {
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient-shift 15s ease infinite;
|
||||
}
|
||||
@keyframes gradient-shift {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
```
|
||||
|
||||
### Noise/Grain Overlay (CSS)
|
||||
|
||||
```css
|
||||
.grain::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,...");
|
||||
opacity: 0.05;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
mix-blend-mode: overlay;
|
||||
}
|
||||
```
|
||||
|
||||
### Glassmorphism
|
||||
|
||||
```css
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(12px) saturate(150%);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(150%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 16px;
|
||||
}
|
||||
```
|
||||
|
||||
### Image Reveal (Clip-Path)
|
||||
|
||||
```css
|
||||
.reveal {
|
||||
clip-path: inset(0 100% 0 0);
|
||||
transition: clip-path 0.8s cubic-bezier(0.77, 0, 0.175, 1);
|
||||
}
|
||||
.reveal.visible { clip-path: inset(0 0 0 0); }
|
||||
```
|
||||
|
||||
### Hover Magnetic Effect (JS Required)
|
||||
|
||||
```js
|
||||
const btn = document.querySelector(".magnetic-btn");
|
||||
btn.addEventListener("mousemove", (e) => {
|
||||
const rect = btn.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left - rect.width / 2;
|
||||
const y = e.clientY - rect.top - rect.height / 2;
|
||||
btn.style.transform = `translate(${x * 0.3}px, ${y * 0.3}px)`;
|
||||
});
|
||||
btn.addEventListener("mouseleave", () => {
|
||||
btn.style.transform = "translate(0, 0)";
|
||||
btn.style.transition = "transform 0.5s ease";
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Performance Rules
|
||||
|
||||
### GPU-Composited Properties (animate these)
|
||||
|
||||
```
|
||||
transform — translate, rotate, scale
|
||||
opacity — fade in/out
|
||||
filter — blur, brightness
|
||||
clip-path — reveal/hide
|
||||
```
|
||||
|
||||
### Layout-Triggering Properties (avoid animating)
|
||||
|
||||
```
|
||||
width, height, top, left, right, bottom
|
||||
margin, padding, border-width
|
||||
font-size, line-height
|
||||
```
|
||||
|
||||
### will-change
|
||||
|
||||
```css
|
||||
.about-to-animate { will-change: transform, opacity; }
|
||||
/* Do NOT: * { will-change: transform; } */
|
||||
```
|
||||
|
||||
### Frame Budget
|
||||
|
||||
- **60fps target:** 16.66ms per frame
|
||||
- **Pause offscreen:** IntersectionObserver to stop animations outside viewport
|
||||
|
||||
```js
|
||||
const observer = new IntersectionObserver(([entry]) => {
|
||||
if (entry.isIntersecting) startRenderLoop();
|
||||
else stopRenderLoop();
|
||||
});
|
||||
observer.observe(canvasElement);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Define the effect** — what visual result is needed?
|
||||
2. **Try CSS first** — gradient, blur, clip-path, mix-blend-mode
|
||||
3. **Escalate to Canvas/WebGL** — only if CSS cannot achieve it
|
||||
4. **Pick library** — curtains.js for DOM-synced, OGL for custom shaders
|
||||
5. **Write shader** — keep fragment shaders simple, profile on mobile
|
||||
6. **Add IntersectionObserver** — pause offscreen effects
|
||||
7. **Test performance** — Chrome DevTools Performance, GPU memory
|
||||
8. **Add prefers-reduced-motion** — disable or simplify effects
|
||||
Loading…
Reference in a new issue