HyperFrames

The restrained brand, rendered in HTML-native video. HyperFrames compositions are plain HTML with data-* timing attributes, animated with GSAP, and rendered to MP4. This page applies the white, graphite, and monochrome system to that workflow.

When to reach for it

Motion covers the brand's motion canon regardless of tool. HyperFrames and Remotion are both canonical — they solve different shapes of problem.

HyperFrames

HTML-first, agent-first

  • Prompting a video end-to-end with an agent
  • Reskinning an existing website's look into motion
  • Short promos, About videos, narrative launch films
  • When General Sans + CSS + GSAP is the whole story

Remotion

React-native, programmatic

  • Complex data-driven compositions (charts, lists)
  • Props-driven templates rendered at scale
  • Fine frame-level control with interpolate / spring
  • Tight integration with a React codebase

The same rules apply to both: white canvas, neutral palette, General Sans, Manrope only for metadata, monochrome imagery, sentence case, 30 fps, and no decorative effects. See Motion for the shared canon.

Install & scaffold

Node 22 and FFmpeg 8 are prerequisites. No global install — everything runs through npx.

  1. 1

    Scaffold a project. Skills install automatically — agents will pick them up on the next session.

    # inside your workspace root
    npx hyperframes init <slug>
    cd <slug>
    
  2. 2

    Preview in the browser with live reload.

    npx hyperframes preview
  3. 3

    Lint before render. Run this after every change.

    npx hyperframes lint
  4. 4

    Render to MP4.

    npx hyperframes render -o renders/out.mp4

Composition anatomy

A composition is an HTML file. The root #stage carries composition-level attributes; every timed element carries its own timing attributes and must include class="clip" or it renders invisible. Timelines are paused GSAP instances registered on window.__timelines — the engine seeks them deterministically.

Required on stage

data-composition-id · data-width · data-height · data-start

Required on clips

data-start · data-duration · data-track-index · class="clip"

Determinism

No Date.now · no Math.random · no network fetch at render

<div id="stage"
     data-composition-id="shibin-about"
     data-start="0"
     data-width="1920"
     data-height="1080">

  <h1 id="title" class="clip hero"
      data-start="0" data-duration="4" data-track-index="1">
    I help Shopify brands grow with AI.
  </h1>
</div>

<script>
  window.__timelines = window.__timelines || {};
  window.__timelines["shibin-about"] = gsap.timeline({ paused: true });
  window.__timelines["shibin-about"]
    .from("#title", { opacity: 0, y: 4, duration: 0.3, ease: "power2.out" }, 0);
</script>

Brand tokens in CSS

Paste this block at the top of every composition. Never hardcode hex values in an index.html — always reference the token. If a composition needs a color the tokens don't provide, the system needs an update, not the composition.

:root {
  --background: #FFFFFF;
  --foreground: #27272A;
  --text-secondary: #626366;
  --text-muted: #71717A;
  --text-faint: #8E8F92;
  --accent: #18181B;
  --divider: #E4E4E7;

  --font-sans: 'Shibin General Sans', 'General Sans', system-ui, sans-serif;
  --font-meta: 'Manrope', system-ui, sans-serif;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-entrance: 0.3s;
  --dur-transition: 0.3s;
}

body {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  margin: 0;
}

Load the exact variable-normal General Sans face used on shibin.co. Use weights 400 / 500 / 600. Load Manrope separately and apply it only to dates, timestamps, breadcrumbs, and other small metadata.

@font-face {
  font-family: 'Shibin General Sans';
  src: url('https://cdn.fontshare.com/wf/LHQJ5KSAL7VGAEIDSTEXCCOIUKFLT2I6/GW57XUEG4ZBVMLZZTQZTGYPROITRRQ5W/JA3IZUEMJ2J6WWT2OQVJOAWDXO3YL4YG.woff2') format('woff2');
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
}
<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500&display=swap">

Default entrance

Start with opacity alone. When spatial movement improves comprehension, add no more than translateY(4px → 0) over 300 ms with power2.out. Exits fade in place. Stagger only elements whose reading order matters.

Duration

300ms (≈9f @ 30fps)

Easing

power2.out (enter) · power2.in (exit)

Enter

opacity 0→1 · optional translateY 4→0

Exit

opacity 1→0

// default entrance
tl.from("#title", { opacity: 0, y: 4, duration: 0.3, ease: "power2.out" }, 0);

// default exit
tl.to("#title", { opacity: 0, duration: 0.3, ease: "power2.in" }, "+=3.7");

The graphite rule

At 1080p, the web’s 40 × 3 rule can scale to 120 × 3. Reveal it over 200 ms and use it once in an opening or section divider. Do not repeat it under every heading.

.graphite-rule {
  width: 120px; height: 3px;
  background: var(--accent);
  border-radius: 2px;
  transform-origin: left center;
  transform: scaleX(0);
}

// GSAP
tl.to(".graphite-rule", { scaleX: 1, duration: 0.2, ease: "power2.out" }, 0.2);

Text action

Actions remain quiet underlined text with an up-right arrow. Fade the action in as one unit. The arrow can nudge two pixels after the text settles; no fill or enclosing pill is needed.

.text-action {
  display: inline-flex; align-items: center; gap: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--foreground);
  font-weight: 500; font-size: 20px;
  color: var(--foreground);
  background: transparent;
}

Using the catalog

HyperFrames ships 50+ blocks via npx hyperframes add. Many arrive with loud colors, effects, or decorative layouts. Before using one:

  1. Map every baked-in color to the neutral tokens above.
  2. Strip gradients, glows, and drop shadows — even subtle ones.
  3. Swap primary type to General Sans and keep Manrope to metadata.
  4. Convert pill CTAs into underlined text actions.
  5. Remove decorative grids, patterned backgrounds, and color treatments from imagery.
  6. If the block still doesn't feel quiet, build the pattern from scratch with the tokens above.

Prefer a plain cross-fade through white to a shader transition. Avoid anything labelled glitch, chromatic, or destruction; those effects compete with the content.

Rendering defaults

Container · codec

H.264 MP4, CRF 18, Rec.709

Framerate

30 fps · 60 fps only for UI capture

Formats

1920×1080 · 1080×1080 · 1080×1920

Safe area

6% of shorter edge (≈64 px on 1080)

Don't

  • × Hardcode hex in HTML — always reference the token.
  • × Skip class="clip" on timed elements.
  • × Call Date.now() or Math.random() at render time.
  • × Use a catalog block without simplifying it first.
  • × Mix framerates within a composition.
  • × Use fonts beyond General Sans and metadata-only Manrope.
  • × Use color imagery, decorative grids, gradients, or heavy shadows.