Free
🤖 AI

Logoipsum

Hero over a looping film whose loop seam is hand-rolled: a requestAnimationFrame fade system cross-fades the video out 0.55s before the end and back in after a manual restart, under a glass prompt box.

1.8k views 152 copies 1.6k palavras

O prompt

# Logoipsum — Hand-Rolled Fade Video Hero

Build a modern hero section over a looping video background. The loop seam is not the browser's — a custom `requestAnimationFrame` fade system takes the video out before it ends and brings it back after a manual restart. No CSS transitions anywhere in that path.

Reproduce this exactly: same copy, same hex values, same pixel numbers, same timings.

## Video background

```
https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260329_050842_be71947f-f16e-4a14-810c-06e83d23ddb5.mp4
```

- Sized **115%** in both width and height
- Centred horizontally, anchored to the top, focal point `object-position: top`, `object-fit: cover`

The clip is a slow drift over green rolling hills with a footpath winding through them; the top third is a bright overcast sky, the lower two thirds dark green.

**Custom JavaScript fade system (NO CSS transitions):**

1. 250ms `requestAnimationFrame` fade-in on load and on each loop start
2. 250ms fade-out when **0.55 seconds** remain before the end
3. A `fadingOutRef` boolean prevents the repeated `timeupdate` events from re-triggering the fade-out
4. On `ended`: set opacity to 0, wait **100ms**, reset `currentTime = 0`, `play()`, fade back in
5. Every new fade cancels the running animation frames so two fades never compete
6. Fades resume from the current opacity — no snapping

## Fonts

Schibsted Grotesk, Inter, Noto Sans and Fustat, each at 400, 500, 600, 700.

## Colours

| Role | Value |
|------|-------|
| Black text | `#000000` |
| Gray text | `#505050` |
| Light gray backgrounds | `#f8f8f8` |
| Green upgrade button | `rgba(90,225,76,0.89)` |
| Dark badge | `#0e1311` |
| White | `#ffffff` |
| Transparent overlay | `rgba(0,0,0,0.24)` |

## Navigation

Padding `16px 120px`.

- Logo **Logoipsum** — Schibsted Grotesk SemiBold, 24px, `-1.44px` tracking
- Menu — Schibsted Grotesk Medium, 16px, `-0.2px` tracking: **Platform**, **Features** (with a chevron-down), **Projects**, **Community**, **Contact**
- Right — **Sign Up** (transparent, 82px wide) and **Log In** (black background, white text, 101px wide)

## Hero content

Pulled up 50px with `-mt-[50px]`. Gaps: 60px between nav and hero, 44px between the header block and the search box, 34px inside the header (badge → title, title → subtitle). Horizontal padding 120px.

**Badge** — a dark `#0e1311` chip with a star icon and **New**, beside light text **Discover what's possible**. Inter Regular 14px, rounded, subtle shadow.

**Headline** — **Transform Data Quickly**, Fustat Bold, 80px, `-4.8px` tracking, `line-height: none`, black, centred.

**Subtitle** — *Upload your information and get powerful insights right away. Work smarter and achieve goals effortlessly.* Fustat Medium, 20px, `-0.4px` tracking, `#505050`, `max-width: 736px`, `width: 542px`.

## Search input box

`max-width: 728px`, `height: 200px`, `border-radius: 18px`, backdrop blur over `rgba(0,0,0,0.24)`.

**Top row** — left: **60/450 credits** with a green **Upgrade** button; right: an AI sparkle icon and **Powered by GPT-4o**. Schibsted Grotesk Medium 12px, white.

**Main input** — white, `border-radius: 12px`, shadow. Placeholder **Type question...** at 16px, `rgba(0,0,0,0.6)`. A black circular submit button, 36px, with an up-arrow icon.

**Bottom row** — left: three gray `border-radius: 6px` action buttons — **Attach** (paperclip), **Voice** (microphone), **Prompts** (search). Right: a character counter **0/3,000**, 12px, gray.

**Icons** (SVG paths from an imported file): chevron-down, up-arrow, star, AI sparkle, paperclip, microphone, search.

## Component structure

`VideoBackground` with the custom fade logic; a navigation bar; a centred hero container with nested badge, header and search-input components. Everything positioned over the full-screen video.

## Quality Bar

- **Nothing here plays, because the spec never says `muted`.** The source carries a 128 kbps stereo AAC track, and a video with audio is blocked from autoplaying in every current browser. `play()` rejects, `ended` never fires, the restart never happens — and since the element is deliberately initialised at opacity 0 and only a successful `play()` triggers the fade-in, the result is not "a static first frame" but a completely invisible background. The spec also never mentions `autoplay`, `playsInline` or `preload`. Add `muted playsInline`, and strip the audio track from a background clip that can never be heard anyway.
- **`width: 115%` is silently clamped to 100% by Tailwind Preflight.** Preflight ships `img, video { max-width: 100%; height: auto }`, so the horizontal overscan never happens while the vertical one does. Measured at 1440×900: the video box came out **1440×1035** instead of the intended **1656×1035** — 15% of overscan below and none at the sides, which quietly breaks the "centred horizontally" framing the design is built on. `max-width: none` on the element fixes it.
- **The loop seam is dark for 900ms out of every 11.04s.** Working from the clip's real duration: fade-out begins at 0.55s remaining and finishes at 0.30s remaining; the video then plays on *invisibly* for 300ms; `ended` fires; 100ms of dead time; then a 250ms fade-in. That is **0.900s of degraded background per loop — 8.2% of it — of which 0.400s (3.6%) is fully transparent**, showing whatever is behind the video. Shorten the lead, drop the 100ms delay, or overlap the restart with the fade.
- **The 0.55s lead is exactly one `timeupdate` tick of slack, and no more.** The trigger can only fire as often as `timeupdate` does. Measured over 120 events: median gap **265ms**, min 109ms, max **275ms**. So in the worst case the first qualifying tick lands at ~275ms remaining, and the 250ms fade finishes with **25ms** to spare. It fits — but the entire margin is 25ms, and any browser with a coarser cadence, or any fade longer than 275ms, clips the fade against the end of the video. Drive the fade off the rAF loop you already have and compare `currentTime` there, instead of sampling on `timeupdate`.
- **Insisting on "NO CSS transitions" is what makes the background able to vanish.** Because the fade is rAF-driven, a stalled frame loop strands the element wherever the last frame left it — and `ended` sets opacity to **0** *before* the fade-in is scheduled, so the stranded value is the invisible one. Observed directly: with the compositor idle across a loop boundary, the video went on playing at a computed opacity of `0`. A CSS transition cannot do this; it is driven by the compositor's own timeline and always reaches its end value. If the hand-rolled fade stays, at least fade *up* from the current opacity on `visibilitychange`, and never park the element at 0 without a guaranteed way back.
- **The subtitle is the one piece of text that fails, and it fails everywhere.** Measured against all 265 frames at 1440×900: `#505050` at 20px has a worst case of **1.30:1** with **32.12% of its band below 4.5:1**, failing in **265 of 265 frames**. It is the only element that sits across the horizon, where the bright sky meets the dark hills, and a mid-grey has no safe side there. Everything else is comfortable — the logo measures **18.26:1**, the menu **17.74:1**, and the 80px headline is effectively clean (worst 2.74:1, but 0.00% of its pixels fail). Darken the subtitle, or move it out of the horizon band.
- **The glass box is doing real work and is still marginal in two places.** With `rgba(0,0,0,0.24)` over the footage, `Powered by GPT-4o` measures a clean **6.55:1**, but the 12px credits line bottoms out at **3.68:1** (0.49% of pixels) and the `0/3,000` counter at `white/0.7` bottoms out at **4.00:1** (0.31%). Both are small areas, but both are 12px text under a 4.5:1 requirement — deepen the overlay slightly or drop the 0.7 alpha on the counter.
- **Noto Sans is loaded and never used.** Four families at four weights is 16 combinations; the design only ever renders five — Schibsted Grotesk 500 and 600, Inter 400, Fustat 500 and 700. Noto Sans is not referenced once anywhere in the spec. Google's four latin slices total **155,736 bytes** (Schibsted 46,864, Inter 48,432, Noto Sans 35,856, Fustat 24,584); self-hosted, subset to the characters this page actually sets and with the variable axes pinned to the weights it actually uses, the three real families come to **13,960 bytes — 9.0%**.
- **`width: 542px` with `max-width: 736px` is a declaration that can never apply.** `max-width` only binds when the used width would exceed it, and 542 never will. One of the two numbers is the one you meant; the other is noise. (Confirmed: the subtitle renders at exactly 542px.)
- **Every control has a hover state and none has a focus state.** Thirteen focusable controls sit over moving footage with no `:focus-visible` rule anywhere in the spec.
- **There is no `prefers-reduced-motion` branch.** A full-bleed video looping forever with a hand-rolled cross-fade is exactly what that setting asks you to stop. Hold a single frame — and note that the reduced-motion path must land at opacity 1, not at the 0 the element starts from.
- **`fadingOutRef` is only ever reset inside the restart timer.** Any path that skips `ended` — a seek, a decode error, a source swap — leaves the flag stuck `true` and no further fade-out will ever fire for the life of the component. Reset it wherever playback restarts, not only in the happy path.
- **There is no `poster`, and the element starts at opacity 0.** First paint is whatever is behind the video, for as long as the clip takes to reach `loadeddata` plus the 250ms fade. A `poster` costs one small image and removes the blank entirely.
- **The search box is furniture, not a form.** There is no `<form>`, the 36px submit button has no handler, the counter is specified as a literal `0/3,000` rather than being bound to the input, and no `maxLength` is given for the 3,000 the counter implies. The input also has no label — a placeholder is not one.
- **The nav buttons are pinned to 82px and 101px.** Those widths fit `Sign Up` and `Log In` in Schibsted Grotesk Medium at 16px and nothing else — a fallback font, a longer label or a translation will clip. Let them size to their content with padding.

Você também pode gostar

Verified DR - Verified Domain Rating for buildpromptkit.comBuildPromptKit - AI prompts that ship real UI — every one has a live demo | Product HuntBuildPromptKit — AI Website & App Prompts - Featured on YizhanFeatured on saasfame.comFeatured on Twelve Tools