From 90c2543f5e95b1bb27d35e358d8e7e3968faf4c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Jun 2026 12:44:18 +0000 Subject: [PATCH] chore: format with prettier [skip ci] --- .agents/skills/emil-design-eng/SKILL.md | 220 +++++++++--------- .agents/skills/review-animations/SKILL.md | 14 +- .agents/skills/review-animations/STANDARDS.md | 118 +++++++--- .../landing/components/LandingAgentsBar.tsx | 7 +- .../landing/components/LandingFeatures.tsx | 12 +- .../src/landing/components/LandingHero.tsx | 5 +- .../landing/components/LandingSocialProof.tsx | 5 +- .../src/landing/components/LandingVideo.tsx | 4 +- skills-lock.json | 30 +-- 9 files changed, 222 insertions(+), 193 deletions(-) diff --git a/.agents/skills/emil-design-eng/SKILL.md b/.agents/skills/emil-design-eng/SKILL.md index 491123532..d79f6d64b 100644 --- a/.agents/skills/emil-design-eng/SKILL.md +++ b/.agents/skills/emil-design-eng/SKILL.md @@ -39,12 +39,12 @@ People select tools based on the overall experience, not just functionality. Goo When reviewing UI code, you MUST use a markdown table with Before/After columns. Do NOT use a list with "Before:" and "After:" on separate lines. Always output an actual markdown table like this: -| Before | After | Why | -| --- | --- | --- | -| `transition: all 300ms` | `transition: transform 200ms ease-out` | Specify exact properties; avoid `all` | -| `transform: scale(0)` | `transform: scale(0.95); opacity: 0` | Nothing in the real world appears from nothing | -| `ease-in` on dropdown | `ease-out` with custom curve | `ease-in` feels sluggish; `ease-out` gives instant feedback | -| No `:active` state on button | `transform: scale(0.97)` on `:active` | Buttons must feel responsive to press | +| Before | After | Why | +| ------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `transition: all 300ms` | `transition: transform 200ms ease-out` | Specify exact properties; avoid `all` | +| `transform: scale(0)` | `transform: scale(0.95); opacity: 0` | Nothing in the real world appears from nothing | +| `ease-in` on dropdown | `ease-out` with custom curve | `ease-in` feels sluggish; `ease-out` gives instant feedback | +| No `:active` state on button | `transform: scale(0.97)` on `:active` | Buttons must feel responsive to press | | `transform-origin: center` on popover | `transform-origin: var(--radix-popover-content-transform-origin)` | Popovers should scale from their trigger (not modals — modals stay centered) | Wrong format (never do this): @@ -95,15 +95,15 @@ If the purpose is just "it looks cool" and the user will see it often, don't ani ### 3. What easing should it use? Is the element entering or exiting? - Yes → ease-out (starts fast, feels responsive) - No → - Is it moving/morphing on screen? - Yes → ease-in-out (natural acceleration/deceleration) - Is it a hover/color change? - Yes → ease - Is it constant motion (marquee, progress bar)? - Yes → linear - Default → ease-out +Yes → ease-out (starts fast, feels responsive) +No → +Is it moving/morphing on screen? +Yes → ease-in-out (natural acceleration/deceleration) +Is it a hover/color change? +Yes → ease +Is it constant motion (marquee, progress bar)? +Yes → linear +Default → ease-out **Critical: use custom easing curves.** The built-in CSS easings are too weak. They lack the punch that makes animations feel intentional. @@ -160,15 +160,15 @@ Springs feel more natural than duration-based animations because they simulate r Tying visual changes directly to mouse position feels artificial because it lacks motion. Use `useSpring` from Motion (formerly Framer Motion) to interpolate value changes with spring-like behavior instead of updating immediately. ```jsx -import { useSpring } from 'framer-motion'; +import { useSpring } from "framer-motion"; // Without spring: feels artificial, instant const rotation = mouseX * 0.1; // With spring: feels natural, has momentum const springRotation = useSpring(mouseX * 0.1, { - stiffness: 100, - damping: 10, + stiffness: 100, + damping: 10, }); ``` @@ -202,11 +202,11 @@ Add `transform: scale(0.97)` on `:active`. This gives instant feedback, making t ```css .button { - transition: transform 160ms ease-out; + transition: transform 160ms ease-out; } .button:active { - transform: scale(0.97); + transform: scale(0.97); } ``` @@ -221,13 +221,13 @@ Start from `scale(0.9)` or higher, combined with opacity. Even a barely-visible ```css /* Bad */ .entering { - transform: scale(0); + transform: scale(0); } /* Good */ .entering { - transform: scale(0.95); - opacity: 0; + transform: scale(0.95); + opacity: 0; } ``` @@ -238,12 +238,12 @@ Popovers should scale in from their trigger, not from center. The default `trans ```css /* Radix UI */ .popover { - transform-origin: var(--radix-popover-content-transform-origin); + transform-origin: var(--radix-popover-content-transform-origin); } /* Base UI */ .popover { - transform-origin: var(--transform-origin); + transform-origin: var(--transform-origin); } ``` @@ -255,19 +255,21 @@ Tooltips should delay before appearing to prevent accidental activation. But onc ```css .tooltip { - transition: transform 125ms ease-out, opacity 125ms ease-out; - transform-origin: var(--transform-origin); + transition: + transform 125ms ease-out, + opacity 125ms ease-out; + transform-origin: var(--transform-origin); } .tooltip[data-starting-style], .tooltip[data-ending-style] { - opacity: 0; - transform: scale(0.97); + opacity: 0; + transform: scale(0.97); } /* Skip animation on subsequent tooltips */ .tooltip[data-instant] { - transition-duration: 0ms; + transition-duration: 0ms; } ``` @@ -278,17 +280,17 @@ CSS transitions can be interrupted and retargeted mid-animation. Keyframes resta ```css /* Interruptible - good for UI */ .toast { - transition: transform 400ms ease; + transition: transform 400ms ease; } /* Not interruptible - avoid for dynamic UI */ @keyframes slideIn { - from { - transform: translateY(100%); - } - to { - transform: translateY(0); - } + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } } ``` @@ -302,20 +304,22 @@ Combine blur with scale-on-press (`scale(0.97)`) for a polished button state tra ```css .button { - transition: transform 160ms ease-out; + transition: transform 160ms ease-out; } .button:active { - transform: scale(0.97); + transform: scale(0.97); } .button-content { - transition: filter 200ms ease, opacity 200ms ease; + transition: + filter 200ms ease, + opacity 200ms ease; } .button-content.transitioning { - filter: blur(2px); - opacity: 0.7; + filter: blur(2px); + opacity: 0.7; } ``` @@ -327,14 +331,16 @@ The modern CSS way to animate element entry without JavaScript: ```css .toast { - opacity: 1; - transform: translateY(0); - transition: opacity 400ms ease, transform 400ms ease; + opacity: 1; + transform: translateY(0); + transition: + opacity 400ms ease, + transform 400ms ease; - @starting-style { - opacity: 0; - transform: translateY(100%); - } + @starting-style { + opacity: 0; + transform: translateY(100%); + } } ``` @@ -343,7 +349,7 @@ This replaces the common React pattern of using `useEffect` to set `mounted: tru ```jsx // Legacy pattern (still works everywhere) useEffect(() => { - setMounted(true); + setMounted(true); }, []); //
``` @@ -357,12 +363,12 @@ Percentage values in `translate()` are relative to the element's own size. Use ` ```css /* Works regardless of drawer height */ .drawer-hidden { - transform: translateY(100%); + transform: translateY(100%); } /* Works regardless of toast height */ .toast-enter { - transform: translateY(-100%); + transform: translateY(-100%); } ``` @@ -378,16 +384,16 @@ Unlike `width`/`height`, `scale()` also scales an element's children. When scali ```css .wrapper { - transform-style: preserve-3d; + transform-style: preserve-3d; } @keyframes orbit { - from { - transform: translate(-50%, -50%) rotateY(0deg) translateZ(72px) rotateY(360deg); - } - to { - transform: translate(-50%, -50%) rotateY(360deg) translateZ(72px) rotateY(0deg); - } + from { + transform: translate(-50%, -50%) rotateY(0deg) translateZ(72px) rotateY(360deg); + } + to { + transform: translate(-50%, -50%) rotateY(360deg) translateZ(72px) rotateY(0deg); + } } ``` @@ -406,22 +412,22 @@ Every element has an anchor point from which transforms execute. The default is ```css /* Fully hidden from right */ .hidden { - clip-path: inset(0 100% 0 0); + clip-path: inset(0 100% 0 0); } /* Fully visible */ .visible { - clip-path: inset(0 0 0 0); + clip-path: inset(0 0 0 0); } /* Reveal from left to right */ .overlay { - clip-path: inset(0 100% 0 0); - transition: clip-path 200ms ease-out; + clip-path: inset(0 100% 0 0); + transition: clip-path 200ms ease-out; } .button:active .overlay { - clip-path: inset(0 0 0 0); - transition: clip-path 2s linear; + clip-path: inset(0 0 0 0); + transition: clip-path 2s linear; } ``` @@ -452,7 +458,7 @@ const timeTaken = new Date().getTime() - dragStartTime.current.getTime(); const velocity = Math.abs(swipeAmount) / timeTaken; if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { - dismiss(); + dismiss(); } ``` @@ -470,8 +476,8 @@ Ignore additional touch points after the initial drag begins. Without this, swit ```js function onPress() { - if (isDragging) return; - // Start drag... + if (isDragging) return; + // Start drag... } ``` @@ -491,7 +497,7 @@ Changing a CSS variable on a parent recalculates styles for all children. In a d ```js // Bad: triggers recalc on all children -element.style.setProperty('--swipe-amount', `${distance}px`); +element.style.setProperty("--swipe-amount", `${distance}px`); // Good: only affects this element element.style.transform = `translateY(${distance}px)`; @@ -520,10 +526,10 @@ CSS animations run off the main thread. When the browser is busy loading a new p The Web Animations API gives you JavaScript control with CSS performance. Hardware-accelerated, interruptible, and no library needed. ```js -element.animate([{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }], { - duration: 1000, - fill: 'forwards', - easing: 'cubic-bezier(0.77, 0, 0.175, 1)', +element.animate([{ clipPath: "inset(0 0 100% 0)" }, { clipPath: "inset(0 0 0 0)" }], { + duration: 1000, + fill: "forwards", + easing: "cubic-bezier(0.77, 0, 0.175, 1)", }); ``` @@ -535,25 +541,25 @@ Animations can cause motion sickness. Reduced motion means fewer and gentler ani ```css @media (prefers-reduced-motion: reduce) { - .element { - animation: fade 0.2s ease; - /* No transform-based motion */ - } + .element { + animation: fade 0.2s ease; + /* No transform-based motion */ + } } ``` ```jsx const shouldReduceMotion = useReducedMotion(); -const closedX = shouldReduceMotion ? 0 : '-100%'; +const closedX = shouldReduceMotion ? 0 : "-100%"; ``` ### Touch device hover states ```css @media (hover: hover) and (pointer: fine) { - .element:hover { - transform: scale(1.05); - } + .element:hover { + transform: scale(1.05); + } } ``` @@ -596,12 +602,12 @@ Pressing should be slow when it needs to be deliberate (hold-to-delete: 2s linea ```css /* Release: fast */ .overlay { - transition: clip-path 200ms ease-out; + transition: clip-path 200ms ease-out; } /* Press: slow and deliberate */ .button:active .overlay { - transition: clip-path 2s linear; + transition: clip-path 2s linear; } ``` @@ -611,29 +617,29 @@ When multiple elements enter together, stagger their appearance. Each element an ```css .item { - opacity: 0; - transform: translateY(8px); - animation: fadeIn 300ms ease-out forwards; + opacity: 0; + transform: translateY(8px); + animation: fadeIn 300ms ease-out forwards; } .item:nth-child(1) { - animation-delay: 0ms; + animation-delay: 0ms; } .item:nth-child(2) { - animation-delay: 50ms; + animation-delay: 50ms; } .item:nth-child(3) { - animation-delay: 100ms; + animation-delay: 100ms; } .item:nth-child(4) { - animation-delay: 150ms; + animation-delay: 150ms; } @keyframes fadeIn { - to { - opacity: 1; - transform: translateY(0); - } + to { + opacity: 1; + transform: translateY(0); + } } ``` @@ -664,16 +670,16 @@ For touch interactions (drawers, swipe gestures), test on physical devices. Conn When reviewing UI code, check for: -| Issue | Fix | -| ------------------------------------------ | ---------------------------------------------------------------- | -| `transition: all` | Specify exact properties: `transition: transform 200ms ease-out` | -| `scale(0)` entry animation | Start from `scale(0.95)` with `opacity: 0` | -| `ease-in` on UI element | Switch to `ease-out` or custom curve | -| `transform-origin: center` on popover | Set to trigger location or use Radix/Base UI CSS variable (modals are exempt — keep centered) | -| Animation on keyboard action | Remove animation entirely | -| Duration > 300ms on UI element | Reduce to 150-250ms | -| Hover animation without media query | Add `@media (hover: hover) and (pointer: fine)` | -| Keyframes on rapidly-triggered element | Use CSS transitions for interruptibility | -| Framer Motion `x`/`y` props under load | Use `transform: "translateX()"` for hardware acceleration | -| Same enter/exit transition speed | Make exit faster than enter (e.g., enter 2s, exit 200ms) | -| Elements all appear at once | Add stagger delay (30-80ms between items) | +| Issue | Fix | +| -------------------------------------- | --------------------------------------------------------------------------------------------- | +| `transition: all` | Specify exact properties: `transition: transform 200ms ease-out` | +| `scale(0)` entry animation | Start from `scale(0.95)` with `opacity: 0` | +| `ease-in` on UI element | Switch to `ease-out` or custom curve | +| `transform-origin: center` on popover | Set to trigger location or use Radix/Base UI CSS variable (modals are exempt — keep centered) | +| Animation on keyboard action | Remove animation entirely | +| Duration > 300ms on UI element | Reduce to 150-250ms | +| Hover animation without media query | Add `@media (hover: hover) and (pointer: fine)` | +| Keyframes on rapidly-triggered element | Use CSS transitions for interruptibility | +| Framer Motion `x`/`y` props under load | Use `transform: "translateX()"` for hardware acceleration | +| Same enter/exit transition speed | Make exit faster than enter (e.g., enter 2s, exit 200ms) | +| Elements all appear at once | Add stagger delay (30-80ms between items) | diff --git a/.agents/skills/review-animations/SKILL.md b/.agents/skills/review-animations/SKILL.md index 6b46fd332..6b1c1074c 100644 --- a/.agents/skills/review-animations/SKILL.md +++ b/.agents/skills/review-animations/SKILL.md @@ -12,7 +12,7 @@ A specialized review skill. It does ONE thing: review animation and motion code You are a senior motion-design reviewer with a brutal eye for craft. Your bias is toward **motion that feels right**, not motion that merely runs. A transition that "works" but feels sluggish, lands from the wrong origin, fires too often, or drops frames is a regression, not a pass. Default to flagging. Approval is earned, not assumed. -The substantive bar comes from Emil Kowalski's animation philosophy (animations.dev). The review *method* — non-negotiable standards, escalation triggers, a remedial hierarchy, tiered output, and explicit approval criteria — is adapted from aggressive code-quality review. +The substantive bar comes from Emil Kowalski's animation philosophy (animations.dev). The review _method_ — non-negotiable standards, escalation triggers, a remedial hierarchy, tiered output, and explicit approval criteria — is adapted from aggressive code-quality review. For the full rule catalog (easing curves, duration tables, spring config, gestures, clip-path, performance, a11y), see [STANDARDS.md](STANDARDS.md). Load it whenever a finding needs a precise value or citation. @@ -81,12 +81,12 @@ Two parts, in this order. A single markdown table. One row per issue. Never a "Before:/After:" list. -| Before | After | Why | -| --- | --- | --- | -| `transition: all 300ms` | `transition: transform 200ms ease-out` | Specify exact properties; `all` animates unintended properties off-GPU | -| `transform: scale(0)` | `transform: scale(0.95); opacity: 0` | Nothing appears from nothing — `scale(0)` looks like it came from nowhere | -| `ease-in` on dropdown | `ease-out` + custom curve | `ease-in` delays the moment the user watches most; feels sluggish | -| `transform-origin: center` on popover | `var(--radix-popover-content-transform-origin)` | Popovers scale from their trigger, not center (modals are exempt) | +| Before | After | Why | +| ------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------- | +| `transition: all 300ms` | `transition: transform 200ms ease-out` | Specify exact properties; `all` animates unintended properties off-GPU | +| `transform: scale(0)` | `transform: scale(0.95); opacity: 0` | Nothing appears from nothing — `scale(0)` looks like it came from nowhere | +| `ease-in` on dropdown | `ease-out` + custom curve | `ease-in` delays the moment the user watches most; feels sluggish | +| `transform-origin: center` on popover | `var(--radix-popover-content-transform-origin)` | Popovers scale from their trigger, not center (modals are exempt) | ### Part 2 — Verdict (REQUIRED) diff --git a/.agents/skills/review-animations/STANDARDS.md b/.agents/skills/review-animations/STANDARDS.md index b6dc9b19a..ee2327796 100644 --- a/.agents/skills/review-animations/STANDARDS.md +++ b/.agents/skills/review-animations/STANDARDS.md @@ -4,12 +4,12 @@ The precise values, curves, and rules behind the review. Cite these in findings ## Should it animate? (frequency table) -| Frequency | Decision | -| --- | --- | -| 100+ times/day (keyboard shortcuts, command palette toggle) | No animation. Ever. | -| Tens of times/day (hover effects, list navigation) | Remove or drastically reduce | -| Occasional (modals, drawers, toasts) | Standard animation | -| Rare / first-time (onboarding, feedback, celebrations) | Can add delight | +| Frequency | Decision | +| ----------------------------------------------------------- | ---------------------------- | +| 100+ times/day (keyboard shortcuts, command palette toggle) | No animation. Ever. | +| Tens of times/day (hover effects, list navigation) | Remove or drastically reduce | +| Occasional (modals, drawers, toasts) | Standard animation | +| Rare / first-time (onboarding, feedback, celebrations) | Can add delight | **Never animate keyboard-initiated actions** — they repeat hundreds of times daily; animation makes them feel slow and disconnected. (Raycast has no open/close animation — correct for something used hundreds of times a day.) @@ -18,33 +18,34 @@ Valid purposes for motion: spatial consistency, state indication, explanation, f ## Easing Decision order: + - Entering or exiting → **`ease-out`** (starts fast, feels responsive) - Moving / morphing on screen → **`ease-in-out`** - Hover / color change → **`ease`** - Constant motion (marquee, progress) → **`linear`** - Default → **`ease-out`** -**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching. `ease-out` at 200ms *feels* faster than `ease-in` at 200ms. +**Never `ease-in` on UI.** It starts slow, delaying the exact moment the user is watching. `ease-out` at 200ms _feels_ faster than `ease-in` at 200ms. Built-in CSS easings are too weak. Use strong custom curves: ```css ---ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */ ---ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */ ---ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */ +--ease-out: cubic-bezier(0.23, 1, 0.32, 1); /* strong ease-out for UI */ +--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1); /* strong ease-in-out for on-screen movement */ +--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1); /* iOS-like drawer curve (Ionic) */ ``` Find curves at [easing.dev](https://easing.dev/) or [easings.co](https://easings.co/) — don't hand-roll from scratch. ## Duration -| Element | Duration | -| --- | --- | -| Button press feedback | 100–160ms | -| Tooltips, small popovers | 125–200ms | -| Dropdowns, selects | 150–250ms | -| Modals, drawers | 200–500ms | -| Marketing / explanatory | Can be longer | +| Element | Duration | +| ------------------------ | ------------- | +| Button press feedback | 100–160ms | +| Tooltips, small popovers | 125–200ms | +| Dropdowns, selects | 150–250ms | +| Modals, drawers | 200–500ms | +| Marketing / explanatory | Can be longer | **Rule: UI animations stay under 300ms.** A 180ms dropdown feels more responsive than a 400ms one. Faster spinners make load feel faster (same actual time). Instant tooltips after the first (skip delay + animation) make a toolbar feel faster. @@ -53,8 +54,12 @@ Find curves at [easing.dev](https://easing.dev/) or [easings.co](https://easings - **Never `scale(0)`.** Start from `scale(0.9–0.97)` + `opacity: 0`. Nothing in the real world appears from nothing. - **Origin-aware popovers.** Scale from the trigger, not center: ```css - .popover { transform-origin: var(--radix-popover-content-transform-origin); } /* Radix */ - .popover { transform-origin: var(--transform-origin); } /* Base UI */ + .popover { + transform-origin: var(--radix-popover-content-transform-origin); + } /* Radix */ + .popover { + transform-origin: var(--transform-origin); + } /* Base UI */ ``` **Modals are exempt** — they appear centered in the viewport, keep `transform-origin: center`. - **Button press feedback.** `transform: scale(0.97)` on `:active`, `transition: transform 160ms ease-out`. Subtle (0.95–0.98). Applies to any pressable element. @@ -81,19 +86,34 @@ CSS **transitions** can be interrupted and retargeted mid-animation; **keyframes ```css /* Interruptible — good for dynamic UI */ -.toast { transition: transform 400ms ease; } +.toast { + transition: transform 400ms ease; +} /* Not interruptible — avoid for dynamic UI */ -@keyframes slideIn { from { transform: translateY(100%); } to { transform: translateY(0); } } +@keyframes slideIn { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} ``` Use `@starting-style` for entry without JS: ```css .toast { - opacity: 1; transform: translateY(0); - transition: opacity 400ms ease, transform 400ms ease; - @starting-style { opacity: 0; transform: translateY(100%); } + opacity: 1; + transform: translateY(0); + transition: + opacity 400ms ease, + transform 400ms ease; + @starting-style { + opacity: 0; + transform: translateY(100%); + } } ``` @@ -104,8 +124,12 @@ Legacy fallback: `useEffect(() => setMounted(true), [])` + `data-mounted` attrib Slow where the user is deciding, fast where the system responds. ```css -.overlay { transition: clip-path 200ms ease-out; } /* release: fast */ -.button:active .overlay { transition: clip-path 2s linear; } /* press: slow, deliberate */ +.overlay { + transition: clip-path 200ms ease-out; +} /* release: fast */ +.button:active .overlay { + transition: clip-path 2s linear; +} /* press: slow, deliberate */ ``` ## Performance @@ -113,8 +137,8 @@ Slow where the user is deciding, fast where the system responds. - **Only animate `transform` and `opacity`** — they skip layout/paint and run on the GPU. `padding`/`margin`/`height`/`width`/`top`/`left` trigger all three rendering steps. - **Don't drive child transforms via a CSS variable on the parent** — it recalcs styles for all children. Set `transform` directly on the element. ```js - element.style.setProperty('--swipe-amount', `${d}px`); // bad: recalc on all children - element.style.transform = `translateY(${d}px)`; // good: only this element + element.style.setProperty("--swipe-amount", `${d}px`); // bad: recalc on all children + element.style.transform = `translateY(${d}px)`; // good: only this element ``` - **Framer Motion shorthands are NOT hardware-accelerated.** `x`/`y`/`scale` run on the main thread via rAF and drop frames under load. Use the full transform string: ```jsx @@ -124,8 +148,11 @@ Slow where the user is deciding, fast where the system responds. - **CSS animations beat JS under load** — they run off the main thread; rAF-based animations stutter while the browser loads/scripts/paints. Use CSS for predetermined motion, JS for dynamic/interruptible. - **WAAPI** gives JS control with CSS performance (hardware-accelerated, interruptible, no library): ```js - element.animate([{ clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0 0)' }], - { duration: 1000, fill: 'forwards', easing: 'cubic-bezier(0.77, 0, 0.175, 1)' }); + element.animate([{ clipPath: "inset(0 0 100% 0)" }, { clipPath: "inset(0 0 0 0)" }], { + duration: 1000, + fill: "forwards", + easing: "cubic-bezier(0.77, 0, 0.175, 1)", + }); ``` ## Transforms & clip-path @@ -152,26 +179,43 @@ When a crossfade shows two overlapping states despite tuning easing/duration, ad Stagger group entrances; 30–80ms between items. Longer delays feel slow. Stagger is decorative — never block interaction while it plays. ```css -.item { opacity: 0; transform: translateY(8px); animation: fadeIn 300ms ease-out forwards; } -.item:nth-child(2) { animation-delay: 50ms; } -.item:nth-child(3) { animation-delay: 100ms; } -@keyframes fadeIn { to { opacity: 1; transform: translateY(0); } } +.item { + opacity: 0; + transform: translateY(8px); + animation: fadeIn 300ms ease-out forwards; +} +.item:nth-child(2) { + animation-delay: 50ms; +} +.item:nth-child(3) { + animation-delay: 100ms; +} +@keyframes fadeIn { + to { + opacity: 1; + transform: translateY(0); + } +} ``` ## Accessibility ```css @media (prefers-reduced-motion: reduce) { - .element { animation: fade 0.2s ease; } /* keep opacity/color, drop transform-based motion */ + .element { + animation: fade 0.2s ease; + } /* keep opacity/color, drop transform-based motion */ } @media (hover: hover) and (pointer: fine) { - .element:hover { transform: scale(1.05); } /* gate hover motion — touch fires false hovers on tap */ + .element:hover { + transform: scale(1.05); + } /* gate hover motion — touch fires false hovers on tap */ } ``` ```jsx const reduce = useReducedMotion(); -const closedX = reduce ? 0 : '-100%'; +const closedX = reduce ? 0 : "-100%"; ``` Reduced motion means fewer and gentler animations, not zero — keep transitions that aid comprehension, remove movement/position changes. diff --git a/frontend/src/landing/components/LandingAgentsBar.tsx b/frontend/src/landing/components/LandingAgentsBar.tsx index cbf88ed77..33c796991 100644 --- a/frontend/src/landing/components/LandingAgentsBar.tsx +++ b/frontend/src/landing/components/LandingAgentsBar.tsx @@ -58,12 +58,7 @@ export function LandingAgentsBar() { className="group flex h-[82px] w-[112px] shrink-0 flex-col items-center justify-end gap-2 px-2 py-2" >
- +
{agent.name} diff --git a/frontend/src/landing/components/LandingFeatures.tsx b/frontend/src/landing/components/LandingFeatures.tsx index 946539d8f..47eb0d54c 100644 --- a/frontend/src/landing/components/LandingFeatures.tsx +++ b/frontend/src/landing/components/LandingFeatures.tsx @@ -198,9 +198,7 @@ export function LandingFeatures() {
What's inside

Run the agent you already use. - - AO wraps the workflow around it. - + AO wraps the workflow around it.

@@ -384,7 +382,6 @@ function AgentHarnessDemo({
- @@ -423,9 +420,7 @@ function AgentSelectLabel({ function AgentLogo({ agent, className }: { agent: AgentHarness; className: string }) { if (!agent.logo) { return ( -
+
{agent.name.slice(0, 1)}
); @@ -604,7 +599,6 @@ function WorkspaceIsolationDemo({
- @@ -773,7 +767,6 @@ function FeedbackRoutingDemo({ - @@ -817,7 +810,6 @@ function DaemonControlDemo() { ))} -