@ericrisco/rsc 3.1.3 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/manifest.json +43 -69
- package/package.json +6 -1
- package/scripts/design-upstream.js +92 -0
- package/scripts/doctor.js +0 -24
- package/scripts/lib/default-skill-floor.js +5 -1
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/recommend.js +1 -3
- package/scripts/lib/retired-skills.js +7 -1
- package/skills/ai-media/SKILL.md +2 -2
- package/skills/design/IMPECCABLE-LICENSE.txt +191 -0
- package/skills/design/SKILL.md +79 -328
- package/skills/design/THIRD_PARTY.md +26 -0
- package/skills/design/engine/LICENSE +21 -0
- package/skills/design/engine/RUN.md +515 -0
- package/skills/design/engine/UPSTREAM.json +4 -0
- package/skills/design/engine/assets/app.css +216 -0
- package/skills/design/engine/assets/base.css +105 -0
- package/skills/design/engine/assets/base.js +37 -0
- package/skills/design/engine/assets/charts.js +617 -0
- package/skills/design/engine/assets/scenes/stage.css +28 -0
- package/skills/design/engine/assets/scenes/stage.js +133 -0
- package/skills/design/engine/library/app.json +1327 -0
- package/skills/design/engine/library/directions.json +1078 -0
- package/skills/design/engine/library/heroes.json +1086 -0
- package/skills/design/engine/library/motions.json +92 -0
- package/skills/design/engine/library/palettes.json +1485 -0
- package/skills/design/engine/library/photos.json +7240 -0
- package/skills/design/engine/library/sections.json +4429 -0
- package/skills/design/engine/library/sites.json +8350 -0
- package/skills/design/engine/library/type.json +2024 -0
- package/skills/design/engine/references/app-screens.md +1199 -0
- package/skills/design/engine/references/colour.md +141 -0
- package/skills/design/engine/references/copy.md +177 -0
- package/skills/design/engine/references/critique.md +182 -0
- package/skills/design/engine/references/imagery.md +791 -0
- package/skills/design/engine/references/layout.md +292 -0
- package/skills/design/engine/references/motion.md +980 -0
- package/skills/design/engine/references/research.md +356 -0
- package/skills/design/engine/references/scenes.md +174 -0
- package/skills/design/engine/references/tells.md +115 -0
- package/skills/design/engine/references/type.md +170 -0
- package/skills/design/engine/scripts/assets.mjs +865 -0
- package/skills/design/engine/scripts/draw.mjs +584 -0
- package/skills/design/engine/scripts/inspect.mjs +303 -0
- package/skills/design/engine/scripts/lib/app-draw.mjs +203 -0
- package/skills/design/engine/scripts/lib/cdp.mjs +248 -0
- package/skills/design/engine/scripts/lib/probe.mjs +909 -0
- package/skills/design/engine/scripts/lib/rules.mjs +243 -0
- package/skills/design/engine/scripts/lint.mjs +119 -0
- package/skills/design/engine/scripts/scenes.mjs +124 -0
- package/skills/design/evals/cases.yaml +35 -46
- package/skills/design/references/design-md.md +49 -0
- package/skills/design/references/finish-review.md +46 -0
- package/skills/design/references/modes.md +51 -0
- package/skills/expo/SKILL.md +3 -4
- package/skills/marketing/references/landing-copy.md +2 -2
- package/skills/react/SKILL.md +2 -2
- package/skills/react-native/SKILL.md +3 -3
- package/skills/scroll-film/SKILL.md +220 -0
- package/skills/scroll-film/assets/page-template.html +128 -0
- package/skills/scroll-film/assets/scrub-engine.js +352 -0
- package/skills/scroll-film/evals/README.md +26 -0
- package/skills/scroll-film/evals/cases.yaml +81 -0
- package/skills/scroll-film/references/chain.md +218 -0
- package/skills/scroll-film/references/engine.md +132 -0
- package/skills/scroll-film/scripts/verify.sh +158 -0
- package/skills/swift-ios/SKILL.md +1 -1
- package/skills/ui-engineering/SKILL.md +3 -4
- package/skills/ui-engineering/evals/cases.yaml +2 -2
- package/skills/variant-explorer/SKILL.md +4 -4
- package/skills/variant-explorer/evals/cases.yaml +1 -1
- package/targets/clone-bootstrap.mjs +1 -1
- package/scripts/lib/design-identity.js +0 -117
- package/scripts/lib/starting-point.js +0 -314
- package/skills/design/evals/README.md +0 -53
- package/skills/design/references/ai-tells.md +0 -75
- package/skills/design/references/brand-grounding.md +0 -197
- package/skills/design/references/copywriting-frameworks.md +0 -138
- package/skills/design/references/design-systems.md +0 -49
- package/skills/design/references/inspiration-sources.md +0 -96
- package/skills/design/references/landing-anatomy-and-cro.md +0 -202
- package/skills/design/references/motion-and-interaction.md +0 -228
- package/skills/design/references/research-method.md +0 -154
- package/skills/design/references/signature-and-craft.md +0 -148
- package/skills/design/references/starting-point.md +0 -75
- package/skills/design/references/trends-2026.md +0 -80
- package/skills/design/references/visual-system.md +0 -237
- package/skills/design/scripts/verify.sh +0 -357
- package/skills/design-dna/PROMPT.md +0 -211
- package/skills/design-dna/SKILL.md +0 -162
- package/skills/design-dna/design-dna.schema.json +0 -230
- package/skills/design-dna/evals/README.md +0 -64
- package/skills/design-dna/evals/cases.yaml +0 -68
- package/skills/design-dna/references/method.md +0 -436
- package/skills/design-dna/scripts/emit.py +0 -164
- package/skills/design-dna/templates/PROMPT.md.tmpl +0 -42
- package/skills/design-dna/templates/SKILL.md.tmpl +0 -42
- package/skills/design-dna/templates/check.py.tmpl +0 -143
- package/skills/design-loop/SKILL.md +0 -160
- package/skills/design-loop/evals/README.md +0 -56
- package/skills/design-loop/evals/cases.yaml +0 -60
- package/skills/motion-craft/SKILL.md +0 -125
- package/skills/motion-craft/evals/cases.yaml +0 -38
- package/skills/motion-craft/scripts/verify.sh +0 -75
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
# Landing Anatomy & Conversion (CRO)
|
|
2
|
-
|
|
3
|
-
The CRO playbook. Every section earns its place by doing one job that moves a visitor toward conversion. Pair this with `copywriting-frameworks.md` for the words and `visual-system.md` for the tokens.
|
|
4
|
-
|
|
5
|
-
## Above the fold
|
|
6
|
-
|
|
7
|
-
The first viewport decides whether the visitor stays. It must pass the **5-second test**: a stranger reads it and can say what it is, who it's for, and why it's better.
|
|
8
|
-
|
|
9
|
-
- **F-pattern** for text-dense pages (docs, blogs): the eye sweeps the top line, drops, sweeps again. Put the value prop and primary CTA on the first horizontal sweep.
|
|
10
|
-
- **Z-pattern** for sparse hero layouts: top-left brand → top-right nav/CTA → diagonal to the headline → bottom-right primary action.
|
|
11
|
-
- **Must be visible without scrolling:** the value prop (headline + subhead), the primary CTA, one proof signal (logo strip or a hard metric), and the product itself (screenshot, terminal, or short loop) — not a stock gradient.
|
|
12
|
-
|
|
13
|
-
```text
|
|
14
|
-
Bad — Centered headline over an atmospheric gradient; product hidden below fold.
|
|
15
|
-
Good — Headline + subhead + "Start free" CTA + a real product screenshot, all above the fold.
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Section-by-section anatomy
|
|
19
|
-
|
|
20
|
-
Each section has a job, a copy framework, an accessibility note, and the conversion principle it serves.
|
|
21
|
-
|
|
22
|
-
| Section | Job | Copy framework | Conversion principle |
|
|
23
|
-
| --- | --- | --- | --- |
|
|
24
|
-
| Hero | State the value prop; pass the 5s test | Outcome + timeframe headline | Clarity beats cleverness |
|
|
25
|
-
| Logo / social-proof strip | Borrow credibility instantly | "Trusted by N teams" | Social proof / authority |
|
|
26
|
-
| Problem / agitation | Name the pain in the reader's words | PAS (Problem-Agitate) | Loss aversion |
|
|
27
|
-
| Solution | Show the product doing the job | FAB (feature→benefit) | Concreteness over claims |
|
|
28
|
-
| Features → benefits (bento) | Translate capability into outcome | JTBD per cell | Self-relevance |
|
|
29
|
-
| How-it-works | Reduce perceived effort | 3-step sequence | Effort reduction |
|
|
30
|
-
| Testimonials / case studies | Prove it works for people like them | Quantified quote | Similarity + proof |
|
|
31
|
-
| Objection handling | Preempt the top "no" | "X without Y" | Risk reduction |
|
|
32
|
-
| Pricing | Make the choice easy | Value framing, anchored | Anchoring + decoy |
|
|
33
|
-
| FAQ | Answer real blockers | Question→direct answer | Friction removal |
|
|
34
|
-
| Final CTA | One clear action | Value-on-the-button | Single decision |
|
|
35
|
-
| Footer | Navigation, legal, trust | Scannable links | Trust + completeness |
|
|
36
|
-
|
|
37
|
-
Accessibility note for every section: give each `<section>` an accessible name via `aria-labelledby` (pointing at its heading) or `aria-label`, and keep the heading order linear (`h1` → `h2` → `h3`, no skips).
|
|
38
|
-
|
|
39
|
-
## Full landing skeleton
|
|
40
|
-
|
|
41
|
-
One complete, copy-pasteable Next.js 15 App Router page: semantic landmarks, exactly one `<h1>`, the Metadata API, and valid JSON-LD.
|
|
42
|
-
|
|
43
|
-
```tsx
|
|
44
|
-
// app/page.tsx — semantic landing skeleton (Next.js 15 App Router)
|
|
45
|
-
import type { Metadata } from "next";
|
|
46
|
-
|
|
47
|
-
export const metadata: Metadata = {
|
|
48
|
-
title: "Deploy in minutes — Acme", // 50–60 chars
|
|
49
|
-
description: "Ship a fix in 4 minutes with no YAML and no on-call page. Free to start.", // 120–160
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export default function Page() {
|
|
53
|
-
return (
|
|
54
|
-
<main>
|
|
55
|
-
<section aria-labelledby="hero-h">
|
|
56
|
-
<h1 id="hero-h">Ship a fix in 4 minutes</h1>
|
|
57
|
-
{/* subhead, primary CTA, product screenshot */}
|
|
58
|
-
</section>
|
|
59
|
-
<section aria-label="Trusted by">{/* logo strip */}</section>
|
|
60
|
-
<section aria-labelledby="pricing-h">
|
|
61
|
-
<h2 id="pricing-h">Pricing</h2>
|
|
62
|
-
{/* 3-tier table */}
|
|
63
|
-
</section>
|
|
64
|
-
<section aria-labelledby="faq-h">
|
|
65
|
-
<h2 id="faq-h">FAQ</h2>
|
|
66
|
-
{/* question/answer list */}
|
|
67
|
-
</section>
|
|
68
|
-
<script
|
|
69
|
-
type="application/ld+json"
|
|
70
|
-
dangerouslySetInnerHTML={{ __html: JSON.stringify({
|
|
71
|
-
"@context": "https://schema.org",
|
|
72
|
-
"@type": "Organization",
|
|
73
|
-
name: "Acme",
|
|
74
|
-
url: "https://acme.example",
|
|
75
|
-
}) }}
|
|
76
|
-
/>
|
|
77
|
-
</main>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## CTA strategy
|
|
83
|
-
|
|
84
|
-
- **One primary action per viewport.** A secondary action (ghost button, text link) may accompany it, but never two equal-weight primaries competing for the click.
|
|
85
|
-
- **Placement cadence:** above the fold + after the value is established + after pricing + a sticky bar on mobile. Visitors convert at different scroll depths; meet each one.
|
|
86
|
-
- **Value on the button:** "Start free", "Get my estimate", "See it live" — never "Submit" or "Click here".
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
// components/sticky-cta.tsx — sticky mobile CTA, hidden on desktop (Client Component)
|
|
90
|
-
"use client";
|
|
91
|
-
|
|
92
|
-
export function StickyCta() {
|
|
93
|
-
return (
|
|
94
|
-
<div className="fixed inset-x-0 bottom-0 z-50 border-t border-fg/10 bg-bg/90 p-3 backdrop-blur md:hidden">
|
|
95
|
-
<a
|
|
96
|
-
href="#start"
|
|
97
|
-
className="flex min-h-11 items-center justify-center rounded-card bg-brand-500 font-medium text-white"
|
|
98
|
-
>
|
|
99
|
-
Start free
|
|
100
|
-
</a>
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Pricing psychology
|
|
107
|
-
|
|
108
|
-
- **Anchoring:** show the highest tier near the recommended one so the middle reads as reasonable.
|
|
109
|
-
- **3 tiers, highlighted middle:** the middle tier is the intended default; mark it with a ring and a "Most popular" badge.
|
|
110
|
-
- **Annual default:** show annual pricing first with the monthly equivalent ("$20/mo billed annually") — frames the lower number.
|
|
111
|
-
- **Value framing, not feature dumps:** lead each tier with the outcome the buyer gets, then list features.
|
|
112
|
-
- **Decoy effect:** a deliberately less attractive tier makes the target tier look better.
|
|
113
|
-
- **Risk reversal:** money-back guarantee, free trial, no credit card — lowers the cost of saying yes.
|
|
114
|
-
|
|
115
|
-
```html
|
|
116
|
-
<!-- 3-tier pricing, middle tier highlighted -->
|
|
117
|
-
<div class="mx-auto grid max-w-5xl grid-cols-1 gap-6 md:grid-cols-3">
|
|
118
|
-
<article class="rounded-card border border-fg/10 p-6">
|
|
119
|
-
<h3 class="font-semibold">Starter</h3>
|
|
120
|
-
<p class="price mt-2 text-3xl font-semibold">$0</p>
|
|
121
|
-
</article>
|
|
122
|
-
<article class="rounded-card border border-brand-500 p-6 ring-2 ring-brand-500">
|
|
123
|
-
<p class="text-xs font-medium text-brand-500">Most popular</p>
|
|
124
|
-
<h3 class="font-semibold">Team</h3>
|
|
125
|
-
<p class="price mt-2 text-3xl font-semibold">$20<span class="text-base text-fg/60">/mo</span></p>
|
|
126
|
-
</article>
|
|
127
|
-
<article class="rounded-card border border-fg/10 p-6">
|
|
128
|
-
<h3 class="font-semibold">Enterprise</h3>
|
|
129
|
-
<p class="mt-2 text-3xl font-semibold">Custom</p>
|
|
130
|
-
</article>
|
|
131
|
-
</div>
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Social proof
|
|
135
|
-
|
|
136
|
-
Logos, quantified testimonials, metrics, and trust badges — each placed where it answers the visitor's current doubt. Logos near the hero borrow authority early; a quantified case study near pricing closes the deal.
|
|
137
|
-
|
|
138
|
-
```text
|
|
139
|
-
Bad — "This tool is amazing, it changed everything for us!" — Happy Customer
|
|
140
|
-
Good — "Cut our deploy time from 22 min to 90 sec." — Priya R., Staff Eng, Northwind
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Quantified, attributed, specific. A vague rave is worth less than one hard number.
|
|
144
|
-
|
|
145
|
-
## Objection handling + FAQ
|
|
146
|
-
|
|
147
|
-
Surface the real reasons a visitor hesitates (price, migration cost, lock-in, security) and answer them directly. Add `FAQPage` JSON-LD **only** when the on-page content matches the markup — fabricated FAQ structured data is a guidelines violation.
|
|
148
|
-
|
|
149
|
-
```json
|
|
150
|
-
{
|
|
151
|
-
"@context": "https://schema.org",
|
|
152
|
-
"@type": "FAQPage",
|
|
153
|
-
"mainEntity": [
|
|
154
|
-
{
|
|
155
|
-
"@type": "Question",
|
|
156
|
-
"name": "Do I need to change my CI pipeline?",
|
|
157
|
-
"acceptedAnswer": {
|
|
158
|
-
"@type": "Answer",
|
|
159
|
-
"text": "No. Driftway runs as a single CLI step inside your existing pipeline."
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
"@type": "Question",
|
|
164
|
-
"name": "Is there a free tier?",
|
|
165
|
-
"acceptedAnswer": {
|
|
166
|
-
"@type": "Answer",
|
|
167
|
-
"text": "Yes. Solo projects are free; paid tiers start at $20/mo billed annually."
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## A/B mindset + instrumentation
|
|
175
|
-
|
|
176
|
-
- **One hypothesis at a time.** Change the headline OR the CTA color, not both — otherwise you cannot attribute the lift.
|
|
177
|
-
- **Measure:** conversion rate (primary), scroll depth (do they reach pricing?), and INP at the CTA (a janky button costs clicks).
|
|
178
|
-
- **Event-hook sketch:** fire a typed event on the primary action so the funnel is measurable.
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
// lib/track.ts — minimal typed conversion event
|
|
182
|
-
type Event = "cta_click" | "pricing_view" | "signup_start";
|
|
183
|
-
export function track(event: Event, props: Record<string, string | number> = {}) {
|
|
184
|
-
// forward to your analytics provider
|
|
185
|
-
window.dispatchEvent(new CustomEvent("analytics", { detail: { event, ...props } }));
|
|
186
|
-
}
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## SEO-aware structure constraint
|
|
190
|
-
|
|
191
|
-
Enforce the structural constraints here; defer keyword research, GEO, and technical audits to the `../marketing/SKILL.md` sibling (it carries SEO/GEO + keyword intent).
|
|
192
|
-
|
|
193
|
-
- Exactly one `<h1>`; linear heading order.
|
|
194
|
-
- Semantic landmarks (`header`/`nav`/`main`/`section`/`footer`).
|
|
195
|
-
- Metadata API: title 50–60 chars, description 120–160 chars.
|
|
196
|
-
- JSON-LD presence appropriate to the page: `Organization` sitewide, `Product` on a product page, `BreadcrumbList` for nested routes, `Article` for content, `FAQPage` only when FAQ content matches.
|
|
197
|
-
|
|
198
|
-
## See Also
|
|
199
|
-
|
|
200
|
-
- `copywriting-frameworks.md` — the words for every section above.
|
|
201
|
-
- `../../marketing/SKILL.md` — deep SEO/GEO, keyword research, and SEO-aware copy structure.
|
|
202
|
-
- `../../nextjs/SKILL.md` — App Router Metadata API and rendering specifics.
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
# Motion & Interaction
|
|
2
|
-
|
|
3
|
-
Motion intent and budget, current-first. CSS is the default engine; reach for a JS library only when CSS cannot express the animation. Deep mechanics (springs, layout animations, `AnimatePresence` internals) live in the external *motion-ui* and *motion-foundations* references (not repo skills) — this skill owns intent + budget and defers mechanics outward.
|
|
4
|
-
|
|
5
|
-
## Purpose gate
|
|
6
|
-
|
|
7
|
-
Every animation must do one of three things: **guide** attention, **communicate** state, or **preserve** continuity between views. If it does none, delete it. Decorative motion costs performance budget and attention while adding no information.
|
|
8
|
-
|
|
9
|
-
## Timing & easing tokens
|
|
10
|
-
|
|
11
|
-
Define duration and easing as tokens; never hand-tune per element.
|
|
12
|
-
|
|
13
|
-
```css
|
|
14
|
-
@theme {
|
|
15
|
-
--dur-instant: 80ms; /* checkbox tick, instant feedback */
|
|
16
|
-
--dur-fast: 180ms; /* hover, small state changes */
|
|
17
|
-
--dur-normal: 280ms; /* entrances, panel open */
|
|
18
|
-
--dur-slow: 600ms; /* large hero / staged sequences */
|
|
19
|
-
--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* decelerate; the default for UI */
|
|
20
|
-
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* on-screen movement, A to B */
|
|
21
|
-
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer / sheet (Ionic) */
|
|
22
|
-
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); /* playful overshoot, sparingly */
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching most, so
|
|
27
|
-
the interface feels sluggish — `ease-out` at 200ms *feels* faster than `ease-in` at 200ms. And the
|
|
28
|
-
built-in CSS easings are too weak to read as intentional; the curves above are the ones to reach for.
|
|
29
|
-
Need one that is not here? Take it from [easing.dev](https://easing.dev/), do not hand-roll it.
|
|
30
|
-
|
|
31
|
-
The choice, in decision order: entering or exiting → `ease-out`; moving on screen → `ease-in-out`;
|
|
32
|
-
hover or color → `ease`; constant motion (marquee, progress) → `linear`; unsure → `ease-out`.
|
|
33
|
-
`../../motion-craft/SKILL.md` owns the duration ranges, the easing families and the never-animate list, and
|
|
34
|
-
its diagnosis table maps the complaint — sluggish, cheap, janky — back to the cause.
|
|
35
|
-
|
|
36
|
-
- **Enter vs exit asymmetry:** entrances are slower and softer (`--dur-normal`, `--ease-out`); exits are quicker and quieter (~150ms) so dismissals feel responsive.
|
|
37
|
-
- **Press feedback:** `scale(0.97)` on `:active` gives a tactile button without distracting movement.
|
|
38
|
-
|
|
39
|
-
```css
|
|
40
|
-
/* Button press — explicit properties, never `transition: all` */
|
|
41
|
-
.button {
|
|
42
|
-
transition-property: transform, background-color, box-shadow;
|
|
43
|
-
transition-duration: 150ms;
|
|
44
|
-
transition-timing-function: var(--ease-out);
|
|
45
|
-
}
|
|
46
|
-
.button:active { transform: scale(0.97); }
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Micro-interactions
|
|
50
|
-
|
|
51
|
-
CSS-first, state-driven, interruptible (CSS transitions retarget mid-flight when intent changes).
|
|
52
|
-
|
|
53
|
-
```css
|
|
54
|
-
/* Hover + focus affordance on a card */
|
|
55
|
-
.card {
|
|
56
|
-
transition-property: transform, box-shadow;
|
|
57
|
-
transition-duration: 180ms;
|
|
58
|
-
transition-timing-function: var(--ease-out);
|
|
59
|
-
}
|
|
60
|
-
.card:hover { transform: translateY(-2px); box-shadow: var(--shadow-card); }
|
|
61
|
-
.card:focus-visible { outline: 2px solid var(--color-brand-500); outline-offset: 2px; }
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
```css
|
|
65
|
-
/* Icon cross-fade — fade + scale instead of an instant visibility toggle */
|
|
66
|
-
.icon-swap { transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out); }
|
|
67
|
-
.icon-swap[data-state="off"] { opacity: 0; transform: scale(0.8); }
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
```css
|
|
71
|
-
/* Skeleton shimmer for loading state */
|
|
72
|
-
.skeleton {
|
|
73
|
-
background: linear-gradient(90deg, oklch(0.9 0 0), oklch(0.95 0 0), oklch(0.9 0 0));
|
|
74
|
-
background-size: 200% 100%;
|
|
75
|
-
animation: shimmer 1.2s linear infinite;
|
|
76
|
-
}
|
|
77
|
-
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
For **optimistic state** (toggle/like), flip the visual immediately on click and reconcile when the request resolves — the UI never waits on the network for feedback.
|
|
81
|
-
|
|
82
|
-
## Scroll-driven, CSS-first
|
|
83
|
-
|
|
84
|
-
Native `animation-timeline: view()` and `scroll()` are the default for scroll-linked animation: zero JS, no main-thread scroll handler, no CLS. Use them before any library.
|
|
85
|
-
|
|
86
|
-
Guard scroll-driven animation behind a `@supports (animation-timeline: view())` feature query so the fallback is **explicit and visible**, not accidental. Elements start fully visible (`opacity: 1`); only inside the feature query — and only when motion is allowed — do they start hidden and animate in. A browser without scroll-timeline support (or a reduced-motion user) never enters the query, so content is never stuck invisible.
|
|
87
|
-
|
|
88
|
-
```css
|
|
89
|
-
/* Default: fully visible. No scroll-timeline support => content is never hidden. */
|
|
90
|
-
.reveal { opacity: 1; }
|
|
91
|
-
|
|
92
|
-
/* Progressive enhancement: only browsers WITH scroll-timeline opt in. */
|
|
93
|
-
@supports (animation-timeline: view()) {
|
|
94
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
95
|
-
.reveal {
|
|
96
|
-
animation: reveal linear both;
|
|
97
|
-
animation-timeline: view();
|
|
98
|
-
animation-range: entry 0% cover 30%;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
@keyframes reveal {
|
|
103
|
-
from { opacity: 0; translate: 0 16px; }
|
|
104
|
-
to { opacity: 1; translate: 0 0; }
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
```css
|
|
109
|
-
/* Parallax — element drifts as the page scrolls, driven by the scroll timeline */
|
|
110
|
-
@supports (animation-timeline: scroll()) {
|
|
111
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
112
|
-
.parallax {
|
|
113
|
-
animation: drift linear both;
|
|
114
|
-
animation-timeline: scroll(root block);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
@keyframes drift { to { translate: 0 -40px; } }
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Browser support note (verified June 2026): scroll-driven timelines (`animation-timeline` with `scroll()`/`view()`) now ship in Chromium (Chrome/Edge 115+) and Safari (18+, full support in Safari 26). Firefox has it implemented but still behind a flag (`layout.css.scroll-driven-animations.enabled`), so it is off by default for most users. That gap is exactly why the `@supports (animation-timeline: view())` query is load-bearing, not cosmetic: where the feature is unsupported (Firefox today, older engines), the feature query is skipped entirely and the element renders in its default (visible, un-drifted) state — a graceful, non-broken fallback. Always gate scroll-driven effects behind that `@supports` guard.
|
|
122
|
-
|
|
123
|
-
## Performance budget
|
|
124
|
-
|
|
125
|
-
- **Animate `transform`, `opacity`, `filter` only.** These run on the compositor and skip layout/paint.
|
|
126
|
-
- **Never animate `width`, `height`, `top`, `left`, `margin`.** They trigger layout on every frame and jank.
|
|
127
|
-
- **`will-change` sparingly** — only to fix a first-frame stutter on a compositor-friendly property, and remove it after. Never `will-change: all`.
|
|
128
|
-
- **Scroll handlers cost INP.** Prefer scroll-driven CSS; if you must use JS, throttle and keep handlers passive.
|
|
129
|
-
- **`content-visibility: auto`** on long offscreen sections skips their rendering work until they approach the viewport.
|
|
130
|
-
|
|
131
|
-
## Accessibility
|
|
132
|
-
|
|
133
|
-
Honor `prefers-reduced-motion` at both layers.
|
|
134
|
-
|
|
135
|
-
```css
|
|
136
|
-
/* CSS layer — kill non-essential motion globally */
|
|
137
|
-
@media (prefers-reduced-motion: reduce) {
|
|
138
|
-
*, *::before, *::after {
|
|
139
|
-
animation-duration: 0.01ms !important;
|
|
140
|
-
animation-iteration-count: 1 !important;
|
|
141
|
-
transition-duration: 0.01ms !important;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
// JS layer — branch behavior when motion must be coded
|
|
148
|
-
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
149
|
-
if (!reduce) {
|
|
150
|
-
// run the orchestrated entrance
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
- **No motion-only meaning:** never convey state (error, success) by movement alone; pair it with color, icon, or text.
|
|
155
|
-
- **Pause controls for loops:** any auto-playing or looping animation needs a way to stop it.
|
|
156
|
-
- **Vestibular safety:** cut large translate and parallax for reduced-motion users — big movement triggers nausea for some people.
|
|
157
|
-
|
|
158
|
-
## When to escalate to motion/react
|
|
159
|
-
|
|
160
|
-
Stay in CSS for hover, press, reveal, and parallax. Escalate to `motion/react` only for what CSS cannot express: orchestrated sequences, layout animations (`layout` prop), and exit animations of unmounting components.
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
"use client";
|
|
164
|
-
import { motion, AnimatePresence } from "motion/react";
|
|
165
|
-
// Use only for orchestration/exit that CSS can't express. Tokens + springs: motion-foundations.
|
|
166
|
-
|
|
167
|
-
export function Toast({ open, message }: { open: boolean; message: string }) {
|
|
168
|
-
return (
|
|
169
|
-
<AnimatePresence>
|
|
170
|
-
{open && (
|
|
171
|
-
<motion.div
|
|
172
|
-
initial={{ opacity: 0, y: 12 }}
|
|
173
|
-
animate={{ opacity: 1, y: 0 }}
|
|
174
|
-
exit={{ opacity: 0, y: 12 }}
|
|
175
|
-
transition={{ duration: 0.18, ease: [0.22, 1, 0.36, 1] }}
|
|
176
|
-
role="status"
|
|
177
|
-
>
|
|
178
|
-
{message}
|
|
179
|
-
</motion.div>
|
|
180
|
-
)}
|
|
181
|
-
</AnimatePresence>
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Once you are in `motion/react`, defer the mechanics (spring config, variants, `layoutId` shared transitions) to the external *motion-foundations* and *motion-ui* references.
|
|
187
|
-
|
|
188
|
-
## Escalating past the native path
|
|
189
|
-
|
|
190
|
-
Native `animation-timeline: view()` covers reveals, progress rails and parallax. Two effects it
|
|
191
|
-
cannot express are **pinning** (a section holds still while its content advances) and a
|
|
192
|
-
**horizontal pan** driven by vertical scroll. Those need a scroll library, and the improvised
|
|
193
|
-
version is always wrong in the same three ways. Escalate deliberately:
|
|
194
|
-
|
|
195
|
-
**Never listen to scroll directly.** `window.addEventListener('scroll')` fires off the main
|
|
196
|
-
thread's budget, blows INP, and never gets a passive-listener or cleanup story right. The
|
|
197
|
-
allowed inputs are `IntersectionObserver`, a scroll-driven CSS timeline, `useScroll()` from
|
|
198
|
-
`motion/react`, or GSAP's ScrollTrigger. Nothing else.
|
|
199
|
-
|
|
200
|
-
**Sticky stack** (pin a section, advance its layers):
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
// 'use client' leaf only. One trigger per pinned section, killed on unmount.
|
|
204
|
-
const st = ScrollTrigger.create({
|
|
205
|
-
trigger: sectionRef.current,
|
|
206
|
-
start: "top top", // pin the moment the section's top reaches the viewport top
|
|
207
|
-
end: () => `+=${panels.length * window.innerHeight}`,
|
|
208
|
-
pin: true,
|
|
209
|
-
pinSpacing: true, // without this the next section slides under the pin
|
|
210
|
-
scrub: 1, // 1 = follow scroll with a 1s catch-up, not a hard 1:1 jump
|
|
211
|
-
invalidateOnRefresh: true, // recompute on resize, or the end point is stale on rotate
|
|
212
|
-
});
|
|
213
|
-
// cleanup is not optional: return () => st.kill();
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
**Horizontal pan** is the same trigger with `end: () => "+=" + track.scrollWidth` and the
|
|
217
|
-
track moved via `x`, not `left` — `transform` is compositor-only, `left` triggers layout.
|
|
218
|
-
|
|
219
|
-
Both are opt-in above a motion budget, both stay inside
|
|
220
|
-
`@media (prefers-reduced-motion: no-preference)`, and both collapse to a plain vertical stack
|
|
221
|
-
below `768px` rather than pinning on a phone.
|
|
222
|
-
|
|
223
|
-
## See Also
|
|
224
|
-
|
|
225
|
-
- `visual-system.md` — the easing and duration tokens these animations consume.
|
|
226
|
-
- *motion-ui* — production motion patterns and library mechanics (external reference, not a repo skill).
|
|
227
|
-
- *motion-foundations* — spring physics and timing theory (external reference, not a repo skill).
|
|
228
|
-
- `../../nextjs/SKILL.md` — Client vs Server Component boundaries for animated UI.
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Research-First Design Method
|
|
2
|
-
|
|
3
|
-
The engine that keeps design output current and non-generic. Run it before you prescribe type, color, layout, or copy — every time.
|
|
4
|
-
|
|
5
|
-
## Why re-research every project
|
|
6
|
-
|
|
7
|
-
- **Trends churn quarterly.** What read as premium 18 months ago (neumorphism, heavy glassmorphism, blob backgrounds) now reads as dated. The reference set has a short half-life.
|
|
8
|
-
- **Model taste skews to the AI-template median.** Your built-in aesthetic prior is the average of every scraped landing page — purple→blue gradients, centered hero over atmospheric mesh, oversized cards. That average is the slop you must escape.
|
|
9
|
-
- **Competitors moved.** The benchmark is the current best work in *this* domain, not last year's. A dev tool that looked sharp against 2024 peers looks soft against 2026 peers.
|
|
10
|
-
- **The domain dictates the reference set.** A fintech dashboard, a developer CLI, and a DTC skincare store have nothing in common. Pull references from the right neighborhood, not a generic "good design" bucket.
|
|
11
|
-
|
|
12
|
-
Your built-in aesthetic prior is the problem this protocol fixes. Treat your first instinct as a hypothesis to be corrected by evidence, not as the answer.
|
|
13
|
-
|
|
14
|
-
## The loop
|
|
15
|
-
|
|
16
|
-
1. **Define 2–3 reference archetypes** from the DIRECTION BRIEF in `../SKILL.md`. Name them concretely: "Linear-grade dev tool, dark, type-led" beats "modern SaaS".
|
|
17
|
-
2. **WebSearch award galleries + tier-1 product sites.** Query the galleries by domain and year; query named competitors by page.
|
|
18
|
-
3. **WebFetch 3–5 exemplars,** prompting each for type, color, layout, motion, and copy voice. Demand concrete values, not adjectives.
|
|
19
|
-
4. **Extract a pattern table** — the shared moves (what every exemplar does) and the differentiators (where they diverge). Shared moves are table stakes; differentiators are where you choose.
|
|
20
|
-
5. **Synthesize a one-paragraph DESIGN DIRECTION** with citations: which URL contributed which decision.
|
|
21
|
-
6. **Build** against the direction.
|
|
22
|
-
7. **Re-check against references in QA** — open the exemplars side by side and confirm you matched the bar (density, type, motion restraint), not the pixels.
|
|
23
|
-
|
|
24
|
-
```text
|
|
25
|
-
WebSearch: "best <domain> landing page 2026 site:awwwards.com OR site:godly.website"
|
|
26
|
-
WebSearch: "<competitor> pricing page design"
|
|
27
|
-
WebFetch(url, "Extract: typeface pairing, color palette (approx OKLCH/hex),
|
|
28
|
-
layout system (grid/bento/asymmetry), motion (what animates, on what trigger),
|
|
29
|
-
hero copy voice. List concrete details, not adjectives.")
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
If WebSearch/WebFetch tools are unavailable in the current run, state that explicitly to the user, fall back to your strongest current knowledge of the named tier-1 sites, and flag the output as un-verified so it can be re-checked.
|
|
33
|
-
|
|
34
|
-
### What to extract per exemplar
|
|
35
|
-
|
|
36
|
-
Force every fetch to produce concrete, comparable data. Vague notes ("clean", "modern") are useless; you cannot build from an adjective.
|
|
37
|
-
|
|
38
|
-
| Field | Capture | Example |
|
|
39
|
-
| --- | --- | --- |
|
|
40
|
-
| Typeface pairing | display + text faces, weights | Geist Semibold / Inter Regular |
|
|
41
|
-
| Type scale | base size + ratio | 16px base, ~1.25 ratio |
|
|
42
|
-
| Palette | bg / fg / accent in OKLCH or hex | near-black bg, single blue accent |
|
|
43
|
-
| Layout | grid system, max-width, measure | centered hero, bento below, 1024px |
|
|
44
|
-
| Motion | what animates, on what trigger, duration | bento cells reveal on scroll, ~280ms |
|
|
45
|
-
| Copy voice | sentence length, hype level, verbs | short, no hype, imperative |
|
|
46
|
-
| Hero structure | what is shown above the fold | terminal recording + one CTA |
|
|
47
|
-
|
|
48
|
-
### Time-box the loop
|
|
49
|
-
|
|
50
|
-
Research is a means, not the deliverable. Spend ~15–25 minutes: 3–5 exemplars, one pattern table, one synthesis paragraph. If you are still browsing after 30 minutes you are procrastinating the build — commit to a direction and start.
|
|
51
|
-
|
|
52
|
-
## Source map
|
|
53
|
-
|
|
54
|
-
| Source | Mine for |
|
|
55
|
-
| --- | --- |
|
|
56
|
-
| awwwards.com | Bold direction, motion, art direction |
|
|
57
|
-
| godly.website | Current premium web aesthetics |
|
|
58
|
-
| land-book.com | Landing-page structure + section order |
|
|
59
|
-
| mobbin.com | Real app UI flows and patterns |
|
|
60
|
-
| Refactoring UI | Spacing, hierarchy, color rules |
|
|
61
|
-
| Linear | Restraint, dark UI, dense product surfaces |
|
|
62
|
-
| Stripe | Docs + marketing balance, gradients done right |
|
|
63
|
-
| Vercel | Type-led minimalism, dev-tool voice |
|
|
64
|
-
| Cursor / Resend | Dev-tool landing conventions |
|
|
65
|
-
|
|
66
|
-
Pick sources by domain. For a developer tool, weight Linear / Vercel / Cursor / Resend heavily and skip the expressive Awwwards entries. For an editorial or portfolio piece, invert that weighting.
|
|
67
|
-
|
|
68
|
-
## Synthesis template
|
|
69
|
-
|
|
70
|
-
Fill this block before writing markup. It is the contract the build must satisfy and the artifact QA checks against.
|
|
71
|
-
|
|
72
|
-
```text
|
|
73
|
-
DESIGN DIRECTION
|
|
74
|
-
Archetype ...... <e.g. "Linear-grade dev tool, dark, type-led">
|
|
75
|
-
Type pairing ... display=<face> / text=<face>, scale ratio <1.25>
|
|
76
|
-
Palette ........ bg/fg/brand/accent in OKLCH (list values)
|
|
77
|
-
Layout ......... <12-col | bento | asymmetric editorial>, max-width <px>, measure <ch>
|
|
78
|
-
Motion budget .. <reveals on entry only | none | expressive>, timings
|
|
79
|
-
Copy voice ..... <direct/compressed | warm | technical>; one-line value prop
|
|
80
|
-
Citations ...... <url 1>, <url 2>, <url 3> (what each contributed)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Worked example
|
|
84
|
-
|
|
85
|
-
Brief: a developer-tool landing page for **Driftway**, a fictional CLI that deploys preview environments from a Git branch in one command. Audience: backend and platform engineers. Tone: technical, dense, quiet.
|
|
86
|
-
|
|
87
|
-
References fetched and what each contributed:
|
|
88
|
-
|
|
89
|
-
- `https://linear.app` — dark, near-monochrome surface; tight type with a single accent; restraint as the whole aesthetic.
|
|
90
|
-
- `https://vercel.com` — type-led hero with the product (a terminal) shown immediately; minimal motion; high-contrast mono code blocks.
|
|
91
|
-
- `https://resend.com` — dev-tool voice in copy (verbs, no hype); a clean bento for feature → benefit; annual-default pricing.
|
|
92
|
-
|
|
93
|
-
Extracted pattern table:
|
|
94
|
-
|
|
95
|
-
| Dimension | Shared move | Differentiator chosen |
|
|
96
|
-
| --- | --- | --- |
|
|
97
|
-
| Type | One sans display + mono for code | Geist display, Geist Mono for CLI snippets |
|
|
98
|
-
| Color | Dark bg, one cool accent | bg near-black, single blue brand accent |
|
|
99
|
-
| Layout | Hero shows the product, then bento proof | Terminal recording as the hero visual |
|
|
100
|
-
| Motion | Subtle entry reveals only | Scroll-reveal on bento cells, ≤ 280ms |
|
|
101
|
-
| Copy | Outcome + "no X" phrasing | "Preview every branch — no staging queue" |
|
|
102
|
-
|
|
103
|
-
Resulting filled direction (token values match the `@theme` block in `visual-system.md`):
|
|
104
|
-
|
|
105
|
-
```text
|
|
106
|
-
DESIGN DIRECTION
|
|
107
|
-
Archetype ...... Linear-grade dev tool, dark, type-led, terminal-forward
|
|
108
|
-
Type pairing ... display=Geist / text=Inter (mono=Geist Mono for code), scale ratio 1.25
|
|
109
|
-
Palette ........ bg=oklch(0.17 0.01 256) fg=oklch(0.96 0.01 256)
|
|
110
|
-
brand=oklch(0.62 0.19 256) accent=oklch(0.78 0.13 256)
|
|
111
|
-
Layout ......... asymmetric bento under a centered hero, max-width 1024px, measure 65ch
|
|
112
|
-
Motion budget .. entry reveals only via animation-timeline: view(), enter 280ms / exit 150ms
|
|
113
|
-
Copy voice ..... direct, compressed, technical; value prop = "Preview every branch — no staging queue"
|
|
114
|
-
Citations ...... linear.app (restraint + dark surface), vercel.com (product-forward hero),
|
|
115
|
-
resend.com (dev voice + bento + annual pricing)
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
That direction now drives `visual-system.md` (tokens), `landing-anatomy-and-cro.md` (section order), and `copywriting-frameworks.md` (voice) with zero guesswork.
|
|
119
|
-
|
|
120
|
-
## Anti-pattern
|
|
121
|
-
|
|
122
|
-
Copying a reference pixel-for-pixel reproduces someone else's product constraints, not yours — and it shows. Extract *principles* (restraint, product-forward hero, one accent) and re-derive the specifics for your domain.
|
|
123
|
-
|
|
124
|
-
```text
|
|
125
|
-
Bad — Clone Linear's exact gradient, spacing, and copy onto a skincare store.
|
|
126
|
-
Good — Take Linear's PRINCIPLE (radical restraint, one accent, product-forward)
|
|
127
|
-
and re-derive a warm, editorial skincare palette + layout from it.
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
A second failure mode is **research theater**: fetching five sites, then ignoring them and building your default gradient hero anyway. The synthesis paragraph and its citations are the guardrail — if the built page does not visibly trace back to a cited decision, the research did not happen. In QA, open the exemplars beside the result and confirm each major decision (type, palette, layout, motion restraint) has a source.
|
|
131
|
-
|
|
132
|
-
## Feeding the result into the build
|
|
133
|
-
|
|
134
|
-
The filled DESIGN DIRECTION block is not a document for humans; it is the input to the rest of the skill:
|
|
135
|
-
|
|
136
|
-
- **Palette + type + radius + shadow** → become the `@theme` tokens in `visual-system.md`. Do not invent new values at build time; promote the direction's values into tokens verbatim.
|
|
137
|
-
- **Layout + section order** → drive the section stack in `landing-anatomy-and-cro.md`. The archetype decides whether you lead with a product shot (dev tool) or an editorial headline (portfolio).
|
|
138
|
-
- **Copy voice + value prop** → seed the VOICE block and headline work in `copywriting-frameworks.md`.
|
|
139
|
-
- **Motion budget** → sets the timing tokens and the reveal-or-not decision in `motion-and-interaction.md`.
|
|
140
|
-
|
|
141
|
-
When the direction changes mid-project (new competitor, pivot), re-run the loop rather than patching the old tokens — a stale direction silently reintroduces the generic default you escaped.
|
|
142
|
-
|
|
143
|
-
## See Also
|
|
144
|
-
|
|
145
|
-
- `visual-system.md` — turn the filled direction into tokens.
|
|
146
|
-
- `../../nextjs/SKILL.md` — implement the chosen direction on the App Router stack.
|
|
147
|
-
- *frontend-design-direction* — product-domain direction judgment (external reference, not a repo skill).
|
|
148
|
-
|
|
149
|
-
## Where to look
|
|
150
|
-
|
|
151
|
-
`inspiration-sources.md` is the dated registry of where to browse: whole pages to beat, SaaS and
|
|
152
|
-
conversion surfaces, real product flows (Mobbin, Page Flows — the bucket most people skip), single
|
|
153
|
-
patterns like footers and empty states, and the design systems that are specification rather than
|
|
154
|
-
inspiration. Extract principles, not pixels, and come back with a named page rather than a gallery.
|