@topy-ai/maggie 0.8.5 → 0.8.6
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-zh-TW.md +1 -1
- package/README.md +1 -1
- package/bundled-references/design-motion-production.md +234 -0
- package/bundled-references/landing-page-visual-defaults.md +101 -0
- package/bundled-skills/maggie-design-motion/SKILL.md +11 -1
- package/bundled-skills/maggie-landing-page/SKILL.md +6 -2
- package/package.json +1 -1
- package/references/design-motion-production.md +234 -0
- package/references/landing-page-visual-defaults.md +101 -0
package/README-zh-TW.md
CHANGED
|
@@ -143,7 +143,7 @@ structure drift 或重複 treatment product 時 fail closed。
|
|
|
143
143
|
deployment、memory、feedback 和 MaggieDash。內容先 draft/review,外部寫入、
|
|
144
144
|
publish 與 production deployment 需要明確確認。
|
|
145
145
|
|
|
146
|
-
目前 npm 版本為 `0.8.5
|
|
146
|
+
目前 npm 版本為 `0.8.6`,補齊兩個設計技能:加入完整的生產 reference(所有可複製的程式碼片段與預設表格),並把設計來源明確為 Figma 非必需(實時 URL、既有程式碼、Figma、或純截圖)。先前 0.8.5 採納兩個設計技能(改編自 MIT 的 `elayadesign/ai-design-skills`):maggie-landing-page(轉化策略 + 視覺系統紀律,具體數值為可被專案 DESIGN.md 覆蓋的預設值;由 maggie-design 生產與驗證)與 maggie-design-motion(靜態設計→像素級動畫 HTML 複刻→匯出 MP4 + GIF)。先前 0.8.4 將技能庫擴展成完整的「一人用 AI 跑一家公司」C-suite,新增八條職能線:maggie-sales(CRM 中立的銷售管線)、maggie-finance(只解釋的 CFO 指標,含確定性 maggie_finance.py 計算器)、maggie-support(面向客戶的客戶成功)、maggie-product(路線圖 + 預註冊實驗)、maggie-fundraising(pitch deck、data room、投資人更新)、maggie-legal(ToS/隱私/DPA/SOC2 readiness)、maggie-monetization(定價/paywall/dunning,金錢操作需人工批准)、maggie-orchestrator(CEO 編排層:將目標拆成帶審批門的 mission 分派到各職能線,含 maggie_orchestrator.py 規劃器)。先前 0.8.3 將 cmo.so/www 開發經驗沉澱進技能庫:新增兩個技能 maggie-skill-authoring(依 catalog 與 MCP 工具白名單撰寫/校驗社區營銷 playbook 技能)與 maggie-feedback-triage(反饋的團隊接收端:決策/定級/分派、PRD + issue 流程、24/7 隊列守護);新增多個 reference(OAuth PKCE 登入、Expo 約定與 Router 骨架、MCP evals 測試框架、conversation cards、WordPress→Firebase 認證遷移手冊);並增補 maggie-assistant-tools(tool≤user 每輪工具過濾、agent loop 安全不變量、preview-only 橋接代理、MCP resources/prompts、注解加固、CLI 與 ChatGPT 插件打包)、maggie-auth-reference(自清理登入測試套件、自簽單次令牌)、maggie-mobile-app(OTA 開機拉取、live==built 部署守衛)、maggie-deployment(pm2 單包發布、live-hash 校驗)與 maggie-ops(template-as-code 郵件同步)。先前 0.8.2 新增 maggie-spirit(為 AI 品牌生成平面像素 spirit/sprite 系統)與 goal-led AI agent 設計參考;0.8.0 為安全優化 roadmap(scan、image-patch、標頭評分、SSVC、GDPR 日誌規則、新 MCP 工具)。先前 0.8.0 為安全優化 roadmap(scan、image-patch、標頭評分、SSVC、GDPR 日誌規則、新 MCP 工具),含 fix-plan 修補風險分級 T1–T4(判斷補丁是否安全套用)與 host-impact 補丁影響面量測(哪些服務需重啟、是否需重開機)、public-check、Maggie Security、batch 31 排程 agent routines、報告收件匣與無人值守 agent 限制、PHP runtime 盤點與主機鎖、pull 網站改寫、query 點子與社群佇列檢查,batch 30 MaggieDash 先裝核心、release privacy gate、feedback 備援、社群連結與圖片檢查、大量頁面產生與 app 檢查,batch 29 預約規則、部署、app QA、助理與社群功能,以及 batch 28 產品 app、assistant tools、mobile、product release、skills catalog 與 bridge host 功能、batch 26、27 feedback 修正與 idempotent `maggie update`與 Booking email-template 合約/CLI validator 同步。本版完成 2026-09-24 booking feedback batch:Calendar grid/free-slot entry 與 host modal focus-restoration evidence。本版新增 Starter marketplace provisioning plan;本版完成 feedback batches 22、23:加入 declarative browser interaction evidence、design markup/style/content-key gate、compiler source anchoring、entity double-escape protection、`update --help` read-only、noindex hypothetical indexability、可過期 baseline acknowledgement、DOM-only link extraction regression,以及 MaggieDash 0.2.10 的 content override、短期 browser session、CDN-safe public-session cache contracts。authentication 和 isolated dev server 仍由 host 負責。`maggie-booking` workflow、傳統 email/password、Stripe、Resend、public booking flow 及既有安全邊界維持不變;provider-bound video generation 與 transactional delivery 仍記錄在 issue #39,前一批延後功能仍在 issue #38,不會直接成為 active memory。0.7.33 記錄並審查一筆更正回饋;確認 0.7.32 已正確處理 batch index,沒有修改已驗證的 shared behavior。0.7.32 加入 feedback 一基索引支援、MaggieDash panel 的 source/freshness/error evidence、Consent Mode 與實際 write/no-op reconciliation、SEO baseline recapture、sitemap origin rebasing、content-diff scope、migration ledger reconciliation、deployment credential preflight、release provenance/runtime preflight、icon release gate、opt-in blog auto-publish gate,以及 least-privilege VPS deployer。也修復 top-level `maggie feedback` dispatcher parity。0.7.31 加入 update 差異摘要、atomic installer copy、subset install manifest merge,以及 inventory drift 時 doctor non-zero。0.7.30 補上 feedback tracking issue closeout gate 與 batch 14/15 audit ledger。0.7.29 修正 npm package landing page 使用英文 README;0.7.28 加入 versioned Gemini model policy、明確 fallback 與 provenance、bundled-first CLI
|
|
147
147
|
dispatch、`maggie --version`、feedback batch review 聚合與重複偵測、marketplace enrichment evidence、booking
|
|
148
148
|
worker/resolver evidence,以及 read-only migration preflight。它也包含 host-owned mobile app surface contract、signed-in
|
|
149
149
|
camera-state QA、直接 Astro route resolution、correlated feedback batch,以及
|
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ Maggie keeps the existing project foundation and asks for decisions before
|
|
|
89
89
|
shared routes, analytics, or publishing boundaries change. The current
|
|
90
90
|
package ships 36 installable skills and a local-first MaggieDash foundation.
|
|
91
91
|
|
|
92
|
-
This package is `0.8.
|
|
92
|
+
This package is `0.8.6` — it completes the two adopted design skills with full production references (every copy-ready snippet and default table) and makes the design source Figma-optional (live URL, existing code, Figma, or a screenshot). `0.8.5` adopted the two design skills (adapted, MIT, from `elayadesign/ai-design-skills`): `maggie-landing-page` (conversion strategy + a visual-system discipline whose values defer to the project's DESIGN.md) and `maggie-design-motion` (static design → pixel-exact animated HTML replica → MP4 + GIF export). `0.8.4` expanded the library into a full "one person runs a company with AI" C-suite with eight new function lanes: `maggie-sales` (CRM-neutral pipeline), `maggie-finance` (explain-only CFO metrics + a deterministic `maggie_finance.py` calculator), `maggie-support` (customer success), `maggie-product` (roadmap + pre-registered experiments), `maggie-fundraising` (deck, data room, investor updates), `maggie-legal` (ToS/privacy/DPA/SOC2 readiness), `maggie-monetization` (pricing/paywalls/dunning; money actions human-approved), and `maggie-orchestrator` (the CEO layer that decomposes a goal into gated missions across the lanes, with a `maggie_orchestrator.py` planner). `0.8.3` folded cmo.so/www development learnings in (maggie-skill-authoring, maggie-feedback-triage, and mobile/MCP/auth references); `0.8.2` added `maggie-spirit`; `0.8.0` was the security optimization roadmap.
|
|
93
93
|
|
|
94
94
|
The 0.7.59 release made `maggie security host-audit` (the deep
|
|
95
95
|
lynis audit over SSH) robust on hosts where a storage test can stall: lynis runs
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Design-motion production detail
|
|
2
|
+
|
|
3
|
+
The complete, copy-ready snippets and tables for the `maggie-design-motion` skill.
|
|
4
|
+
The skill carries
|
|
5
|
+
the method in prose; this reference holds the full executable detail so nothing is
|
|
6
|
+
lost. Values are the defaults from the skill's "Make it yours" section. Adapted from
|
|
7
|
+
`elayadesign/ai-design-skills` `design-to-motion` (MIT); see the skill's Credits.
|
|
8
|
+
|
|
9
|
+
## Source alternatives (Figma is optional)
|
|
10
|
+
|
|
11
|
+
The best fidelity comes from a source with real layers and values, but **Figma is
|
|
12
|
+
not required**. In order of "least guessing":
|
|
13
|
+
|
|
14
|
+
1. **Live URL (recommended when the design is already on the web).** The design is
|
|
15
|
+
already rendered - do not replicate pixels. Open it in headless Chrome (the
|
|
16
|
+
project's browser tooling, as `maggie-clone`/`browser-inspection` do), read the
|
|
17
|
+
computed styles, fonts, and bounds from the DOM, and screenshot it for the
|
|
18
|
+
`?still` reference. You get exact values without pixel-lifting.
|
|
19
|
+
2. **Existing code / component.** If the design already exists as HTML/React/etc.,
|
|
20
|
+
skip replication entirely: mount it, add the motion layer (Part B) and the
|
|
21
|
+
capture (Part C) on top. This is the highest fidelity of all.
|
|
22
|
+
3. **Figma REST API.** Exact layers, bounds, fills, effects, radii (A2/A3). Best for
|
|
23
|
+
a design that lives only in Figma.
|
|
24
|
+
4. **Screenshot / flat image.** Always works; every element is rebuilt or lifted
|
|
25
|
+
from pixels (A2). Lowest fidelity, no external dependency at all.
|
|
26
|
+
|
|
27
|
+
Pick the first one that fits what the user actually has. Only paths 3-4 need the
|
|
28
|
+
pixel work below; paths 1-2 jump straight to the motion system.
|
|
29
|
+
|
|
30
|
+
## A3. Layer export placement (Figma)
|
|
31
|
+
|
|
32
|
+
Export each moving element as its own PNG at 3x (batched 20-25 ids):
|
|
33
|
+
|
|
34
|
+
| Case | Placement |
|
|
35
|
+
|---|---|
|
|
36
|
+
| PNG size equals `absoluteRenderBounds` (text always) | Place at the render bounds |
|
|
37
|
+
| PNG larger than render bounds (parent clipped it), even overhang | `x = bbox.x - (pngW - bbox.w)/2`; use `renderBounds.y` unless the top was clipped |
|
|
38
|
+
| Uneven overhang (arrowhead, one square cap) | Use `strokeGeometry`/`fillGeometry` bounds from the `geometry=paths` response |
|
|
39
|
+
| Rotated vectors with a large layer blur | `use_absolute_bounds=true`, place at the bbox; PSNR offset search only if that region < ~45 dB; blobs may store at 1x |
|
|
40
|
+
| Zero-height/width stroked vectors | `use_absolute_bounds=true` returns null; export without it |
|
|
41
|
+
| Mask group with blurred content | Anchor on the edge that was not clipped |
|
|
42
|
+
| Arcs, donuts, partial ellipses | `use_absolute_bounds=true` |
|
|
43
|
+
| Patterns/overlays larger than their surface | `use_absolute_bounds=true`, then crop to the surface rect |
|
|
44
|
+
| Instance children (`I12:34;56:78`) | Often null; export the parent instance |
|
|
45
|
+
| Lines that draw on | Write the centerline yourself and use it as a mask over the PNG (below) |
|
|
46
|
+
| Photo baked in a larger layer | Export the photo node separately; cover the baked photo with a skeleton, put the separate photo on top |
|
|
47
|
+
|
|
48
|
+
Keep Figma z-order; render frame strokes as an overlay above clipped content;
|
|
49
|
+
rebuild a container in CSS when its children must move separately; map drop-shadow
|
|
50
|
+
blur `R` to `box-shadow` blur `R` (or `filter: drop-shadow()` ~`R/2` on a group); a
|
|
51
|
+
group shadow is the shadow of the union; skip hidden layers.
|
|
52
|
+
|
|
53
|
+
### Drawing a line on
|
|
54
|
+
|
|
55
|
+
Read corner points and arc radii from `strokeGeometry` (centerline = halfway across
|
|
56
|
+
the stroke; a rounded corner's centerline radius = inner radius + half the stroke):
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<svg class="ly" viewBox="0 0 1440 990" style="left:0;top:0;width:1440px;height:990px">
|
|
60
|
+
<mask id="draw1">
|
|
61
|
+
<path d="M193 832 V700 A45 45 0 0 1 238 655 H380" fill="none" stroke="#fff" stroke-width="8"
|
|
62
|
+
pathLength="1" stroke-dasharray="1" stroke-dashoffset="1"/>
|
|
63
|
+
</mask>
|
|
64
|
+
<image href="data:image/png;base64,..." x="192" y="653" width="190" height="181" mask="url(#draw1)"/>
|
|
65
|
+
</svg>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Animate `stroke-dashoffset` 1 -> 0 on the JS clock. `pathLength="1"` avoids length
|
|
69
|
+
measuring; the wide mask stroke covers antialiasing. For travelling pulses, read
|
|
70
|
+
positions with `getPointAtLength` on the same path.
|
|
71
|
+
|
|
72
|
+
## A5. Clip pattern
|
|
73
|
+
|
|
74
|
+
Keeps stage coordinates valid inside a clip; for separate cards the card's unit
|
|
75
|
+
wraps its clip so the card and contents move together:
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<div class="clip" style="left:113px;top:138px;width:305px;height:907px;border-radius:16px 0 0 16px">
|
|
79
|
+
<div class="cin" style="left:-113px;top:-138px"><!-- units in stage coordinates --></div>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
.clip { position: absolute; overflow: hidden; isolation: isolate; }
|
|
85
|
+
.cin { position: absolute; width: var(--stage-w); height: var(--stage-h); }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## B2. Loading choreography kinds
|
|
89
|
+
|
|
90
|
+
| Kind | Use for | From |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `card` | Separate cards, with shadow | `translateY(24px)`, `scale(0.985)`, `blur(6px)`, opacity 0 |
|
|
93
|
+
| `up` | Text, headers, values, rows | `translateY(10px)`, `blur(4px)`, opacity 0 |
|
|
94
|
+
| `down` | Top toolbars/bars | `translateY(-8px)`, `blur(4px)` |
|
|
95
|
+
| `left` | Sidebar items, list rows, labels | `translateX(-10px)`, `blur(4px)` |
|
|
96
|
+
| `pop` | Icons, badges, avatars, buttons, dots | `scale(0.82-0.88)` from own centre, `blur(4px)` |
|
|
97
|
+
| `photo` | Images | skeleton shimmer, then `blur(14px) saturate(0.6)` to sharp |
|
|
98
|
+
| `wipe` | Bars, fills, highlights | left-to-right `clip-path` reveal in pixels on the full-stage unit |
|
|
99
|
+
| `line`/`grow` | Rules, ticks, timelines | `scaleX(0)`/`scaleY(0)` from the start edge |
|
|
100
|
+
| `donut` | Rings, gauges | conic mask sweeping clockwise from 12 o'clock |
|
|
101
|
+
| `tray`/`rise` | Sheets, trays, folders | slide up 40px from the container's bottom edge, clipped |
|
|
102
|
+
| `fade` | Layers under another layer | opacity only |
|
|
103
|
+
|
|
104
|
+
Containers load in reading order 80-90ms apart; items 30-80ms; total intro ~2-2.5s.
|
|
105
|
+
Never `scale(0)` an object (only lines/bars grow from zero). Covered layers arrive
|
|
106
|
+
last and leave first. Skeletons match the real shape, 1px inside it.
|
|
107
|
+
|
|
108
|
+
## B4. Story + accent tables
|
|
109
|
+
|
|
110
|
+
| Visual | Story |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Stack (leads, notifications, files) | A new item lands on top with blur, others step back a slot, the last leaves |
|
|
113
|
+
| Line/area chart | A scrubber glides across, dots ride the curve, the tooltip counts; or the line redraws and numbers count up |
|
|
114
|
+
| Bar chart, equalizer, grid | Bars breathe like a live feed and settle to design heights; a dot/tooltip rides the active bar |
|
|
115
|
+
| Donut, gauge | Segments ease out one at a time and back; or the ring sweeps again clockwise |
|
|
116
|
+
| Table, list | A cursor inspects rows, the row highlights, a preview loads; or a soft highlight steps down |
|
|
117
|
+
| Calendar, scheduler | A highlight searches for an open slot, the event glides in, then back |
|
|
118
|
+
| Flow, connectors | Pulses travel along connectors in flow direction, nodes light up as a pulse passes |
|
|
119
|
+
| AI, prompt, form | The prompt deletes and retypes, chips evaluate one by one, the result appears with blur |
|
|
120
|
+
| Counters, KPIs | Numbers count up with the chart that drives them |
|
|
121
|
+
| Logo row | A continuous marquee moving exactly one full set per loop |
|
|
122
|
+
| Media player | The handle and progress advance while the play button pulses |
|
|
123
|
+
|
|
124
|
+
| Accent | Use for | Values |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| Pulse ring | Status dots, live indicators, play | 1.8-2.2s period, ring scale 1 -> 2.6, opacity 0.55 -> 0 |
|
|
127
|
+
| Heartbeat | Hearts, likes, sparkles | +20% over 0.35s, every 3s |
|
|
128
|
+
| Gentle bob | Floating objects | +/-2.6px, 2.8s sine |
|
|
129
|
+
| Sheen sweep | Photos, bars, buttons | band 38% width, peak 34% white, 1.5s pass every 3.4s |
|
|
130
|
+
| Skeleton glimmer | Placeholder lines | 2.4s shimmer |
|
|
131
|
+
|
|
132
|
+
### Story track helper
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
// [[time, value], ...] eased per segment; values may be numbers or arrays
|
|
136
|
+
function track(keys, t, ease = easeInOut) {
|
|
137
|
+
if (t <= keys[0][0]) return keys[0][1];
|
|
138
|
+
for (let i = 1; i < keys.length; i++) {
|
|
139
|
+
const [t1, v1] = keys[i], [t0, v0] = keys[i - 1];
|
|
140
|
+
if (t <= t1) {
|
|
141
|
+
const k = ease(Math.min(1, (t - t0) / (t1 - t0 || 1)));
|
|
142
|
+
return Array.isArray(v0) ? v0.map((v, j) => v + (v1[j] - v) * k) : v0 + (v1 - v0) * k;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return keys[keys.length - 1][1];
|
|
146
|
+
}
|
|
147
|
+
// a slot that scrubs along a curve and returns to its design position
|
|
148
|
+
slot.style.transform = `translate(${track([[0,[0,0]],[1.2,[46,-18]],[2.6,[46,-18]],[3.8,[0,0]]], t).join('px,')}px)`;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## B5-B6. Cycle and easing
|
|
152
|
+
|
|
153
|
+
| Phase | Default timing |
|
|
154
|
+
|---|---|
|
|
155
|
+
| Build in | 0 -> 2.4s |
|
|
156
|
+
| Stories | 2.4 -> 11.0s |
|
|
157
|
+
| Dissolve | 11.0 -> 11.6s |
|
|
158
|
+
| Empty beat | 11.6 -> 12.0s |
|
|
159
|
+
|
|
160
|
+
| Token | Curve | Use |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `--e` | `cubic-bezier(0.16, 1, 0.3, 1)` | entrance transforms (~1.05s) |
|
|
163
|
+
| `--e2` | `cubic-bezier(0.33, 1, 0.68, 1)` | opacity and blur (0.7-0.8s) |
|
|
164
|
+
| `--io` | `cubic-bezier(0.65, 0, 0.35, 1)` | wipes, fills, sweeps, camera |
|
|
165
|
+
| linear | `linear` | constant motion only (shimmer, playback) |
|
|
166
|
+
|
|
167
|
+
## C1. Deterministic capture (full)
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
await page.clock.install({ time: 0 });
|
|
171
|
+
await page.goto(url + '?capture', { waitUntil: 'load' });
|
|
172
|
+
await page.evaluate(() => document.fonts.ready);
|
|
173
|
+
await page.clock.pauseAt(10000);
|
|
174
|
+
const loopTime = [];
|
|
175
|
+
for (let i = 0; i < frames; i++) {
|
|
176
|
+
// 1. whole milliseconds that average exactly 1000/60 (17,17,16,...); runFor(1000/60) drifts
|
|
177
|
+
const dt = Math.round((i + 1) * 1000 / 60) - Math.round(i * 1000 / 60);
|
|
178
|
+
await page.clock.runFor(dt);
|
|
179
|
+
loopTime.push(await page.evaluate(([i, PRE, dt]) => {
|
|
180
|
+
if (i === PRE) window.__start(); // 2. start inside the same evaluate as the seek
|
|
181
|
+
if (i >= PRE) window.__tick(dt / 1000); // 3. the page clock advances here, never through rAF
|
|
182
|
+
const now = performance.now();
|
|
183
|
+
for (const a of document.getAnimations()) { // CSS transitions follow the fake clock
|
|
184
|
+
a.__m ??= window.__mark;
|
|
185
|
+
a.pause(); a.currentTime = Math.max(0, now - a.__m);
|
|
186
|
+
}
|
|
187
|
+
return i >= PRE ? window.__lt : 0;
|
|
188
|
+
}, [i, PRE, dt]));
|
|
189
|
+
await page.screenshot({ path: `${out}/${String(i).padStart(5, '0')}.png` });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`PRE` is ~15 settle frames before the start. Viewport = frame size;
|
|
194
|
+
`deviceScaleFactor = min(1920/W, 1200/H)`; the export window starts at frame `PRE-1`
|
|
195
|
+
(loop time 0, empty state) and spans `cycle x 60` frames; record ~10 past it for the
|
|
196
|
+
seam check. Page side in `?capture`: `__start()` adds the `in` class and every state
|
|
197
|
+
change sets `window.__mark = performance.now()`; `__tick(dt)` advances loop time,
|
|
198
|
+
runs the cycle phases, draws stories/accents, sets `window.__lt`; never-ending loops
|
|
199
|
+
(shimmer, marquee) are driven by the JS clock, not infinite CSS keyframes.
|
|
200
|
+
|
|
201
|
+
## C2. Export commands
|
|
202
|
+
|
|
203
|
+
| File | Spec |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `<name>-x.mp4` | H.264, `yuv420p`, 60fps, CRF 14-15, preset slow, `+faststart`, max 1920x1200, even dims |
|
|
206
|
+
| `<name>-loop.gif` | 960px wide, 15-20fps, ~2-3MB |
|
|
207
|
+
| `<name>-800x500.gif` | always delivered; crop to content at 1.6:1 or fit+pad; 20fps (12 if heavy) |
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# MP4 for X
|
|
211
|
+
ffmpeg -start_number $A -framerate 60 -i frames/%05d.png -frames:v $N \
|
|
212
|
+
-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -preset slow -crf 15 \
|
|
213
|
+
-pix_fmt yuv420p -movflags +faststart name-x.mp4
|
|
214
|
+
|
|
215
|
+
# website GIF (960px)
|
|
216
|
+
ffmpeg -start_number $A -framerate 60 -i frames/%05d.png -filter_complex \
|
|
217
|
+
"trim=end_frame=$N,fps=20,scale=960:-2:flags=lanczos,split[a][b];\
|
|
218
|
+
[a]palettegen=max_colors=192:stats_mode=diff[p];\
|
|
219
|
+
[b][p]paletteuse=dither=bayer:bayer_scale=4:diff_mode=rectangle" -loop 0 name-loop.gif
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For the 800x500 GIF, add before `split`:
|
|
223
|
+
`scale=800:500:force_original_aspect_ratio=decrease,pad=800:500:(ow-iw)/2:(oh-ih)/2:color=<page bg>`.
|
|
224
|
+
Keep bayer dithering whenever a photo is in frame (else photos band); if a GIF is
|
|
225
|
+
too heavy, lower frame rate or colours before size.
|
|
226
|
+
|
|
227
|
+
## Make it yours (defaults)
|
|
228
|
+
|
|
229
|
+
Rise 10px (`up`), 8px (`down`/`left`); entrance blur 4px (photos 14px); pop scale
|
|
230
|
+
0.82-0.88; transform 1.05s `--e`; opacity/blur 0.7-0.8s `--e2`; wipe 0.7s (slow bars
|
|
231
|
+
1.1s) `--io`; card rise 24px + `scale(0.985)` + `blur(6px)`; website hero appear 6px
|
|
232
|
+
rise + 2px blur, 50-70ms stagger, ~1.2s; container stagger 80-90ms; typing 45-70ms/
|
|
233
|
+
glyph; story start 2.4s; story length 3-8s; cycle 12s; MP4 60fps CRF 15; GIFs 960px
|
|
234
|
+
and 800x500 at 20fps.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Landing-page visual defaults
|
|
2
|
+
|
|
3
|
+
The complete default value tables for the `maggie-landing-page` skill (Part B). These are
|
|
4
|
+
**defaults**: the user's prompt and the project's own system (a `DESIGN.md`, token
|
|
5
|
+
set, or brand) win over them. Use them to fill gaps or when there is no system yet,
|
|
6
|
+
never to repaint an established brand. Adapted from `elayadesign/ai-design-skills`
|
|
7
|
+
`landing-page-design` (MIT; itself from `Leonxlnx/taste-skill`); see the skill's
|
|
8
|
+
Credits.
|
|
9
|
+
|
|
10
|
+
## Fonts
|
|
11
|
+
|
|
12
|
+
- **Default to:** Geist, Manrope, or Poppins (one typeface per site). A mono (Geist
|
|
13
|
+
Mono) only for code, data, or numeric UI where a monospace is functionally needed.
|
|
14
|
+
- **Avoid defaulting to:** Inter, Roboto, Arial, Open Sans, Helvetica.
|
|
15
|
+
- No italics in the interface; cap weight at bold (no 900/black).
|
|
16
|
+
- The project's font choice always wins.
|
|
17
|
+
|
|
18
|
+
## Type scale (snap every size to a step)
|
|
19
|
+
|
|
20
|
+
Snap to the closest step **below**, taking its paired line height. Never leave
|
|
21
|
+
arbitrary values (`text-[19px]`, `font-size: 22px`, `1.4rem`). Default scale is
|
|
22
|
+
Tailwind's:
|
|
23
|
+
|
|
24
|
+
| Class | Size | Line height |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `text-xs` | 12px (0.75rem) | 16px |
|
|
27
|
+
| `text-sm` | 14px (0.875rem) | 20px |
|
|
28
|
+
| `text-base` | 16px (1rem) | 24px |
|
|
29
|
+
| `text-lg` | 18px (1.125rem) | 28px |
|
|
30
|
+
| `text-xl` | 20px (1.25rem) | 28px |
|
|
31
|
+
| `text-2xl` | 24px (1.5rem) | 32px |
|
|
32
|
+
| `text-3xl` | 30px (1.875rem) | 36px |
|
|
33
|
+
| `text-4xl` | 36px (2.25rem) | 40px |
|
|
34
|
+
| `text-5xl` | 48px (3rem) | 1 |
|
|
35
|
+
| `text-6xl` | 60px (3.75rem) | 1 |
|
|
36
|
+
| `text-7xl` | 72px (4.5rem) | 1 |
|
|
37
|
+
| `text-8xl` | 96px (6rem) | 1 |
|
|
38
|
+
| `text-9xl` | 128px (8rem) | 1 |
|
|
39
|
+
|
|
40
|
+
Buttons: main `text-base` (16px) semibold; smaller header buttons `text-sm` (14px)
|
|
41
|
+
semibold.
|
|
42
|
+
|
|
43
|
+
## Spacing (only these values)
|
|
44
|
+
|
|
45
|
+
| Token | Value | | Token | Value |
|
|
46
|
+
|---|---|---|---|---|
|
|
47
|
+
| Spacing-0 | 0 | | Spacing-300 | 24px |
|
|
48
|
+
| Spacing-25 | 2px | | Spacing-400 | 32px |
|
|
49
|
+
| Spacing-50 | 4px | | Spacing-500 | 40px |
|
|
50
|
+
| Spacing-75 | 8px | | Spacing-600 | 48px |
|
|
51
|
+
| Spacing-100 | 12px | | Spacing-700 | 64px |
|
|
52
|
+
| Spacing-200 | 16px | | Spacing-800 | 80px |
|
|
53
|
+
| | | | Spacing-900 | 96px |
|
|
54
|
+
|
|
55
|
+
Main buttons: 8px vertical / 12px horizontal padding.
|
|
56
|
+
|
|
57
|
+
## Corner radius
|
|
58
|
+
|
|
59
|
+
Use the token radii. **Nested-radius formula** when a shape sits inside another and
|
|
60
|
+
the gap is under 32px: `inner = outer - gap`, applied only when the result is > 2px
|
|
61
|
+
(else leave square). Example: an outer card at 16px with 8px padding gives the inner
|
|
62
|
+
element an 8px radius.
|
|
63
|
+
|
|
64
|
+
## Backgrounds and hero
|
|
65
|
+
|
|
66
|
+
- Backgrounds are flat - no gradients. The one exception is gradient **text** on the
|
|
67
|
+
hero heading. (A project whose brand uses gradient surfaces overrides this.)
|
|
68
|
+
- Never border only one side of a card (all around or none).
|
|
69
|
+
- Default dark-mode background colours: `#000000`, `#181818`, `#1F1F1F`, `#272727`,
|
|
70
|
+
`#313131`, `#131209`.
|
|
71
|
+
- Hero heading text gradient: dark theme `#FFFFFF` -> `#9B9B9B`; light theme
|
|
72
|
+
`#000000` -> `#666666`, left to right. Cap heading and subheading width at ~680px;
|
|
73
|
+
break lines at meaningful points, never mid-phrase.
|
|
74
|
+
|
|
75
|
+
## Icons
|
|
76
|
+
|
|
77
|
+
Default to Phosphor, Solar, or Iconamoon; avoid Material Icons/Symbols. The
|
|
78
|
+
project's icon set wins; validate it with `maggie-design`'s icon inventory +
|
|
79
|
+
glyph-render gates before release.
|
|
80
|
+
|
|
81
|
+
## Motion
|
|
82
|
+
|
|
83
|
+
- Base curve `cubic-bezier(0.22,1,0.36,1)` with ~500ms for layout/position changes;
|
|
84
|
+
hover colour/opacity 300ms (never under 200ms). Never a browser-default curve
|
|
85
|
+
(`ease`, `linear`, `ease-in-out`).
|
|
86
|
+
- Animate only `transform` and `opacity`; never `top`/`left`/`width`/`height`.
|
|
87
|
+
- Respect `prefers-reduced-motion` (replace movement with a plain opacity change).
|
|
88
|
+
- Scroll reveals rise ~32px and fade over 600-800ms, once, via `IntersectionObserver`
|
|
89
|
+
(never an unthrottled scroll listener).
|
|
90
|
+
- Menu link entrance: items arrive in reading order, each rising ~24px while fading,
|
|
91
|
+
stepped 60ms apart, capped at 400ms total.
|
|
92
|
+
- Tagline reveal: words go from ~25-35% opacity to full colour one at a time in
|
|
93
|
+
reading order as each crosses a trigger line (base curve; `IntersectionObserver`
|
|
94
|
+
per word or an rAF-throttled listener).
|
|
95
|
+
|
|
96
|
+
## Banned filler words (content realism)
|
|
97
|
+
|
|
98
|
+
Do not write "revolutionize", "supercharge", "cutting edge", "effortless",
|
|
99
|
+
"transform your workflow", "take it to the next level", "harness the power", or
|
|
100
|
+
"world class". Say what the product does in plain terms. Sentence case for headings
|
|
101
|
+
and buttons; calm, specific system messages.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: maggie-design-motion
|
|
3
3
|
description: Turn a static design (a Figma frame, screenshot, dashboard, card set, app screen, or a UI on a photo) into a pixel-exact HTML replica that loads from an empty state, then has every visual act out what it does in a seamless loop, and export an MP4 plus GIFs. Use when asked to animate a design, a Figma link, a screenshot, UI cards, a dashboard or hero image, or to make a showcase loop, motion mockup, or a video/GIF of a design. Also for bento/feature cards on a landing page.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Maggie Design Motion
|
|
@@ -21,6 +21,16 @@ completion. Timing/distance/curve values live in **Make it yours** (defaults;
|
|
|
21
21
|
override there, never ad hoc). Adapted from `elayadesign/ai-design-skills` (MIT);
|
|
22
22
|
see Credits.
|
|
23
23
|
|
|
24
|
+
**Figma is optional.** Choose the source with the least guessing for what the user
|
|
25
|
+
actually has: a **live URL** (already rendered - read computed styles + screenshot
|
|
26
|
+
via the project's browser tooling, as `maggie-clone`/`browser-inspection` do; no
|
|
27
|
+
pixel-lifting), **existing code/component** (skip replication, add the motion layer
|
|
28
|
+
on top - highest fidelity), **Figma REST** (exact layers), or a **screenshot** (works
|
|
29
|
+
with zero dependencies, elements rebuilt/lifted from pixels). Only Figma and
|
|
30
|
+
screenshot need the pixel work in Part A; a live URL or existing code jumps to the
|
|
31
|
+
motion system. See [design-motion production detail](../../references/design-motion-production.md)
|
|
32
|
+
for the full source-alternatives list and every copy-ready snippet and table.
|
|
33
|
+
|
|
24
34
|
## Two contexts (decide first)
|
|
25
35
|
|
|
26
36
|
| | Showcase | Website |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: maggie-landing-page
|
|
3
3
|
description: Build a high-converting landing page - intake, page structure, layout selection, conversion copy, SEO/AEO, plus a visual-system discipline (typography, spacing, radius, backgrounds, hero, icons, motion) whose specific values are defaults the project's DESIGN.md overrides. Use when building, editing, styling, or writing copy for a landing page, marketing page, or a page section meant to convert. Designs the page; maggie-design produces and validates it.
|
|
4
4
|
metadata:
|
|
5
|
-
version: 0.1.
|
|
5
|
+
version: 0.1.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Maggie Landing Page
|
|
@@ -106,7 +106,11 @@ proof buried; mobile that breaks readability; no clear next step.
|
|
|
106
106
|
# Part B - Visual system (defaults; the project's DESIGN.md wins)
|
|
107
107
|
|
|
108
108
|
Resolve every visual value through a rule, never ad hoc. When the project has a
|
|
109
|
-
token system, use it; the values below are the starting defaults.
|
|
109
|
+
token system, use it; the values below are the starting defaults. The **complete
|
|
110
|
+
default tables** (the full type scale, spacing tokens, dark-mode colours, hero
|
|
111
|
+
gradients, motion curves and durations, banned filler words) are in
|
|
112
|
+
[landing-page visual defaults](../../references/landing-page-visual-defaults.md); the
|
|
113
|
+
summary below is the working subset.
|
|
110
114
|
|
|
111
115
|
## B1. Typography
|
|
112
116
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Design-motion production detail
|
|
2
|
+
|
|
3
|
+
The complete, copy-ready snippets and tables for the `maggie-design-motion` skill.
|
|
4
|
+
The skill carries
|
|
5
|
+
the method in prose; this reference holds the full executable detail so nothing is
|
|
6
|
+
lost. Values are the defaults from the skill's "Make it yours" section. Adapted from
|
|
7
|
+
`elayadesign/ai-design-skills` `design-to-motion` (MIT); see the skill's Credits.
|
|
8
|
+
|
|
9
|
+
## Source alternatives (Figma is optional)
|
|
10
|
+
|
|
11
|
+
The best fidelity comes from a source with real layers and values, but **Figma is
|
|
12
|
+
not required**. In order of "least guessing":
|
|
13
|
+
|
|
14
|
+
1. **Live URL (recommended when the design is already on the web).** The design is
|
|
15
|
+
already rendered - do not replicate pixels. Open it in headless Chrome (the
|
|
16
|
+
project's browser tooling, as `maggie-clone`/`browser-inspection` do), read the
|
|
17
|
+
computed styles, fonts, and bounds from the DOM, and screenshot it for the
|
|
18
|
+
`?still` reference. You get exact values without pixel-lifting.
|
|
19
|
+
2. **Existing code / component.** If the design already exists as HTML/React/etc.,
|
|
20
|
+
skip replication entirely: mount it, add the motion layer (Part B) and the
|
|
21
|
+
capture (Part C) on top. This is the highest fidelity of all.
|
|
22
|
+
3. **Figma REST API.** Exact layers, bounds, fills, effects, radii (A2/A3). Best for
|
|
23
|
+
a design that lives only in Figma.
|
|
24
|
+
4. **Screenshot / flat image.** Always works; every element is rebuilt or lifted
|
|
25
|
+
from pixels (A2). Lowest fidelity, no external dependency at all.
|
|
26
|
+
|
|
27
|
+
Pick the first one that fits what the user actually has. Only paths 3-4 need the
|
|
28
|
+
pixel work below; paths 1-2 jump straight to the motion system.
|
|
29
|
+
|
|
30
|
+
## A3. Layer export placement (Figma)
|
|
31
|
+
|
|
32
|
+
Export each moving element as its own PNG at 3x (batched 20-25 ids):
|
|
33
|
+
|
|
34
|
+
| Case | Placement |
|
|
35
|
+
|---|---|
|
|
36
|
+
| PNG size equals `absoluteRenderBounds` (text always) | Place at the render bounds |
|
|
37
|
+
| PNG larger than render bounds (parent clipped it), even overhang | `x = bbox.x - (pngW - bbox.w)/2`; use `renderBounds.y` unless the top was clipped |
|
|
38
|
+
| Uneven overhang (arrowhead, one square cap) | Use `strokeGeometry`/`fillGeometry` bounds from the `geometry=paths` response |
|
|
39
|
+
| Rotated vectors with a large layer blur | `use_absolute_bounds=true`, place at the bbox; PSNR offset search only if that region < ~45 dB; blobs may store at 1x |
|
|
40
|
+
| Zero-height/width stroked vectors | `use_absolute_bounds=true` returns null; export without it |
|
|
41
|
+
| Mask group with blurred content | Anchor on the edge that was not clipped |
|
|
42
|
+
| Arcs, donuts, partial ellipses | `use_absolute_bounds=true` |
|
|
43
|
+
| Patterns/overlays larger than their surface | `use_absolute_bounds=true`, then crop to the surface rect |
|
|
44
|
+
| Instance children (`I12:34;56:78`) | Often null; export the parent instance |
|
|
45
|
+
| Lines that draw on | Write the centerline yourself and use it as a mask over the PNG (below) |
|
|
46
|
+
| Photo baked in a larger layer | Export the photo node separately; cover the baked photo with a skeleton, put the separate photo on top |
|
|
47
|
+
|
|
48
|
+
Keep Figma z-order; render frame strokes as an overlay above clipped content;
|
|
49
|
+
rebuild a container in CSS when its children must move separately; map drop-shadow
|
|
50
|
+
blur `R` to `box-shadow` blur `R` (or `filter: drop-shadow()` ~`R/2` on a group); a
|
|
51
|
+
group shadow is the shadow of the union; skip hidden layers.
|
|
52
|
+
|
|
53
|
+
### Drawing a line on
|
|
54
|
+
|
|
55
|
+
Read corner points and arc radii from `strokeGeometry` (centerline = halfway across
|
|
56
|
+
the stroke; a rounded corner's centerline radius = inner radius + half the stroke):
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<svg class="ly" viewBox="0 0 1440 990" style="left:0;top:0;width:1440px;height:990px">
|
|
60
|
+
<mask id="draw1">
|
|
61
|
+
<path d="M193 832 V700 A45 45 0 0 1 238 655 H380" fill="none" stroke="#fff" stroke-width="8"
|
|
62
|
+
pathLength="1" stroke-dasharray="1" stroke-dashoffset="1"/>
|
|
63
|
+
</mask>
|
|
64
|
+
<image href="data:image/png;base64,..." x="192" y="653" width="190" height="181" mask="url(#draw1)"/>
|
|
65
|
+
</svg>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Animate `stroke-dashoffset` 1 -> 0 on the JS clock. `pathLength="1"` avoids length
|
|
69
|
+
measuring; the wide mask stroke covers antialiasing. For travelling pulses, read
|
|
70
|
+
positions with `getPointAtLength` on the same path.
|
|
71
|
+
|
|
72
|
+
## A5. Clip pattern
|
|
73
|
+
|
|
74
|
+
Keeps stage coordinates valid inside a clip; for separate cards the card's unit
|
|
75
|
+
wraps its clip so the card and contents move together:
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<div class="clip" style="left:113px;top:138px;width:305px;height:907px;border-radius:16px 0 0 16px">
|
|
79
|
+
<div class="cin" style="left:-113px;top:-138px"><!-- units in stage coordinates --></div>
|
|
80
|
+
</div>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
.clip { position: absolute; overflow: hidden; isolation: isolate; }
|
|
85
|
+
.cin { position: absolute; width: var(--stage-w); height: var(--stage-h); }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## B2. Loading choreography kinds
|
|
89
|
+
|
|
90
|
+
| Kind | Use for | From |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `card` | Separate cards, with shadow | `translateY(24px)`, `scale(0.985)`, `blur(6px)`, opacity 0 |
|
|
93
|
+
| `up` | Text, headers, values, rows | `translateY(10px)`, `blur(4px)`, opacity 0 |
|
|
94
|
+
| `down` | Top toolbars/bars | `translateY(-8px)`, `blur(4px)` |
|
|
95
|
+
| `left` | Sidebar items, list rows, labels | `translateX(-10px)`, `blur(4px)` |
|
|
96
|
+
| `pop` | Icons, badges, avatars, buttons, dots | `scale(0.82-0.88)` from own centre, `blur(4px)` |
|
|
97
|
+
| `photo` | Images | skeleton shimmer, then `blur(14px) saturate(0.6)` to sharp |
|
|
98
|
+
| `wipe` | Bars, fills, highlights | left-to-right `clip-path` reveal in pixels on the full-stage unit |
|
|
99
|
+
| `line`/`grow` | Rules, ticks, timelines | `scaleX(0)`/`scaleY(0)` from the start edge |
|
|
100
|
+
| `donut` | Rings, gauges | conic mask sweeping clockwise from 12 o'clock |
|
|
101
|
+
| `tray`/`rise` | Sheets, trays, folders | slide up 40px from the container's bottom edge, clipped |
|
|
102
|
+
| `fade` | Layers under another layer | opacity only |
|
|
103
|
+
|
|
104
|
+
Containers load in reading order 80-90ms apart; items 30-80ms; total intro ~2-2.5s.
|
|
105
|
+
Never `scale(0)` an object (only lines/bars grow from zero). Covered layers arrive
|
|
106
|
+
last and leave first. Skeletons match the real shape, 1px inside it.
|
|
107
|
+
|
|
108
|
+
## B4. Story + accent tables
|
|
109
|
+
|
|
110
|
+
| Visual | Story |
|
|
111
|
+
|---|---|
|
|
112
|
+
| Stack (leads, notifications, files) | A new item lands on top with blur, others step back a slot, the last leaves |
|
|
113
|
+
| Line/area chart | A scrubber glides across, dots ride the curve, the tooltip counts; or the line redraws and numbers count up |
|
|
114
|
+
| Bar chart, equalizer, grid | Bars breathe like a live feed and settle to design heights; a dot/tooltip rides the active bar |
|
|
115
|
+
| Donut, gauge | Segments ease out one at a time and back; or the ring sweeps again clockwise |
|
|
116
|
+
| Table, list | A cursor inspects rows, the row highlights, a preview loads; or a soft highlight steps down |
|
|
117
|
+
| Calendar, scheduler | A highlight searches for an open slot, the event glides in, then back |
|
|
118
|
+
| Flow, connectors | Pulses travel along connectors in flow direction, nodes light up as a pulse passes |
|
|
119
|
+
| AI, prompt, form | The prompt deletes and retypes, chips evaluate one by one, the result appears with blur |
|
|
120
|
+
| Counters, KPIs | Numbers count up with the chart that drives them |
|
|
121
|
+
| Logo row | A continuous marquee moving exactly one full set per loop |
|
|
122
|
+
| Media player | The handle and progress advance while the play button pulses |
|
|
123
|
+
|
|
124
|
+
| Accent | Use for | Values |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| Pulse ring | Status dots, live indicators, play | 1.8-2.2s period, ring scale 1 -> 2.6, opacity 0.55 -> 0 |
|
|
127
|
+
| Heartbeat | Hearts, likes, sparkles | +20% over 0.35s, every 3s |
|
|
128
|
+
| Gentle bob | Floating objects | +/-2.6px, 2.8s sine |
|
|
129
|
+
| Sheen sweep | Photos, bars, buttons | band 38% width, peak 34% white, 1.5s pass every 3.4s |
|
|
130
|
+
| Skeleton glimmer | Placeholder lines | 2.4s shimmer |
|
|
131
|
+
|
|
132
|
+
### Story track helper
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
// [[time, value], ...] eased per segment; values may be numbers or arrays
|
|
136
|
+
function track(keys, t, ease = easeInOut) {
|
|
137
|
+
if (t <= keys[0][0]) return keys[0][1];
|
|
138
|
+
for (let i = 1; i < keys.length; i++) {
|
|
139
|
+
const [t1, v1] = keys[i], [t0, v0] = keys[i - 1];
|
|
140
|
+
if (t <= t1) {
|
|
141
|
+
const k = ease(Math.min(1, (t - t0) / (t1 - t0 || 1)));
|
|
142
|
+
return Array.isArray(v0) ? v0.map((v, j) => v + (v1[j] - v) * k) : v0 + (v1 - v0) * k;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return keys[keys.length - 1][1];
|
|
146
|
+
}
|
|
147
|
+
// a slot that scrubs along a curve and returns to its design position
|
|
148
|
+
slot.style.transform = `translate(${track([[0,[0,0]],[1.2,[46,-18]],[2.6,[46,-18]],[3.8,[0,0]]], t).join('px,')}px)`;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## B5-B6. Cycle and easing
|
|
152
|
+
|
|
153
|
+
| Phase | Default timing |
|
|
154
|
+
|---|---|
|
|
155
|
+
| Build in | 0 -> 2.4s |
|
|
156
|
+
| Stories | 2.4 -> 11.0s |
|
|
157
|
+
| Dissolve | 11.0 -> 11.6s |
|
|
158
|
+
| Empty beat | 11.6 -> 12.0s |
|
|
159
|
+
|
|
160
|
+
| Token | Curve | Use |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| `--e` | `cubic-bezier(0.16, 1, 0.3, 1)` | entrance transforms (~1.05s) |
|
|
163
|
+
| `--e2` | `cubic-bezier(0.33, 1, 0.68, 1)` | opacity and blur (0.7-0.8s) |
|
|
164
|
+
| `--io` | `cubic-bezier(0.65, 0, 0.35, 1)` | wipes, fills, sweeps, camera |
|
|
165
|
+
| linear | `linear` | constant motion only (shimmer, playback) |
|
|
166
|
+
|
|
167
|
+
## C1. Deterministic capture (full)
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
await page.clock.install({ time: 0 });
|
|
171
|
+
await page.goto(url + '?capture', { waitUntil: 'load' });
|
|
172
|
+
await page.evaluate(() => document.fonts.ready);
|
|
173
|
+
await page.clock.pauseAt(10000);
|
|
174
|
+
const loopTime = [];
|
|
175
|
+
for (let i = 0; i < frames; i++) {
|
|
176
|
+
// 1. whole milliseconds that average exactly 1000/60 (17,17,16,...); runFor(1000/60) drifts
|
|
177
|
+
const dt = Math.round((i + 1) * 1000 / 60) - Math.round(i * 1000 / 60);
|
|
178
|
+
await page.clock.runFor(dt);
|
|
179
|
+
loopTime.push(await page.evaluate(([i, PRE, dt]) => {
|
|
180
|
+
if (i === PRE) window.__start(); // 2. start inside the same evaluate as the seek
|
|
181
|
+
if (i >= PRE) window.__tick(dt / 1000); // 3. the page clock advances here, never through rAF
|
|
182
|
+
const now = performance.now();
|
|
183
|
+
for (const a of document.getAnimations()) { // CSS transitions follow the fake clock
|
|
184
|
+
a.__m ??= window.__mark;
|
|
185
|
+
a.pause(); a.currentTime = Math.max(0, now - a.__m);
|
|
186
|
+
}
|
|
187
|
+
return i >= PRE ? window.__lt : 0;
|
|
188
|
+
}, [i, PRE, dt]));
|
|
189
|
+
await page.screenshot({ path: `${out}/${String(i).padStart(5, '0')}.png` });
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`PRE` is ~15 settle frames before the start. Viewport = frame size;
|
|
194
|
+
`deviceScaleFactor = min(1920/W, 1200/H)`; the export window starts at frame `PRE-1`
|
|
195
|
+
(loop time 0, empty state) and spans `cycle x 60` frames; record ~10 past it for the
|
|
196
|
+
seam check. Page side in `?capture`: `__start()` adds the `in` class and every state
|
|
197
|
+
change sets `window.__mark = performance.now()`; `__tick(dt)` advances loop time,
|
|
198
|
+
runs the cycle phases, draws stories/accents, sets `window.__lt`; never-ending loops
|
|
199
|
+
(shimmer, marquee) are driven by the JS clock, not infinite CSS keyframes.
|
|
200
|
+
|
|
201
|
+
## C2. Export commands
|
|
202
|
+
|
|
203
|
+
| File | Spec |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `<name>-x.mp4` | H.264, `yuv420p`, 60fps, CRF 14-15, preset slow, `+faststart`, max 1920x1200, even dims |
|
|
206
|
+
| `<name>-loop.gif` | 960px wide, 15-20fps, ~2-3MB |
|
|
207
|
+
| `<name>-800x500.gif` | always delivered; crop to content at 1.6:1 or fit+pad; 20fps (12 if heavy) |
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# MP4 for X
|
|
211
|
+
ffmpeg -start_number $A -framerate 60 -i frames/%05d.png -frames:v $N \
|
|
212
|
+
-vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -preset slow -crf 15 \
|
|
213
|
+
-pix_fmt yuv420p -movflags +faststart name-x.mp4
|
|
214
|
+
|
|
215
|
+
# website GIF (960px)
|
|
216
|
+
ffmpeg -start_number $A -framerate 60 -i frames/%05d.png -filter_complex \
|
|
217
|
+
"trim=end_frame=$N,fps=20,scale=960:-2:flags=lanczos,split[a][b];\
|
|
218
|
+
[a]palettegen=max_colors=192:stats_mode=diff[p];\
|
|
219
|
+
[b][p]paletteuse=dither=bayer:bayer_scale=4:diff_mode=rectangle" -loop 0 name-loop.gif
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For the 800x500 GIF, add before `split`:
|
|
223
|
+
`scale=800:500:force_original_aspect_ratio=decrease,pad=800:500:(ow-iw)/2:(oh-ih)/2:color=<page bg>`.
|
|
224
|
+
Keep bayer dithering whenever a photo is in frame (else photos band); if a GIF is
|
|
225
|
+
too heavy, lower frame rate or colours before size.
|
|
226
|
+
|
|
227
|
+
## Make it yours (defaults)
|
|
228
|
+
|
|
229
|
+
Rise 10px (`up`), 8px (`down`/`left`); entrance blur 4px (photos 14px); pop scale
|
|
230
|
+
0.82-0.88; transform 1.05s `--e`; opacity/blur 0.7-0.8s `--e2`; wipe 0.7s (slow bars
|
|
231
|
+
1.1s) `--io`; card rise 24px + `scale(0.985)` + `blur(6px)`; website hero appear 6px
|
|
232
|
+
rise + 2px blur, 50-70ms stagger, ~1.2s; container stagger 80-90ms; typing 45-70ms/
|
|
233
|
+
glyph; story start 2.4s; story length 3-8s; cycle 12s; MP4 60fps CRF 15; GIFs 960px
|
|
234
|
+
and 800x500 at 20fps.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Landing-page visual defaults
|
|
2
|
+
|
|
3
|
+
The complete default value tables for the `maggie-landing-page` skill (Part B). These are
|
|
4
|
+
**defaults**: the user's prompt and the project's own system (a `DESIGN.md`, token
|
|
5
|
+
set, or brand) win over them. Use them to fill gaps or when there is no system yet,
|
|
6
|
+
never to repaint an established brand. Adapted from `elayadesign/ai-design-skills`
|
|
7
|
+
`landing-page-design` (MIT; itself from `Leonxlnx/taste-skill`); see the skill's
|
|
8
|
+
Credits.
|
|
9
|
+
|
|
10
|
+
## Fonts
|
|
11
|
+
|
|
12
|
+
- **Default to:** Geist, Manrope, or Poppins (one typeface per site). A mono (Geist
|
|
13
|
+
Mono) only for code, data, or numeric UI where a monospace is functionally needed.
|
|
14
|
+
- **Avoid defaulting to:** Inter, Roboto, Arial, Open Sans, Helvetica.
|
|
15
|
+
- No italics in the interface; cap weight at bold (no 900/black).
|
|
16
|
+
- The project's font choice always wins.
|
|
17
|
+
|
|
18
|
+
## Type scale (snap every size to a step)
|
|
19
|
+
|
|
20
|
+
Snap to the closest step **below**, taking its paired line height. Never leave
|
|
21
|
+
arbitrary values (`text-[19px]`, `font-size: 22px`, `1.4rem`). Default scale is
|
|
22
|
+
Tailwind's:
|
|
23
|
+
|
|
24
|
+
| Class | Size | Line height |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `text-xs` | 12px (0.75rem) | 16px |
|
|
27
|
+
| `text-sm` | 14px (0.875rem) | 20px |
|
|
28
|
+
| `text-base` | 16px (1rem) | 24px |
|
|
29
|
+
| `text-lg` | 18px (1.125rem) | 28px |
|
|
30
|
+
| `text-xl` | 20px (1.25rem) | 28px |
|
|
31
|
+
| `text-2xl` | 24px (1.5rem) | 32px |
|
|
32
|
+
| `text-3xl` | 30px (1.875rem) | 36px |
|
|
33
|
+
| `text-4xl` | 36px (2.25rem) | 40px |
|
|
34
|
+
| `text-5xl` | 48px (3rem) | 1 |
|
|
35
|
+
| `text-6xl` | 60px (3.75rem) | 1 |
|
|
36
|
+
| `text-7xl` | 72px (4.5rem) | 1 |
|
|
37
|
+
| `text-8xl` | 96px (6rem) | 1 |
|
|
38
|
+
| `text-9xl` | 128px (8rem) | 1 |
|
|
39
|
+
|
|
40
|
+
Buttons: main `text-base` (16px) semibold; smaller header buttons `text-sm` (14px)
|
|
41
|
+
semibold.
|
|
42
|
+
|
|
43
|
+
## Spacing (only these values)
|
|
44
|
+
|
|
45
|
+
| Token | Value | | Token | Value |
|
|
46
|
+
|---|---|---|---|---|
|
|
47
|
+
| Spacing-0 | 0 | | Spacing-300 | 24px |
|
|
48
|
+
| Spacing-25 | 2px | | Spacing-400 | 32px |
|
|
49
|
+
| Spacing-50 | 4px | | Spacing-500 | 40px |
|
|
50
|
+
| Spacing-75 | 8px | | Spacing-600 | 48px |
|
|
51
|
+
| Spacing-100 | 12px | | Spacing-700 | 64px |
|
|
52
|
+
| Spacing-200 | 16px | | Spacing-800 | 80px |
|
|
53
|
+
| | | | Spacing-900 | 96px |
|
|
54
|
+
|
|
55
|
+
Main buttons: 8px vertical / 12px horizontal padding.
|
|
56
|
+
|
|
57
|
+
## Corner radius
|
|
58
|
+
|
|
59
|
+
Use the token radii. **Nested-radius formula** when a shape sits inside another and
|
|
60
|
+
the gap is under 32px: `inner = outer - gap`, applied only when the result is > 2px
|
|
61
|
+
(else leave square). Example: an outer card at 16px with 8px padding gives the inner
|
|
62
|
+
element an 8px radius.
|
|
63
|
+
|
|
64
|
+
## Backgrounds and hero
|
|
65
|
+
|
|
66
|
+
- Backgrounds are flat - no gradients. The one exception is gradient **text** on the
|
|
67
|
+
hero heading. (A project whose brand uses gradient surfaces overrides this.)
|
|
68
|
+
- Never border only one side of a card (all around or none).
|
|
69
|
+
- Default dark-mode background colours: `#000000`, `#181818`, `#1F1F1F`, `#272727`,
|
|
70
|
+
`#313131`, `#131209`.
|
|
71
|
+
- Hero heading text gradient: dark theme `#FFFFFF` -> `#9B9B9B`; light theme
|
|
72
|
+
`#000000` -> `#666666`, left to right. Cap heading and subheading width at ~680px;
|
|
73
|
+
break lines at meaningful points, never mid-phrase.
|
|
74
|
+
|
|
75
|
+
## Icons
|
|
76
|
+
|
|
77
|
+
Default to Phosphor, Solar, or Iconamoon; avoid Material Icons/Symbols. The
|
|
78
|
+
project's icon set wins; validate it with `maggie-design`'s icon inventory +
|
|
79
|
+
glyph-render gates before release.
|
|
80
|
+
|
|
81
|
+
## Motion
|
|
82
|
+
|
|
83
|
+
- Base curve `cubic-bezier(0.22,1,0.36,1)` with ~500ms for layout/position changes;
|
|
84
|
+
hover colour/opacity 300ms (never under 200ms). Never a browser-default curve
|
|
85
|
+
(`ease`, `linear`, `ease-in-out`).
|
|
86
|
+
- Animate only `transform` and `opacity`; never `top`/`left`/`width`/`height`.
|
|
87
|
+
- Respect `prefers-reduced-motion` (replace movement with a plain opacity change).
|
|
88
|
+
- Scroll reveals rise ~32px and fade over 600-800ms, once, via `IntersectionObserver`
|
|
89
|
+
(never an unthrottled scroll listener).
|
|
90
|
+
- Menu link entrance: items arrive in reading order, each rising ~24px while fading,
|
|
91
|
+
stepped 60ms apart, capped at 400ms total.
|
|
92
|
+
- Tagline reveal: words go from ~25-35% opacity to full colour one at a time in
|
|
93
|
+
reading order as each crosses a trigger line (base curve; `IntersectionObserver`
|
|
94
|
+
per word or an rAF-throttled listener).
|
|
95
|
+
|
|
96
|
+
## Banned filler words (content realism)
|
|
97
|
+
|
|
98
|
+
Do not write "revolutionize", "supercharge", "cutting edge", "effortless",
|
|
99
|
+
"transform your workflow", "take it to the next level", "harness the power", or
|
|
100
|
+
"world class". Say what the product does in plain terms. Sentence case for headings
|
|
101
|
+
and buttons; calm, specific system messages.
|