
Hyperframes Creative
- 213k installs
- 39.5k repo stars
- Updated August 5, 2026
- heygen-com/hyperframes
Hyperframes-creative is an agent skill that guides non-animation creative direction for video compositions using design systems (frame.md) and planning workflows.
About
Teaches AI agents how to apply design systems, palettes, typography, and narration planning to video compositions. Frame.md inverts web design specs for the camera. Works with multiple animation runtimes.
- Design systems for video composition - frame.md translates web design specs for camera
- Palettes, typography, narration, beat planning, audio-reactive visuals
- Works with GSAP, CSS, Lottie, Three.js, Anime.js animation runtimes
Hyperframes Creative by the numbers
- 213,143 all-time installs (skills.sh)
- +28,788 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #44 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
hyperframes-creative capabilities & compatibility
- Capabilities
- video generation · design systems · narration planning · audio design
- Use cases
- video generation
- IDEs
- vscode · cursor ide · jetbrains · zed
- Pricing
- Free
npx skills add https://github.com/heygen-com/hyperframes --skill hyperframes-creativeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 213k |
|---|---|
| repo stars | ★ 39.5k |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 5, 2026 |
| Repository | heygen-com/hyperframes ↗ |
How do you define brand style for HyperFrames videos?
Teaches AI agents how to apply design systems, palettes, typography, and narration planning to video compositions. Frame.md inverts web design specs for the camera. Works with multiple animation runt
Who is it for?
Defining brand palettes, typography systems, narration scripts, beat plans, and audio-reactive visuals for video projects
Skip if: Pure animation API questions or projects without HyperFrames composition scaffolding from hyperframes-core.
When should I use this skill?
Planning the visual design, color scheme, typography, or soundtrack timing for a video composition
What you get
frame.md or design.md creative specs with palettes, typography, narration plans, and composition patterns.
- frame.md or design.md specs
- Palette and typography decisions
- Narration and beat plans
Files
HyperFrames Creative
Brand, pacing, style, narration, and composition direction. Use after the technical contract from hyperframes-core is in place.
For motion patterns, scene blueprints, transitions, and CSS marker effects, use hyperframes-animation — this skill is intentionally non-animation.
Read these two FIRST for any non-trivial composition — they override web instincts:
>
- references/house-style.md — "interpret the prompt, generate real content," the lazy-default list, and the background/foreground layer recipe. This is what turns a literal restyle into a _concept_.- references/video-composition.md — video-medium density, scale, foreground metadata (the "produced, not generated" detailing: data bars, registration marks, monospace readouts, 8-10 elements/scene).>
Skipping these is the single biggest cause of generic, web-page-looking output. They are not optional rows in the routing table below — for anything beyond a one-line edit, open both before you choose colors or write HTML.
Workflow
1. If a project has a design spec, read it first and treat its frontmatter tokens as brand truth (colors, fonts, spacing, tone, constraints). Which file to read (precedence frame.md → design.md → DESIGN.md) and how to parse it (frontmatter = normative, prose = context) are defined once in `references/design-spec.md` — resolve and load per that doc. 2. If no design spec exists and the user asks for visual direction, choose a route:
- Ready-made frame-preset (optional) →
frame-presets/(adopt aFRAME.mdasframe.md; seereferences/design-spec.md) - Named style or mood →
references/visual-styles.md - Fast defaults →
references/house-style.md - Interactive selection →
references/design-picker.md
3. For multi-scene work, plan beats and rhythm before writing HTML → references/beat-direction.md. For scene transitions, jump to hyperframes-animation/transitions/. 4. For motion-heavy work, read references/motion-principles.md (high-level guardrails), then go to hyperframes-animation for atomic rules.
Routing
| Topic | Read |
|---|---|
Adopt a ready-made frame-preset as frame.md (optional) | frame-presets/ · references/design-spec.md |
| Default palettes, motion, typography, lazy defaults to question | references/house-style.md |
| Named style presets, mood-to-style routing | references/visual-styles.md |
| Palette-specific color tokens | palettes/*.md |
| Composition patterns — PiP, text-behind-subject, title card, slide show | references/composition-patterns.md |
| Stats / infographic presentation | references/data-in-motion.md |
| Structured expansion for open-ended prompts | references/prompt-expansion.md |
| Video-medium density, scale, color, frame composition | references/video-composition.md |
| Per-beat direction, rhythm planning, transition timing | references/beat-direction.md |
| Post-authoring spec verification (colors, type, corners, spacing, depth) | references/design-adherence.md |
| High-level motion guardrails and GSAP-quality rules | references/motion-principles.md |
| Font selection, pairings, rendered-video type guardrails | references/typography.md |
| Script pacing, tone, openings, number pronunciation | references/narration.md |
| Precomputed audio bands mapped to motion | references/audio-reactive.md |
Scripts
scripts/contrast-report.mjs— inspect contrast warnings from rendered frames.scripts/extract-audio-data.py— pre-extract audio bands for audio-reactive compositions.scripts/package-loader.mjs— support script for bundled creative tooling.
Run from the repo root with explicit paths, for example:
python skills/hyperframes-creative/scripts/extract-audio-data.py <audio-file>Animation analysis (animation-map.mjs) lives in hyperframes-animation/scripts/.
Boundaries
- Do not override
hyperframes-coretechnical rules. - Do not require a design system for a minimal technical composition.
- Do not add extra scenes, narration, music, captions, or transitions unless the request calls for them or you first propose the expansion.
- Keep recipe references task-specific; do not read every reference for simple edits.
<!--
Biennale Yellow — caption skin (preset-local source).
This is the preset's own lower-third karaoke caption look. product-launch-video's
Step 2 copies it into the project as caption-skin.html; captions.mjs then fills the
three reserved holes, wraps the fragment in a <template>, and writes
compositions/captions.html:
· the empty GROUPS array → the engine's scene-aware word groups
· the 0 duration + 0 width/height → real total + canvas size
· the empty data-brand-tokens style → :root tokens derived from the project's frame.md
Token contract — reference ONLY this fixed vocab (captions.mjs injects it from
frame.md, so the Step-2 brand overlay flows through; each var has a biennale literal
fallback for standalone preview):
--cap-ink · --cap-canvas · --cap-accent · --cap-accent-2 · --font-display ·
--font-body · --cap-band-top · --cap-band-height
Hooks (do not rename): .caption-group / .caption-word + states .is-active / .is-spoken,
root data-composition-id="captions", timeline at window.__timelines["captions"].
State changes use gsap.set({className}) (applied on the engine's frame-by-frame seek);
never tl.call() callbacks (seek does not fire them → wrong state on render).
Visual: a warm-parchment card on a single 1px hairline indigo-ink border — biennale's
ONLY border — square corners, NO shadow (biennale's depth is atmospheric, never cast).
Instrument Serif, sentence case, ink glyphs. Upcoming words sit in faint ink; the
current word gets a solar-yellow underprint (the brand's signature "sun as panel /
underprint", a hard square block, no blur) with ink reading on top; spoken words
settle to solid ink, underprint cleared.
-->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<!-- captions.mjs injects the project's :root tokens here -->
<style data-brand-tokens></style>
<style>
#captions-root {
position: absolute;
inset: 0;
pointer-events: none;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
}
/* the keep-out band, from captionBand(H) — frame content stays above it */
.caption-stage {
position: absolute;
left: 0;
right: 0;
top: var(--cap-band-top, 900px);
height: var(--cap-band-height, 180px);
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
/* every group is absolutely stacked over the SAME band — exactly one shown at a
time via opacity. Must be absolute: in-flow groups would lay out side-by-side. */
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
/* THE parchment card — single 1px hairline ink border (biennale's only border),
square corners, NO shadow */
.caption-pill {
max-width: 78%;
padding: 20px 44px 24px;
background: var(--cap-canvas, #e9e5db);
border: 1px solid var(--cap-ink, #1b2566);
border-radius: 0;
}
.caption-line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.1em 0.34em;
font-family: var(--font-display, "Instrument Serif"), Georgia, serif;
font-weight: 400;
font-size: clamp(40px, 4.4vw, 58px);
line-height: 1.16;
letter-spacing: -0.01em;
}
/* upcoming — faint indigo ink, no underprint */
.caption-word {
display: inline-block;
padding: 0 0.06em;
color: color-mix(in srgb, var(--cap-ink, #1b2566) 42%, var(--cap-canvas, #e9e5db));
}
/* current word — THE solar-yellow underprint (box-shadow spread = a hard square
block w/o layout shift; no blur), indigo ink reading on top */
.caption-word.is-active {
color: var(--cap-ink, #1b2566);
background: var(--cap-accent, #f1ee2e);
box-shadow: 0 0 0 0.06em var(--cap-accent, #f1ee2e);
}
/* already spoken — solid indigo ink, underprint cleared */
.caption-word.is-spoken {
color: var(--cap-ink, #1b2566);
background: transparent;
box-shadow: none;
}
/* optional per-word accents (dormant until a word-classer tags them) */
/* brand / key term — Instrument Serif italic, biennale's only display inflection */
.caption-word.cap-brand {
font-style: italic;
}
/* numerals / data — the mono voice, slightly recessed */
.caption-word.cap-num {
font-family: var(--font-body, "JetBrains Mono"), ui-monospace, monospace;
color: color-mix(in srgb, var(--cap-ink, #1b2566) 72%, var(--cap-canvas, #e9e5db));
}
/* emphasis — an ember-red hairline underline (the counter-temperature accent) */
.caption-word.cap-cta {
text-decoration: underline;
text-decoration-thickness: 2px;
text-decoration-color: var(--cap-accent-2, #e26b4a);
text-underline-offset: 0.1em;
}
@media (max-aspect-ratio: 9/16) {
.caption-pill {
max-width: 92%;
}
.caption-line {
font-size: clamp(34px, 5.4vw, 48px);
}
}
</style>
<div
id="captions-root"
data-composition-id="captions"
data-timeline-locked
data-start="0"
data-duration="0"
data-fps="30"
data-width="0"
data-height="0"
>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="caption-stage"></div>
</div>
</div>
<script>
// captions.mjs injects the engine's scene-aware groups + real duration here.
var GROUPS = [];
var DURATION = 0;
(function () {
var stage = document.getElementById("caption-stage");
// build DOM: one .caption-group per group, .caption-word per word
GROUPS.forEach(function (group, g) {
var groupEl = document.createElement("div");
groupEl.className = "caption-group";
groupEl.id = "caption-group-" + g;
var pill = document.createElement("div");
pill.className = "caption-pill";
var line = document.createElement("div");
line.className = "caption-line";
(group.words || []).forEach(function (w, i) {
var span = document.createElement("span");
span.className = "caption-word";
span.id = "caption-word-" + g + "-" + i;
span.textContent = String(w.text);
line.appendChild(span);
});
pill.appendChild(line);
groupEl.appendChild(pill);
stage.appendChild(groupEl);
});
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, g) {
var groupEl = document.getElementById("caption-group-" + g);
var words = group.words || [];
var next = GROUPS[g + 1];
var isLast = g === GROUPS.length - 1;
var start = Math.max(0, Number(group.start));
var end = isLast ? DURATION : Math.min(Number(next.start), Number(group.end) + 0.3);
if (end <= start) end = start + 0.01;
// group on/off — exactly one group visible at a time
tl.set(groupEl, { opacity: 1 }, start);
tl.set(groupEl, { opacity: 0 }, end);
// 3-state karaoke via className SETS — seek-safe (gsap.set applies on the
// engine's frame-by-frame seek; .call callbacks would NOT fire).
words.forEach(function (w, i) {
var el = document.getElementById("caption-word-" + g + "-" + i);
var at = Math.max(start, Number(w.start));
tl.set(el, { className: "caption-word" }, start); // upcoming (also resets on reverse-seek)
tl.set(el, { className: "caption-word is-active" }, at); // current
tl.fromTo(el, { scale: 0.97 }, { scale: 1, duration: 0.14, ease: "power2.out" }, at);
if (i + 1 < words.length) {
var nextAt = Math.max(start, Number(words[i + 1].start));
tl.set(el, { className: "caption-word is-spoken" }, nextAt); // demote when next activates
}
});
if (words.length) {
var lastEl = document.getElementById("caption-word-" + g + "-" + (words.length - 1));
var lastSpoken = Math.min(end, Number(words[words.length - 1].end) + 0.1);
tl.set(lastEl, { className: "caption-word is-spoken" }, lastSpoken);
}
});
// full-span anchor so the sub-comp timeline spans the whole video
tl.to({}, { duration: DURATION }, 0);
window.__timelines["captions"] = tl;
})();
</script>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Biennale Yellow</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Archivo:wght@400;600&family=JetBrains+Mono:wght@400&display=swap"
rel="stylesheet"
/>
<style id="ds-tokens">
:root {
--paper: #e9e5db;
--paper-deep: #dcd6c4;
--sun: #f1ee2e;
--sun-soft: #f8f39b;
--haze: #f0da7c;
--ink: #1b2566;
--ember: #e26b4a;
--serif: "Instrument Serif", Georgia, serif;
--sans: "Archivo", sans-serif;
--mono: "JetBrains Mono", ui-monospace, monospace;
--bloom: radial-gradient(
circle at 50% 50%,
rgba(241, 238, 46, 0.92) 0%,
rgba(241, 238, 46, 0.5) 30%,
rgba(240, 218, 124, 0.22) 55%,
rgba(233, 229, 219, 0) 78%
);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
background: #0c0d1a;
}
body {
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
-webkit-font-smoothing: antialiased;
}
.serif {
font-family: var(--serif);
font-weight: 400;
letter-spacing: -0.018em;
line-height: 0.9;
}
.micro {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 13px;
}
section {
padding: 120px 76px;
position: relative;
overflow: hidden;
}
.sec-head {
display: flex;
align-items: baseline;
gap: 24px;
border-bottom: 1px solid var(--ink);
padding-bottom: 18px;
margin-bottom: 56px;
position: relative;
z-index: 2;
}
.sec-head .micro {
color: var(--ink);
}
.sec-head h2 {
font-family: var(--serif);
font-weight: 400;
font-size: 56px;
line-height: 1;
letter-spacing: -0.01em;
}
.sec-head .sp {
flex: 1;
}
/* COVER */
.cover {
min-height: 100vh;
padding: 0;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden;
}
.cover .bloom {
position: absolute;
width: 62%;
height: 70%;
left: 8%;
top: 14%;
background: var(--bloom);
z-index: 0;
pointer-events: none;
}
.cover .ember {
position: absolute;
width: 36%;
height: 40%;
right: -6%;
bottom: -6%;
background: radial-gradient(
circle at 60% 60%,
rgba(226, 107, 74, 0.2) 0%,
rgba(226, 107, 74, 0) 70%
);
z-index: 0;
pointer-events: none;
}
.cover .daterail {
position: absolute;
top: 64px;
right: 76px;
font-family: var(--serif);
font-weight: 400;
font-size: clamp(48px, 5.2vw, 84px);
line-height: 0.96;
text-align: right;
letter-spacing: -0.005em;
z-index: 2;
}
.cover .inner {
padding: 0 76px;
position: relative;
z-index: 2;
}
.cover .micro {
margin-bottom: 28px;
}
.cover h1 {
font-family: var(--serif);
font-weight: 400;
font-size: clamp(110px, 15vw, 230px);
line-height: 0.84;
letter-spacing: -0.018em;
}
.cover h1 em {
font-style: italic;
letter-spacing: -0.005em;
}
.cover .footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: grid;
grid-template-columns: 1.1fr 1fr 1.4fr 2fr;
gap: 36px;
padding: 0 76px 56px;
z-index: 2;
}
.cover .fcell {
border-top: 1px solid var(--ink);
padding-top: 14px;
}
.cover .fcell .t {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 11px;
margin-bottom: 8px;
}
.cover .fcell .b {
font-family: var(--sans);
font-weight: 400;
font-size: 13px;
line-height: 1.5;
color: var(--ink);
}
.cover .pagenum {
position: absolute;
bottom: 30px;
right: 48px;
font-family: var(--mono);
font-size: 13px;
letter-spacing: 0.08em;
opacity: 0.75;
z-index: 2;
}
/* PALETTE */
.swatches {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 28px;
position: relative;
z-index: 2;
}
.sw .chip {
height: 130px;
border: 1px solid var(--ink);
}
.sw .name {
font-family: var(--serif);
font-size: 26px;
margin-top: 14px;
}
.sw .hex {
font-family: var(--mono);
font-size: 12px;
color: var(--ink);
opacity: 0.7;
margin-top: 6px;
}
.sw .role {
font-family: var(--sans);
font-size: 12px;
line-height: 1.5;
color: var(--ink);
margin-top: 8px;
}
/* TYPE */
.type-row {
display: grid;
grid-template-columns: 220px 1fr;
align-items: center;
padding: 26px 0;
border-bottom: 1px solid rgba(27, 37, 102, 0.2);
}
.type-row .tok {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 11px;
}
.type-row .m {
font-family: var(--mono);
font-size: 11px;
color: var(--ink);
opacity: 0.65;
margin-top: 8px;
line-height: 1.5;
}
.type-row .spec {
overflow: hidden;
}
/* COMPONENTS */
.cnote {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 12px;
margin-bottom: 22px;
}
.cgrid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 48px;
margin-bottom: 56px;
}
.s4 {
grid-column: span 4;
}
.s5 {
grid-column: span 5;
}
.s6 {
grid-column: span 6;
}
.s7 {
grid-column: span 7;
}
.s8 {
grid-column: span 8;
}
.ledger .row {
display: grid;
grid-template-columns: 92px 1.6fr 0.9fr 80px;
gap: 24px;
padding: 14px 0;
border-bottom: 1px solid rgba(27, 37, 102, 0.2);
align-items: baseline;
}
.ledger .dt {
font-family: var(--mono);
font-size: 14px;
}
.ledger .ti {
font-family: var(--serif);
font-size: 26px;
line-height: 1.1;
}
.ledger .vn {
font-family: var(--sans);
font-size: 13px;
color: var(--ink);
}
.ledger .du {
font-family: var(--mono);
font-size: 13px;
text-align: right;
}
.strand .row {
display: grid;
grid-template-columns: 56px 1fr;
gap: 20px;
padding: 14px 0;
border-bottom: 1px solid rgba(27, 37, 102, 0.2);
align-items: baseline;
}
.strand .nm {
font-family: var(--serif);
font-size: 34px;
line-height: 1;
}
.strand .ti {
font-family: var(--serif);
font-size: 24px;
line-height: 1.1;
}
.strand .bd {
font-family: var(--sans);
font-size: 13px;
line-height: 1.5;
color: var(--ink);
margin-top: 4px;
}
.bars .row {
display: grid;
grid-template-columns: 60px 1fr 60px;
gap: 16px;
align-items: center;
margin-bottom: 14px;
}
.bars .yr {
font-family: var(--mono);
font-size: 13px;
}
.bars .bar {
height: 20px;
background: var(--ink);
}
.bars .bar.lit {
background: var(--sun);
border: 1px solid var(--ink);
}
.bars .vl {
font-family: var(--mono);
font-size: 13px;
text-align: right;
}
.ypanel {
background: var(--sun);
padding: 36px;
color: var(--ink);
}
.ypanel .l {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 11px;
}
.ypanel .h {
font-family: var(--serif);
font-size: 44px;
line-height: 0.95;
margin-top: 12px;
}
/* COMPOSITIONS */
.gallery {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
position: relative;
z-index: 2;
}
.fw .flabel {
display: flex;
gap: 14px;
align-items: baseline;
margin-bottom: 16px;
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.14em;
font-size: 12px;
color: var(--ink);
opacity: 0.6;
}
.fw .flabel b {
opacity: 1;
}
.frame {
aspect-ratio: 16/9;
container-type: size;
position: relative;
overflow: hidden;
background: var(--paper);
box-shadow: 0 0 0 1px rgba(27, 37, 102, 0.15);
}
.frame > .body {
position: absolute;
inset: 0;
z-index: 3;
}
.frame .bloom {
position: absolute;
background: var(--bloom);
z-index: 1;
pointer-events: none;
}
.frame .pn {
position: absolute;
right: 3cqw;
bottom: 2.6cqw;
font-family: var(--mono);
font-size: 1cqw;
opacity: 0.75;
z-index: 4;
}
/* f1 cover */
.yf1 .bloom {
width: 58%;
height: 64%;
left: 6%;
top: 16%;
}
.yf1 .body {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 5cqw;
}
.yf1 .micro {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3cqw;
font-size: 1cqw;
margin-bottom: 1.8cqw;
}
.yf1 h3 {
font-family: var(--serif);
font-weight: 400;
font-size: 13cqw;
line-height: 0.84;
letter-spacing: -0.018em;
}
.yf1 h3 em {
font-style: italic;
}
.yf1 .dr {
position: absolute;
top: 4cqw;
right: 4cqw;
font-family: var(--serif);
font-size: 5cqw;
line-height: 0.96;
text-align: right;
letter-spacing: -0.005em;
z-index: 4;
}
/* f2 chapter divider */
.yf2 .bloom {
width: 60%;
height: 80%;
right: -10%;
top: -10%;
left: auto;
}
.yf2 .rail {
position: absolute;
left: 3cqw;
top: 50%;
transform: rotate(-90deg) translateX(50%);
transform-origin: left center;
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.32em;
font-size: 1cqw;
white-space: nowrap;
z-index: 4;
}
.yf2 .body {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 5cqw 0 9cqw;
}
.yf2 .num {
font-family: var(--serif);
font-weight: 400;
font-size: 38cqw;
line-height: 0.84;
letter-spacing: -0.04em;
color: var(--ink);
}
.yf2 .ti {
font-family: var(--serif);
font-size: 3.6cqw;
line-height: 1.05;
margin-top: -2cqw;
max-width: 46cqw;
}
/* f3 ledger */
.yf3 .body {
padding: 6cqw 5cqw;
display: flex;
flex-direction: column;
}
.yf3 .top {
display: flex;
justify-content: space-between;
align-items: baseline;
border-bottom: 1px solid var(--ink);
padding-bottom: 1.2cqw;
margin-bottom: 1.6cqw;
}
.yf3 .top h4 {
font-family: var(--serif);
font-size: 3.6cqw;
line-height: 1;
}
.yf3 .top .micro {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.2cqw;
font-size: 0.9cqw;
}
.yf3 .row {
display: grid;
grid-template-columns: 8cqw 1.6fr 0.9fr 6cqw;
gap: 1.6cqw;
padding: 1cqw 0;
border-bottom: 1px solid rgba(27, 37, 102, 0.2);
align-items: baseline;
}
.yf3 .dt {
font-family: var(--mono);
font-size: 1cqw;
}
.yf3 .ti {
font-family: var(--serif);
font-size: 1.9cqw;
line-height: 1.1;
}
.yf3 .vn {
font-family: var(--sans);
font-size: 0.95cqw;
}
.yf3 .du {
font-family: var(--mono);
font-size: 0.95cqw;
text-align: right;
}
/* f4 manifesto */
.yf4 .bloom {
width: 54%;
height: 60%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.yf4 .body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 0 9cqw;
}
.yf4 .qm {
font-family: var(--serif);
font-size: 14cqw;
line-height: 0.5;
height: 7cqw;
}
.yf4 .q {
font-family: var(--serif);
font-style: italic;
font-size: 5cqw;
line-height: 1.04;
letter-spacing: -0.005em;
}
.yf4 .at {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.25cqw;
font-size: 1cqw;
margin-top: 2.4cqw;
}
/* PRINCIPLES */
.dos {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 56px;
position: relative;
z-index: 2;
}
.do-card h4 {
font-family: var(--serif);
font-size: 34px;
margin-bottom: 8px;
}
.do-card .rule {
height: 1px;
background: var(--ink);
margin-bottom: 22px;
}
.do-card ul {
list-style: none;
}
.do-card li {
font-family: var(--sans);
font-size: 14px;
line-height: 1.6;
padding-left: 22px;
position: relative;
margin-bottom: 13px;
color: var(--ink);
}
.do-card li::before {
position: absolute;
left: 0;
font-family: var(--serif);
}
.do-card.do li::before {
content: "\2014";
}
.do-card.dont li::before {
content: "\00d7";
color: var(--ember);
}
.foot {
padding: 64px 76px;
border-top: 1px solid var(--ink);
display: flex;
justify-content: space-between;
align-items: center;
}
.foot .l {
font-family: var(--serif);
font-size: 30px;
}
.foot .r {
font-family: var(--sans);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 11px;
}
@media (max-width: 1100px) {
.gallery {
grid-template-columns: 1fr;
}
.swatches {
grid-template-columns: 1fr 1fr;
}
}
</style>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital,wght@0,400;1,400&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700&display=swap"
/>
<style id="ft-override">
:root {
--primary: #e9e5db !important;
--secondary: #1b2566 !important;
--tertiary: #dcd6c4 !important;
--accent: #f1ee2e !important;
--f-disp: "Instrument Serif", sans-serif !important;
--f-body: "Archivo", sans-serif !important;
--paper: #e9e5db !important;
--paper-deep: #dcd6c4 !important;
--sun: #f1ee2e !important;
--ink: #1b2566 !important;
--serif: "Instrument Serif", sans-serif !important;
--sans: "Archivo", sans-serif !important;
--sun-soft: #f8f39b !important;
--haze: #f0da7c !important;
--ember: #e26b4a !important;
}
</style>
</head>
<body>
<!-- COVER -->
<section class="cover">
<div class="bloom"></div>
<div class="ember"></div>
<div class="daterail">2026<br />—2027</div>
<div class="inner">
<div class="micro">Frame System · Vol. 01</div>
<h1>Paper, ink<br />& <em>yellow.</em></h1>
</div>
<div class="footer">
<div class="fcell">
<div class="t">Register</div>
<div class="b">Literary-editorial, biennale catalogue.</div>
</div>
<div class="fcell">
<div class="t">Ground</div>
<div class="b">Warm parchment, never white.</div>
</div>
<div class="fcell">
<div class="t">Type</div>
<div class="b">Instrument Serif · Archivo · JetBrains Mono.</div>
</div>
<div class="fcell">
<div class="t">Depth</div>
<div class="b">Atmospheric blooms, no shadows, hairline rules.</div>
</div>
</div>
<div class="pagenum">01 / 06</div>
</section>
<!-- PALETTE -->
<section>
<div class="sec-head">
<span class="micro">01</span>
<h2>Palette</h2>
<span class="sp"></span><span class="micro">Paper · Ink · Yellow</span>
</div>
<div class="swatches">
<div class="sw">
<div class="chip" style="background: var(--paper)"></div>
<div class="name">Paper</div>
<div class="hex">#E9E5DB</div>
<div class="role">The warm parchment ground.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--ink)"></div>
<div class="name">Ink</div>
<div class="hex">#1B2566</div>
<div class="role">The single text + rule color.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--sun)"></div>
<div class="name">Sun</div>
<div class="hex">#F1EE2E</div>
<div class="role">Panel, bloom, tile underprint.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--ember)"></div>
<div class="name">Ember</div>
<div class="hex">#E26B4A</div>
<div class="role">Subordinate counter-bloom only.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--paper-deep)"></div>
<div class="name">Paper Deep</div>
<div class="hex">#DCD6C4</div>
<div class="role">Secondary parchment band.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--sun-soft)"></div>
<div class="name">Sun Soft</div>
<div class="hex">#F8F39B</div>
<div class="role">Mid stop in sun blooms.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--haze)"></div>
<div class="name">Haze</div>
<div class="hex">#F0DA7C</div>
<div class="role">Outer stop in sun blooms.</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--bloom), var(--paper)"></div>
<div class="name">Sun Bloom</div>
<div class="hex">radial · atmosphere</div>
<div class="role">The primary depth layer.</div>
</div>
</div>
</section>
<!-- TYPOGRAPHY -->
<section>
<div class="sec-head">
<span class="micro">02</span>
<h2>Typography</h2>
<span class="sp"></span><span class="micro">Serif · Sans · Mono</span>
</div>
<div class="type-row">
<div>
<div class="tok">display</div>
<div class="m">Instrument Serif 400 · line 0.86 · −0.018em</div>
</div>
<div class="spec"><span class="serif" style="font-size: 104px">Biennale</span></div>
</div>
<div class="type-row">
<div>
<div class="tok">display-it</div>
<div class="m">Instrument Serif italic · slow-reading</div>
</div>
<div class="spec">
<span class="serif" style="font-style: italic; font-size: 72px; line-height: 1.04"
>quietly,</span
>
</div>
</div>
<div class="type-row">
<div>
<div class="tok">numeral-md</div>
<div class="m">Instrument Serif 400 · hero stat</div>
</div>
<div class="spec">
<span class="serif" style="font-size: 96px; letter-spacing: -0.04em">240</span>
</div>
</div>
<div class="type-row">
<div>
<div class="tok">body</div>
<div class="m">Archivo 400 · line 1.5 · ink</div>
</div>
<div class="spec">
<p style="font-family: var(--sans); font-size: 16px; line-height: 1.5; max-width: 620px">
Archivo carries every paragraph — the quiet sans beneath the serif display, always in
ink.
</p>
</div>
</div>
<div class="type-row" style="border-bottom: 0">
<div>
<div class="tok">micro-label</div>
<div class="m">Archivo 600 · uppercase · 0.18em</div>
</div>
<div class="spec">
<span class="micro">Programme · Section</span>
<span
class="serif"
style="font-family: var(--mono); font-size: 14px; letter-spacing: 0.04em"
>12 — 14 JUN</span
>
</div>
</div>
</section>
<!-- COMPONENTS -->
<section>
<div class="sec-head">
<span class="micro">03</span>
<h2>Components</h2>
<span class="sp"></span><span class="micro">Hairlines · Blooms · Panels</span>
</div>
<div class="cgrid">
<div class="s7">
<div class="cnote">
Ledger rows — mono date · serif title · sans venue · mono duration
</div>
<div class="ledger">
<div class="row">
<span class="dt">12 JUN</span><span class="ti">Opening Address</span
><span class="vn">Giardini Pavilion</span><span class="du">45m</span>
</div>
<div class="row">
<span class="dt">13 JUN</span><span class="ti">On Slow Reading</span
><span class="vn">Arsenale Hall</span><span class="du">90m</span>
</div>
<div class="row">
<span class="dt">14 JUN</span><span class="ti">Closing Colophon</span
><span class="vn">Central Library</span><span class="du">30m</span>
</div>
</div>
</div>
<div class="s5">
<div class="cnote">Strand list + chart bars</div>
<div class="strand">
<div class="row">
<span class="nm">01</span>
<div><div class="ti">Parchment</div></div>
</div>
<div class="row">
<span class="nm">02</span>
<div><div class="ti">Ink</div></div>
</div>
</div>
<div class="bars" style="margin-top: 20px">
<div class="row">
<span class="yr">'24</span><span class="bar" style="width: 60%"></span
><span class="vl">60</span>
</div>
<div class="row">
<span class="yr">'25</span><span class="bar lit" style="width: 88%"></span
><span class="vl">88</span>
</div>
</div>
</div>
</div>
<div class="cgrid" style="margin-bottom: 0">
<div class="s5">
<div class="cnote">Yellow panel — strongest color statement</div>
<div class="ypanel">
<div class="l">Poster Fill</div>
<div class="h">Ink on Sun.</div>
</div>
</div>
<div class="s7">
<div class="cnote">Sun bloom + ember counter-bloom</div>
<div
style="
position: relative;
height: 170px;
border: 1px solid rgba(27, 37, 102, 0.2);
overflow: hidden;
"
>
<div
style="
position: absolute;
width: 60%;
height: 130%;
left: 6%;
top: -15%;
background: var(--bloom);
"
></div>
<div
style="
position: absolute;
width: 40%;
height: 90%;
right: -8%;
bottom: -20%;
background: radial-gradient(circle, rgba(226, 107, 74, 0.22), transparent 70%);
"
></div>
<div style="position: absolute; left: 24px; bottom: 20px" class="serif">
<span style="font-size: 40px">atmosphere.</span>
</div>
</div>
</div>
</div>
</section>
<!-- COMPOSITIONS -->
<section>
<div class="sec-head">
<span class="micro">04</span>
<h2>Frame Compositions</h2>
<span class="sp"></span><span class="micro">True 16:9 · cqw</span>
</div>
<div class="gallery">
<div class="fw">
<div class="flabel"><b>Cover</b><span>· identity · sun bloom · date rail</span></div>
<div class="frame yf1">
<div class="bloom"></div>
<div class="dr">2026<br />—27</div>
<div class="body">
<div class="micro">Frame System</div>
<h3>Paper, ink<br />& <em>yellow.</em></h3>
</div>
<div class="pn">01 / 06</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Chapter Divider</b><span>· section · jumbo numeral · vertical rail</span>
</div>
<div class="frame yf2">
<div class="bloom"></div>
<div class="rail">Section — Two</div>
<div class="body">
<div class="num">02</div>
<div class="ti">On measure & the slow page.</div>
</div>
<div class="pn">02 / 06</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Ledger</b><span>· catalog · hairline rows · the dense frame</span>
</div>
<div class="frame yf3">
<div class="body">
<div class="top">
<h4>Programme</h4>
<span class="micro">12 — 14 JUN</span>
</div>
<div class="row">
<span class="dt">12 JUN</span><span class="ti">Opening Address</span
><span class="vn">Giardini</span><span class="du">45m</span>
</div>
<div class="row">
<span class="dt">13 JUN</span><span class="ti">On Slow Reading</span
><span class="vn">Arsenale</span><span class="du">90m</span>
</div>
<div class="row">
<span class="dt">14 JUN</span><span class="ti">Closing Colophon</span
><span class="vn">Library</span><span class="du">30m</span>
</div>
</div>
<div class="pn">03 / 06</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Manifesto</b><span>· quote · italic serif · centered bloom</span>
</div>
<div class="frame yf4">
<div class="bloom"></div>
<div class="body">
<div class="qm">“</div>
<div class="q">Read it slowly,<br />or not at all.</div>
<div class="at">— The System Voice</div>
</div>
<div class="pn">04 / 06</div>
</div>
</div>
</div>
</section>
<!-- PRINCIPLES -->
<section>
<div class="sec-head">
<span class="micro">05</span>
<h2>Frame Rules</h2>
<span class="sp"></span><span class="micro">Restraint</span>
</div>
<div class="dos">
<div class="do-card do">
<h4>Do</h4>
<div class="rule"></div>
<ul>
<li>Start on warm parchment; add at least one sun bloom for atmosphere.</li>
<li>
Set every line in ink — display, body, label, mono; contrast is size, not color.
</li>
<li>Use Instrument Serif 400, tight line-height, negative tracking, for display.</li>
<li>
Make every separator a 1px ink hairline; flood a yellow panel for poster moments.
</li>
<li>Reserve JetBrains Mono for dates, numerals, and the bottom-right pagenum.</li>
</ul>
</div>
<div class="do-card dont">
<h4>Don't</h4>
<div class="rule"></div>
<ul>
<li>No drop shadows, no rounded corners, no bordered cards.</li>
<li>No second text color; no bold Instrument Serif.</li>
<li>No borders thicker than 1px; no inverted ink grounds.</li>
<li>No mono for body or headlines; no font substitutes.</li>
<li>Don't crowd the canvas — sparse reads as elegant.</li>
</ul>
</div>
</div>
</section>
<!-- CAPTIONS — preview of caption-skin.html (the preset's lower-third karaoke look) -->
<style>
/* mirrors caption-skin.html; the skin's --cap-* vocab mapped to this preset's tokens */
.capdemo {
--cap-ink: var(--ink);
--cap-canvas: var(--paper);
--cap-accent: var(--sun);
--cap-accent-2: var(--ember);
}
.capdemo .stage {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 16.67cqh;
display: flex;
align-items: center;
justify-content: center;
z-index: 4;
}
.capdemo .pill {
max-width: 80cqw;
padding: 1.4cqw 3cqw 1.7cqw;
background: var(--cap-canvas);
border: 1px solid var(--cap-ink);
border-radius: 0;
}
.capdemo .line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.1em 0.34em;
font-family: var(--serif);
font-weight: 400;
font-size: 3.4cqw;
line-height: 1.16;
letter-spacing: -0.01em;
}
.capdemo .w {
display: inline-block;
padding: 0 0.06em;
color: color-mix(in srgb, var(--cap-ink) 42%, var(--cap-canvas));
}
.capdemo .w.spoken {
color: var(--cap-ink);
}
.capdemo .w.active {
color: var(--cap-ink);
background: var(--cap-accent);
box-shadow: 0 0 0 0.06em var(--cap-accent);
}
.capdemo .bandline {
position: absolute;
left: 0;
right: 0;
bottom: 16.67cqh;
border-top: 1px dashed rgba(27, 37, 102, 0.28);
z-index: 3;
}
.capdemo .bandtag {
position: absolute;
right: 3cqw;
bottom: calc(16.67cqh + 0.7cqw);
font-family: var(--mono);
font-size: 0.95cqw;
letter-spacing: 0.04em;
color: var(--ink);
opacity: 0.5;
z-index: 4;
}
</style>
<section>
<div class="sec-head">
<span class="micro">06</span>
<h2>Captions</h2>
<span class="sp"></span><span class="micro">Lower-third · Karaoke</span>
</div>
<div class="gallery">
<div class="fw">
<div class="flabel"><b>Active line</b><span>· spoken · current · upcoming</span></div>
<div class="frame capdemo">
<div class="bloom" style="width: 54%; height: 60%; left: 10%; top: 12%"></div>
<div class="bandline"></div>
<div class="bandtag">caption band · 16.7%</div>
<div class="stage">
<div class="pill">
<div class="line">
<span class="w spoken">Built</span>
<span class="w spoken">for</span>
<span class="w active">teams</span>
<span class="w">who</span>
<span class="w">ship.</span>
</div>
</div>
</div>
<div class="pn">05 / 06</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Settled line</b><span>· all spoken · underprint cleared</span>
</div>
<div class="frame capdemo">
<div
class="bloom"
style="width: 50%; height: 58%; right: -6%; bottom: -6%; left: auto"
></div>
<div class="stage">
<div class="pill">
<div class="line">
<span class="w spoken">Ship</span>
<span class="w spoken">faster</span>
<span class="w spoken">today.</span>
</div>
</div>
</div>
<div class="pn">06 / 06</div>
</div>
</div>
</div>
</section>
<div class="foot">
<span class="l">Biennale Yellow</span>
<span class="r">Frame Showcase · Atoms Sacred · Composition Free</span>
</div>
<template id="__bundler_thumbnail">
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="#E9E5DB" />
<circle cx="42" cy="42" r="34" fill="#F1EE2E" opacity="0.55" />
<text x="14" y="64" font-family="Georgia" font-size="30" fill="#1B2566">Aa</text>
<rect x="14" y="74" width="60" height="1.5" fill="#1B2566" />
</svg>
</template>
</body>
</html>
Biennale Yellow — Frame (video / frame layer)
Overview
Biennale Yellow at frame scale is a literary-editorial system in the register of an art biennale catalogue: warm parchment, a single deep indigo ink, and solar yellow as atmosphere. No cards, no buttons, no shadows, no rounded corners — the structural vocabulary is just paper, ink, and yellow. Depth is delivered by soft radial sun blooms, not elevation.
The voice is three faces in rigid roles: Instrument Serif (weight 400, tight line-height, negative tracking) carries every display, numeral, and quote from 40px to 720px+; Archivo carries body and the wide-tracked uppercase micro-label; JetBrains Mono carries every date, figure, and the pagenum. Text is always ink — contrast comes from size and weight, never color. The mood sits between a folded museum brochure and a slow-reading literary quarterly: confident, atmospheric, deeply restrained.
Key characteristics at frame scale:
- Warm parchment ground on every frame; never white, never gray.
- Single ink (
{colors.ink}) for all type and all rules; solar yellow as bloom / panel / tile. - Instrument Serif 400 display (tight, negative-tracked); Archivo body + micro-labels; JetBrains Mono data.
- 1px hairline rules are the only border — no thicker weight exists; no shadows, no rounded corners.
- Sun bloom is the primary depth layer on every frame; an ember counter-bloom adds warm-cool tension.
- Editorial-restrained — sparse reads as elegant; crowding breaks the catalogue feel.
The Frame
Frame Craft Bar
Three eyeball tests gate every frame before any structural check:
- Squint — one Instrument Serif moment dominates at 3–6× its neighbor; the sun bloom centers the eye.
- Silence — frames read 55–60% empty; the ledger is the one dense exception (density via quiet hairline repetition, not richness).
- Restraint — one ink color for all type and rules; one sun bloom per frame (+ optional subordinate ember); never invert (no yellow text on ink).
- Reference — aim at an art-biennale catalogue / slow exhibition poster / literary quarterly; failure looks like a flat CMS template (no bloom) or a bordered-card deck.
- Primary: 1920×1080 (16:9). Display authored in `cqw` (
px ÷ 1920 × 100 = cqw). - Vertical: 1080×1920 (9:16). Square: 1080×1080 (1:1).
- Safe area:
pad-edge4cqw (40–76px equivalent) — the elegance depends on edge negative space; only blooms, tiles, and panels bleed.
The container law (load-bearing). Every frame ground sets container-type: size; ALL frame-relative units are cqw/cqh against it — never vw. Hairlines stay 1px; bloom sizes scale as % of the frame.
Colors
Tokens identical to the source. {colors.paper} is the universal ground; {colors.ink} (deep indigo navy) is every line of type and every rule — there is no secondary text color. {colors.sun} deploys three ways: the bloom (radial atmosphere), the yellow-panel (full-bleed poster fill, ink on top), and the block-tile underprint. {colors.sun-soft}/haze are bloom mid/outer stops. {colors.ember} appears only as a 15–22% counter-bloom — never a fill, never text. The system never inverts — ink on sun is correct; yellow text on ink does not exist.
Typography
Two ramps. The reading/data ramp (Archivo body 0.85cqw ink, micro-labels in px, JetBrains Mono data) carries copy + chrome; the display ramp (Instrument Serif ledger-title 1.55cqw → numeral-jumbo 28cqw) carries every headline, numeral, and quote.
- Legibility floor: any load-bearing line ≥ 1.4cqw; mono/labels in px are chrome only.
- Fit-to-measure: size the headline to its length. Cap the block at ≤ 78cqw; ≤3 words →
display; 4–6 →headline; 7+ →headline-sm. The jumbo numeral is a divider's whole point — don't shrink it. - Instrument Serif is weight 400 only (its contrast is the weight signal), tight line-height (0.84–1.06), negative tracking; micro-labels uppercase Archivo 600, ≥0.16em; mono for all numerals/dates. No bold serif, no second text color, no mono in body/display.
Depth & Surface
Atmospheric, not structural. Depth from:
- Sun bloom — the primary layer: a layered radial (sun 70–95% core → sun-soft → haze 18–22% → paper 0%), 42–70% of the frame. One per frame.
- Ember bloom — a 15–22% peach counter-bloom in the opposite corner; always subordinate.
- Block-tile underprint — translucent yellow rectangles on an 8×4 grid (cover/colophon).
- Yellow panel — the one "hard" color statement: a flooded column/third, ink on top.
- Hairline rules — 1px ink for structural separation (soft 18–20% variant for dense rows).
Ceiling: zero box-shadow, zero text-shadow, zero rounded corner, no border thicker than 1px.
Shapes
- 0 radius on everything — strict rectangles. Blooms are edgeless (they fade into paper).
Components
- sun-bloom / ember-bloom / block-tile — the atmospheric depth set. yellow-panel — the poster-fill statement.
- hairline-rule — the only border (1px ink; soft variant for dense rows).
- strand-row / ledger-row / footer-band — the editorial list, tabular calendar, and metadata strip.
- vertical-rail — rotated chapter marker. pagenum — the bottom-right mono chrome.
Frame Treatments
Recipe: ground · container · composes · focal · chrome · accent · silence · Fixed/Free · density.
A sun bloom + bottom-right pagenum on every frame; sparse is the default.
1 · Cover (identity · move: display + sun bloom · left)
Ground paper + a large sun-bloom (left-of-center) + an ember counter-bloom (opposite corner). Composes micro-label, display, date-rail, footer-band, pagenum. Focal a 2-line Instrument Serif display (italic key word) in ink, left, under a micro-label. Chrome a serif date-rail top-right; a 4-column footer-band at the foot. Accent the sun bloom + yellow. Silence the bloom holds the open space. Fixed ink type, one bloom, 1px footer rules, no shadow. Free title, date, footer cells. Density low.
2 · Chapter Divider (section · move: jumbo numeral · vertical rail)
Ground paper + a corner-anchored sun-bloom. Composes vertical-rail, numeral-jumbo, headline-sm. Focal a single huge Instrument Serif numeral-jumbo (≈28cqw) dominating, with a serif title beneath. Chrome a rotated vertical-rail label up the left edge; pagenum. Accent the bloom behind the numeral. Silence ~60%. Fixed serif 400, jumbo numeral, rail label. Free the ordinal, title, rail text. Density low.
3 · Ledger (catalog · move: hairline tabular rows · the dense frame)
Ground paper (bloom optional, subtle). Composes headline-sm + micro-label topbar, ledger-rows. Focal a 4-column tabular calendar — mono date · serif title · sans venue · mono duration — separated by hairline-soft rules under a 1px ink header rule. Chrome pagenum. Accent none — density through quiet repetition, not color. Silence tight — the density exception (achieved by repetition, not richness). Fixed hairline rules, mono dates, serif titles. Free rows, venues. Density dense-exception.
4 · Manifesto / Quote (quote · move: italic serif · centered bloom)
Ground paper + a centered sun-bloom. Composes numeral-lg quote mark, display-it, attribution. Focal a 2-line Instrument Serif italic display-it quote in ink, centered, under an oversized serif quote mark. Chrome a micro-label attribution. Accent the centered bloom. Silence ~60% — deliberately open. Fixed italic serif, one bloom, ink. Free quote, attribution. Density low.
5 · Poster Panel (statement · move: yellow panel · split)
Ground paper with a full-bleed {colors.sun} yellow-panel (column or third). Composes yellow-panel, micro-label, headline. Focal an Instrument Serif headline in ink sitting on the sun panel (ink-on-yellow — a signature). Chrome micro-label; pagenum. Accent the panel itself. Silence the paper side stays open. Fixed ink-on-sun, panel meets paper directly (no border), no shadow. Free headline, panel side/width. Density low.
6 · Strand List (programme · move: numbered editorial rows · left)
Ground paper + a subtle sun-bloom. Composes micro-label, headline-sm, strand-rows. Focal a numbered list — serif numeral + serif title + sans body, hairline-soft separators. Chrome micro-label; pagenum. Accent the bloom. Silence moderate; rows breathe. Fixed serif numerals, hairline-soft rules. Free items, copy. Density standard.
Composition Rules
Do
- Start on warm parchment; add one sun bloom (optionally an ember counter-bloom) — atmosphere is the depth.
- Set every line in ink; use Instrument Serif 400 (tight, negative-tracked) for display, Archivo body, JetBrains Mono for all numerals/dates.
- Make every separator a 1px ink hairline (soft variant for dense rows); flood a yellow panel for poster moments (ink on top).
- Keep micro-labels uppercase Archivo 600, 0.16–0.32em; pin the pagenum bottom-right.
- Lean sparse; left/asymmetric on cover/chapter/list, centered on manifesto.
Don't
- No drop shadows, no rounded corners, no bordered cards, no border thicker than 1px.
- No second text color; no bold Instrument Serif; no inverted ink grounds.
- No mono for body/display; no font substitutes.
- Don't crowd the canvas — sparse reads as elegant; don't omit the bloom (flat parchment reads as a CMS template).
- Don't blow a headline edge-to-edge — step the ramp down.
Aspect-Ratio Behavior
| Treatment | 16:9 | 9:16 | 1:1 |
|---|---|---|---|
| Cover | display left, date-rail top-right, footer foot | display top, footer stacks | display upper, footer foot |
| Chapter Divider | jumbo numeral, rail left | numeral centered, rail top | numeral centered |
| Ledger | 4-col rows | drop venue col → 3-col | 3-col |
| Manifesto | centered italic | centered, taller | centered |
| Poster Panel | side panel + paper | top/bottom panel band | panel third |
| Strand List | numbered rows | rows (tighter) | rows |
pad-edge holds on the short edge; display clamps use the shorter axis so portrait doesn't blow out headlines. Keep load-bearing lines ≥ 1.4cqw. Ledger/strand fixed first columns may tighten on 9:16.
Approved Entities
No real customers, logos, or vendors are defined in the source — render any such mark as a placeholder. Programme titles, venues, and dates are content; the system supplies paper, ink, yellow.
Numerals & Claims (hard rule)
Never invent figures, dates, durations, or counts at frame scale. Render slots as — figure —, {metric}, NN JUN, N m. Ledger dates/durations and chart values especially carry placeholders until the script supplies them. Chapter ordinals (01, 02…) are decorative.
Pre-Render Self-Audit
- Squint — one serif display moment dominates; the bloom centers the eye.
- Silence — sparse frames 55–60% open; only the ledger runs dense (via repetition, not richness).
- One color — ink for all text + rules; sun for bloom/panel/tile; ember counter-bloom only; no inversion.
- Type — Instrument Serif 400 tight negative-tracked, fit-to-measure; micro-labels uppercase 0.16em+; mono numerals; ≥1.4cqw floor.
- Depth — 0 shadow, 0 rounded corner, 1px hairlines only; one sun bloom present.
- Anchor — left on cover/chapter/list, centered on manifesto; pagenum bottom-right.
- Fabrication — every numeral/date traces to the script, else placeholder.
Known Gaps
- Motion intentionally out of scope. frame.md specifies composition only; the source's 280ms crossfade is a deck mechanic.
- Instrument Serif + Archivo + JetBrains Mono via Google Fonts. CJK: Smiley Sans (display) / Noto Serif SC (body) / Noto Sans SC (labels); italic serif and mono tabular figures have no exact Hanzi equal — keep ledger dates Latin.
- 9:16 / 1:1 are guidance; the
min(vw,vh)clamp pattern keeps display from blowing out — verify per ratio. - Sun/ember blooms, block tiles, yellow panels, and hairline rules are CSS-only; no external imagery is required.
<!--
BlockFrame — caption skin (preset-local source).
This is the preset's own lower-third karaoke caption look. product-launch-video's
Step 2 copies it into the project as caption-skin.html; captions.mjs then fills the
three reserved holes, wraps the fragment in a <template>, and writes
compositions/captions.html:
· the empty GROUPS array → the engine's scene-aware word groups
· the 0 duration + 0 width/height → real total + canvas size
· the empty data-brand-tokens style → :root tokens derived from the project's frame.md
Token contract — reference ONLY this fixed vocab (captions.mjs injects it from
frame.md, so the Step-2 brand overlay flows through; each var has a blockframe literal
fallback for standalone preview):
--cap-ink · --cap-canvas · --cap-accent · --cap-accent-2 · --font-display ·
--font-body · --cap-band-top · --cap-band-height
Hooks (do not rename): .caption-group / .caption-word + states .is-active / .is-spoken,
root data-composition-id="captions", timeline at window.__timelines["captions"].
State changes use gsap.set({className}) (applied on the engine's frame-by-frame seek);
never tl.call() callbacks (seek does not fire them → wrong state on render).
Visual: a white card carved by a 4px black border with an 8px hard offset shadow
(solid black, zero blur, bottom-right — blockframe's sacred depth), square corners.
Inter 900, UPPERCASE, negative-tracked. Upcoming words sit in faded ink; the current
word gets a saturated-yellow stamp-block — a hard square highlight (box-shadow spread,
no blur) with black ink reading on top, blockframe's signature stamp gesture; spoken
words settle to solid black ink, stamp cleared. The word pops in with a snappy
back.out overshoot — loud, a little crooked.
-->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<!-- captions.mjs injects the project's :root tokens here -->
<style data-brand-tokens></style>
<style>
#captions-root {
position: absolute;
inset: 0;
pointer-events: none;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
}
/* the keep-out band, from captionBand(H) — frame content stays above it */
.caption-stage {
position: absolute;
left: 0;
right: 0;
top: var(--cap-band-top, 900px);
height: var(--cap-band-height, 180px);
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
/* every group is absolutely stacked over the SAME band — exactly one shown at a
time via opacity. Must be absolute: in-flow groups would lay out side-by-side. */
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
/* THE bordered card — 4px black border + 8px hard offset shadow (solid black,
zero blur, bottom-right — blockframe's sacred depth), square corners */
.caption-pill {
max-width: 80%;
padding: 18px 40px 22px;
background: var(--cap-canvas, #ffffff);
border: 4px solid var(--cap-ink, #000000);
border-radius: 0;
box-shadow: 8px 8px 0 var(--cap-ink, #000000);
}
.caption-line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.12em 0.32em;
font-family: var(--font-display, "Inter"), system-ui, sans-serif;
font-weight: 900;
text-transform: uppercase;
font-size: clamp(38px, 4.2vw, 56px);
line-height: 1.12;
letter-spacing: -0.02em;
}
/* upcoming — faded black ink, no stamp */
.caption-word {
display: inline-block;
padding: 0 0.1em;
color: color-mix(in srgb, var(--cap-ink, #000000) 40%, var(--cap-canvas, #ffffff));
}
/* current word — THE yellow stamp-block (box-shadow spread = a hard square block
w/o layout shift; no blur), black ink reading on top — blockframe's signature stamp */
.caption-word.is-active {
color: var(--cap-ink, #000000);
background: var(--cap-accent, #f7cb46);
box-shadow: 0 0 0 0.08em var(--cap-accent, #f7cb46);
}
/* already spoken — solid black ink, stamp cleared */
.caption-word.is-spoken {
color: var(--cap-ink, #000000);
background: transparent;
box-shadow: none;
}
/* optional per-word accents (dormant until a word-classer tags them) */
/* brand / key term — heavier ink weight, blockframe's display is already 900 */
.caption-word.cap-brand {
color: var(--cap-ink, #000000);
}
/* numerals / data — the body voice, slightly recessed */
.caption-word.cap-num {
font-family: var(--font-body, "Inter"), ui-monospace, monospace;
color: color-mix(in srgb, var(--cap-ink, #000000) 74%, var(--cap-canvas, #ffffff));
}
/* emphasis — a cream hard underline bar (the second-chroma accent) */
.caption-word.cap-cta {
text-decoration: underline;
text-decoration-thickness: 4px;
text-decoration-color: var(--cap-accent-2, #ffdc8b);
text-underline-offset: 0.12em;
}
@media (max-aspect-ratio: 9/16) {
.caption-pill {
max-width: 92%;
}
.caption-line {
font-size: clamp(34px, 5.4vw, 48px);
}
}
</style>
<div
id="captions-root"
data-composition-id="captions"
data-timeline-locked
data-start="0"
data-duration="0"
data-fps="30"
data-width="0"
data-height="0"
>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="caption-stage"></div>
</div>
</div>
<script>
// captions.mjs injects the engine's scene-aware groups + real duration here.
var GROUPS = [];
var DURATION = 0;
(function () {
var stage = document.getElementById("caption-stage");
// build DOM: one .caption-group per group, .caption-word per word
GROUPS.forEach(function (group, g) {
var groupEl = document.createElement("div");
groupEl.className = "caption-group";
groupEl.id = "caption-group-" + g;
var pill = document.createElement("div");
pill.className = "caption-pill";
var line = document.createElement("div");
line.className = "caption-line";
(group.words || []).forEach(function (w, i) {
var span = document.createElement("span");
span.className = "caption-word";
span.id = "caption-word-" + g + "-" + i;
span.textContent = String(w.text);
line.appendChild(span);
});
pill.appendChild(line);
groupEl.appendChild(pill);
stage.appendChild(groupEl);
});
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, g) {
var groupEl = document.getElementById("caption-group-" + g);
var words = group.words || [];
var next = GROUPS[g + 1];
var isLast = g === GROUPS.length - 1;
var start = Math.max(0, Number(group.start));
var end = isLast ? DURATION : Math.min(Number(next.start), Number(group.end) + 0.3);
if (end <= start) end = start + 0.01;
// group on/off — exactly one group visible at a time
tl.set(groupEl, { opacity: 1 }, start);
tl.set(groupEl, { opacity: 0 }, end);
// 3-state karaoke via className SETS — seek-safe (gsap.set applies on the
// engine's frame-by-frame seek; .call callbacks would NOT fire).
words.forEach(function (w, i) {
var el = document.getElementById("caption-word-" + g + "-" + i);
var at = Math.max(start, Number(w.start));
tl.set(el, { className: "caption-word" }, start); // upcoming (also resets on reverse-seek)
tl.set(el, { className: "caption-word is-active" }, at); // current
tl.fromTo(el, { scale: 0.86 }, { scale: 1, duration: 0.2, ease: "back.out(2.4)" }, at);
if (i + 1 < words.length) {
var nextAt = Math.max(start, Number(words[i + 1].start));
tl.set(el, { className: "caption-word is-spoken" }, nextAt); // demote when next activates
}
});
if (words.length) {
var lastEl = document.getElementById("caption-word-" + g + "-" + (words.length - 1));
var lastSpoken = Math.min(end, Number(words[words.length - 1].end) + 0.1);
tl.set(lastEl, { className: "caption-word is-spoken" }, lastSpoken);
}
});
// full-span anchor so the sub-comp timeline spans the whole video
tl.to({}, { duration: DURATION }, 0);
window.__timelines["captions"] = tl;
})();
</script>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>BlockFrame</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;800;900&family=Space+Grotesk:wght@500;600;700&display=swap"
rel="stylesheet"
/>
<style id="ds-tokens">
:root {
--black: #000000;
--white: #ffffff;
--offwhite: #fffdf5;
--pink: #fe90e8;
--blue: #c0f7fe;
--green: #99e885;
--yellow: #f7cb46;
--cream: #ffdc8b;
--disp: "Inter", sans-serif;
--mono: "Space Grotesk", monospace;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
background: #111;
}
body {
background: var(--offwhite);
color: var(--black);
font-family: var(--disp);
-webkit-font-smoothing: antialiased;
}
.disp {
font-family: var(--disp);
text-transform: uppercase;
letter-spacing: -0.03em;
line-height: 0.95;
font-weight: 900;
}
.pill {
display: inline-block;
border: 3px solid var(--black);
background: var(--white);
box-shadow: 4px 4px 0 var(--black);
padding: 6px 16px;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 13px;
}
.dotgrid {
background-image: radial-gradient(circle, var(--black) 1.2px, transparent 1.2px);
background-size: 24px 24px;
}
section {
padding: 90px 60px;
position: relative;
border-bottom: 4px solid var(--black);
}
.sec-head {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 48px;
}
.sec-head h2 {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.03em;
font-size: 46px;
line-height: 1;
}
.sec-head .sp {
flex: 1;
height: 4px;
background: var(--black);
}
/* COVER */
.cover {
min-height: 100vh;
background: var(--offwhite);
padding: 60px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden;
}
.cover .dg {
position: absolute;
inset: 0;
opacity: 0.4;
background-image: radial-gradient(circle, var(--black) 1.2px, transparent 1.2px);
background-size: 24px 24px;
pointer-events: none;
}
.cover .inner {
position: relative;
z-index: 3;
max-width: 72%;
}
.cover .pill {
margin-bottom: 28px;
}
.cover h1 {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.04em;
line-height: 0.9;
font-size: clamp(64px, 9vw, 150px);
}
.cover .sub {
font-family: var(--mono);
font-weight: 500;
font-size: 18px;
line-height: 1.5;
color: var(--black);
margin-top: 24px;
max-width: 540px;
}
.cover .deco-rect {
position: absolute;
top: 18%;
right: 8%;
width: 200px;
height: 130px;
background: var(--pink);
border: 4px solid var(--black);
box-shadow: 8px 8px 0 var(--black);
transform: rotate(8deg);
z-index: 2;
}
.cover .deco-rect2 {
position: absolute;
bottom: 16%;
right: 18%;
width: 120px;
height: 120px;
background: var(--blue);
border: 4px solid var(--black);
box-shadow: 8px 8px 0 var(--black);
transform: rotate(-6deg);
z-index: 1;
}
.cover .star {
position: absolute;
top: 30%;
right: 30%;
width: 90px;
height: 90px;
background: var(--yellow);
border: 3px solid var(--black);
clip-path: polygon(
50% 0%,
61% 35%,
98% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
2% 35%,
39% 35%
);
z-index: 4;
}
.cover .counter {
position: absolute;
bottom: 30px;
left: 60px;
border: 3px solid var(--black);
background: var(--white);
box-shadow: 4px 4px 0 var(--black);
padding: 10px 18px;
font-family: var(--mono);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 14px;
}
/* PALETTE */
.swatches {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 28px;
}
.sw {
border: 4px solid var(--black);
background: var(--white);
box-shadow: 8px 8px 0 var(--black);
}
.sw .chip {
height: 120px;
border-bottom: 4px solid var(--black);
}
.sw .meta {
padding: 16px 18px;
}
.sw .name {
font-family: var(--disp);
font-weight: 800;
text-transform: uppercase;
letter-spacing: -0.01em;
font-size: 18px;
}
.sw .hex {
font-family: var(--mono);
font-weight: 500;
font-size: 12px;
letter-spacing: 0.04em;
margin-top: 6px;
}
/* TYPE */
.type-row {
display: grid;
grid-template-columns: 220px 1fr;
align-items: center;
padding: 24px 0;
border-bottom: 4px solid var(--black);
}
.type-row .tok {
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
}
.type-row .m {
font-family: var(--mono);
font-weight: 500;
font-size: 11px;
color: #444;
margin-top: 8px;
line-height: 1.5;
}
.type-row .spec {
overflow: hidden;
}
/* COMPONENTS */
.cnote {
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
margin-bottom: 20px;
}
.cgrid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 32px;
margin-bottom: 48px;
}
.s3 {
grid-column: span 3;
}
.s4 {
grid-column: span 4;
}
.s6 {
grid-column: span 6;
}
.s8 {
grid-column: span 8;
}
.fcard {
border: 4px solid var(--black);
background: var(--white);
box-shadow: 8px 8px 0 var(--black);
padding: 32px;
position: relative;
height: 100%;
}
.fcard .notch {
position: absolute;
top: -12px;
right: 24px;
width: 44px;
height: 44px;
background: var(--yellow);
border: 3px solid var(--black);
}
.fcard .ic {
width: 60px;
height: 60px;
border: 3px solid var(--black);
background: var(--blue);
display: grid;
place-items: center;
font-family: var(--disp);
font-weight: 700;
font-size: 26px;
margin-bottom: 20px;
}
.fcard .t {
font-family: var(--disp);
font-weight: 700;
text-transform: uppercase;
font-size: 21px;
line-height: 1.2;
}
.fcard .bd {
font-family: var(--disp);
font-weight: 500;
font-size: 14px;
line-height: 1.6;
color: #222;
margin-top: 10px;
}
.statcard {
border: 3px solid var(--black);
background: var(--white);
box-shadow: 4px 4px 0 var(--black);
padding: 24px;
position: relative;
}
.statcard .dot {
position: absolute;
top: 14px;
right: 14px;
width: 12px;
height: 12px;
border-radius: 50%;
border: 2px solid var(--black);
}
.statcard .n {
font-family: var(--disp);
font-weight: 900;
font-size: 48px;
line-height: 1;
}
.statcard .l {
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
margin-top: 8px;
}
.btn {
display: inline-block;
border: 3px solid var(--black);
background: var(--yellow);
box-shadow: 4px 4px 0 var(--black);
padding: 14px 32px;
font-family: var(--disp);
font-weight: 700;
font-size: 16px;
}
.stripe {
height: 80px;
border: 3px solid var(--black);
background: repeating-linear-gradient(
45deg,
var(--black),
var(--black) 4px,
var(--green) 4px,
var(--green) 12px
);
}
.listnum {
display: inline-grid;
place-items: center;
width: 36px;
height: 36px;
border: 3px solid var(--black);
background: var(--yellow);
font-family: var(--mono);
font-weight: 700;
font-size: 14px;
}
/* COMPOSITIONS */
.gallery {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
}
.fw .flabel {
display: flex;
gap: 14px;
align-items: baseline;
margin-bottom: 16px;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 12px;
color: #555;
}
.fw .flabel b {
color: var(--black);
}
.frame {
aspect-ratio: 16/9;
container-type: size;
position: relative;
overflow: hidden;
background: var(--offwhite);
border: 4px solid var(--black);
}
.frame > .body {
position: absolute;
inset: 0;
z-index: 3;
}
/* f1 cover */
.bf1 {
background: var(--cream);
}
.bf1 .dg {
position: absolute;
inset: 0;
opacity: 0.35;
background-image: radial-gradient(circle, var(--black) 0.15cqw, transparent 0.15cqw);
background-size: 3cqw 3cqw;
z-index: 1;
}
.bf1 .body {
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 5cqw;
}
.bf1 .pill {
display: inline-block;
border: 0.25cqw solid var(--black);
background: var(--white);
box-shadow: 0.4cqw 0.4cqw 0 var(--black);
padding: 0.5cqw 1.2cqw;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 1cqw;
margin-bottom: 2cqw;
align-self: flex-start;
}
.bf1 h3 {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.04em;
line-height: 0.9;
font-size: 9.5cqw;
max-width: 60cqw;
}
.bf1 .star {
position: absolute;
top: 18%;
right: 8%;
width: 14cqw;
height: 14cqw;
background: var(--pink);
border: 0.3cqw solid var(--black);
clip-path: polygon(
50% 0%,
61% 35%,
98% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
2% 35%,
39% 35%
);
z-index: 2;
}
.bf1 .rect {
position: absolute;
bottom: 16%;
right: 16%;
width: 16cqw;
height: 9cqw;
background: var(--blue);
border: 0.3cqw solid var(--black);
box-shadow: 0.6cqw 0.6cqw 0 var(--black);
transform: rotate(-6deg);
z-index: 1;
}
/* f2 feature cards */
.bf2 {
background: var(--blue);
}
.bf2 .body {
padding: 5cqw;
display: flex;
flex-direction: column;
}
.bf2 .pill {
align-self: flex-start;
border: 0.2cqw solid var(--black);
background: var(--white);
box-shadow: 0.3cqw 0.3cqw 0 var(--black);
padding: 0.4cqw 1cqw;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.85cqw;
margin-bottom: 1.4cqw;
}
.bf2 h3 {
font-family: var(--disp);
font-weight: 800;
text-transform: uppercase;
letter-spacing: -0.02em;
font-size: 3.6cqw;
margin-bottom: 2cqw;
}
.bf2 .row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.6cqw;
flex: 1;
}
.bf2 .c {
border: 0.3cqw solid var(--black);
background: var(--white);
box-shadow: 0.6cqw 0.6cqw 0 var(--black);
padding: 1.8cqw;
}
.bf2 .c .ic {
width: 4cqw;
height: 4cqw;
border: 0.2cqw solid var(--black);
display: grid;
place-items: center;
font-family: var(--disp);
font-weight: 700;
font-size: 1.8cqw;
margin-bottom: 1.2cqw;
}
.bf2 .c .t {
font-family: var(--disp);
font-weight: 700;
text-transform: uppercase;
font-size: 1.5cqw;
line-height: 1.15;
}
.bf2 .c .bd {
font-family: var(--disp);
font-weight: 500;
font-size: 0.95cqw;
line-height: 1.5;
color: #222;
margin-top: 0.6cqw;
}
/* f3 stat grid (tilted) */
.bf3 {
background: var(--green);
}
.bf3 .body {
padding: 5cqw;
display: flex;
flex-direction: column;
}
.bf3 .pill {
align-self: flex-start;
border: 0.2cqw solid var(--black);
background: var(--white);
box-shadow: 0.3cqw 0.3cqw 0 var(--black);
padding: 0.4cqw 1cqw;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.85cqw;
margin-bottom: 1.4cqw;
}
.bf3 h3 {
font-family: var(--disp);
font-weight: 800;
text-transform: uppercase;
letter-spacing: -0.02em;
font-size: 3.6cqw;
margin-bottom: 2.4cqw;
}
.bf3 .row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2cqw;
}
.bf3 .sc {
border: 0.25cqw solid var(--black);
background: var(--white);
box-shadow: 0.4cqw 0.4cqw 0 var(--black);
padding: 1.8cqw;
position: relative;
}
.bf3 .sc:nth-child(odd) {
transform: rotate(-2deg);
}
.bf3 .sc:nth-child(even) {
transform: rotate(2deg);
}
.bf3 .sc .dot {
position: absolute;
top: 1cqw;
right: 1cqw;
width: 1cqw;
height: 1cqw;
border-radius: 50%;
border: 0.15cqw solid var(--black);
}
.bf3 .sc .n {
font-family: var(--disp);
font-weight: 900;
font-size: 4.6cqw;
line-height: 1;
}
.bf3 .sc .l {
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.85cqw;
margin-top: 0.6cqw;
}
/* f4 close (black) */
.bf4 {
background: var(--black);
}
.bf4 .body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.bf4 .cardf {
border: 0.3cqw solid var(--white);
box-shadow: 1.2cqw 1.2cqw 0 var(--yellow);
padding: 4cqw 6cqw;
position: relative;
}
.bf4 .pill {
border: 0.25cqw solid var(--white);
background: var(--black);
color: var(--white);
box-shadow: 0.5cqw 0.5cqw 0 var(--white);
padding: 0.5cqw 1.2cqw;
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 1cqw;
margin-bottom: 1.6cqw;
display: inline-block;
}
.bf4 h3 {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.03em;
font-size: 7cqw;
line-height: 0.95;
color: var(--white);
}
.bf4 .star {
position: absolute;
top: -3cqw;
right: -3cqw;
width: 9cqw;
height: 9cqw;
background: var(--pink);
border: 0.3cqw solid var(--white);
clip-path: polygon(
50% 0%,
61% 35%,
98% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
2% 35%,
39% 35%
);
}
/* PRINCIPLES */
.dos {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32px;
}
.do-card {
border: 4px solid var(--black);
box-shadow: 8px 8px 0 var(--black);
padding: 36px;
}
.do-card.do {
background: var(--green);
}
.do-card.dont {
background: var(--white);
}
.do-card h4 {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.02em;
font-size: 30px;
margin-bottom: 20px;
}
.do-card ul {
list-style: none;
}
.do-card li {
font-family: var(--disp);
font-weight: 500;
font-size: 15px;
line-height: 1.55;
padding-left: 26px;
position: relative;
margin-bottom: 13px;
}
.do-card li::before {
position: absolute;
left: 0;
font-family: var(--disp);
font-weight: 900;
}
.do-card.do li::before {
content: "+";
}
.do-card.dont li::before {
content: "\00d7";
}
.foot {
padding: 48px 60px;
background: var(--black);
color: var(--white);
display: flex;
justify-content: space-between;
align-items: center;
}
.foot .l {
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
letter-spacing: -0.03em;
font-size: 28px;
}
.foot .r {
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 11px;
color: #aaa;
}
@media (max-width: 1100px) {
.gallery {
grid-template-columns: 1fr;
}
.swatches {
grid-template-columns: 1fr 1fr;
}
}
</style>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap"
/>
<style id="ft-override">
:root {
--primary: #fffdf5 !important;
--secondary: #000000 !important;
--tertiary: #ffdc8b !important;
--accent: #fe90e8 !important;
--f-disp: "Inter", sans-serif !important;
--f-body: "Space Grotesk", sans-serif !important;
--black: #000000 !important;
--offwhite: #fffdf5 !important;
--pink: #fe90e8 !important;
--cream: #ffdc8b !important;
--disp: "Inter", sans-serif !important;
--mono: "Space Grotesk", sans-serif !important;
--white: #ffffff !important;
--blue: #c0f7fe !important;
--green: #99e885 !important;
--yellow: #f7cb46 !important;
}
</style>
</head>
<body>
<!-- COVER -->
<section class="cover">
<div class="dg"></div>
<div class="star"></div>
<div class="deco-rect"></div>
<div class="deco-rect2"></div>
<div class="inner">
<span class="pill">Frame System · Vol. 01</span>
<h1>Loud,<br />bordered,<br />a little crooked.</h1>
<div class="sub">
A maximalist neobrutalist frame system — 4px ink borders, 8px hard shadows, five candy
pastels, and tilted decorations that puncture the grid.
</div>
</div>
<div class="counter">01 / 06</div>
</section>
<!-- PALETTE -->
<section>
<div class="sec-head">
<span class="pill">Palette</span>
<h2>Five Candy Pastels</h2>
<span class="sp"></span>
</div>
<div class="swatches">
<div class="sw">
<div class="chip" style="background: var(--pink)"></div>
<div class="meta">
<div class="name">Pink</div>
<div class="hex">#FE90E8</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--blue)"></div>
<div class="meta">
<div class="name">Blue</div>
<div class="hex">#C0F7FE</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--green)"></div>
<div class="meta">
<div class="name">Green</div>
<div class="hex">#99E885</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--yellow)"></div>
<div class="meta">
<div class="name">Yellow</div>
<div class="hex">#F7CB46 · CTA</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--cream)"></div>
<div class="meta">
<div class="name">Cream</div>
<div class="hex">#FFDC8B</div>
</div>
</div>
<div class="sw">
<div
class="chip"
style="background: var(--offwhite); border-bottom: 4px solid var(--black)"
></div>
<div class="meta">
<div class="name">Off-White</div>
<div class="hex">#FFFDF5 · canvas</div>
</div>
</div>
<div class="sw">
<div
class="chip"
style="background: var(--white); border-bottom: 4px solid var(--black)"
></div>
<div class="meta">
<div class="name">White</div>
<div class="hex">#FFFFFF · cards</div>
</div>
</div>
<div class="sw">
<div class="chip" style="background: var(--black)"></div>
<div class="meta">
<div class="name">Black</div>
<div class="hex">#000000 · all borders</div>
</div>
</div>
</div>
</section>
<!-- TYPOGRAPHY -->
<section>
<div class="sec-head">
<span class="pill" style="background: var(--pink)">Type</span>
<h2>Inter × Space Grotesk</h2>
<span class="sp"></span>
</div>
<div class="type-row">
<div>
<div class="tok">heading-xl</div>
<div class="m">Inter 900 · uppercase · −0.03em</div>
</div>
<div class="spec"><span class="disp" style="font-size: 80px">Maximal.</span></div>
</div>
<div class="type-row">
<div>
<div class="tok">heading-lg</div>
<div class="m">Inter 800 · uppercase · −0.02em</div>
</div>
<div class="spec">
<span class="disp" style="font-weight: 800; font-size: 50px">Bordered & Bold</span>
</div>
</div>
<div class="type-row">
<div>
<div class="tok">stat-number</div>
<div class="m">Inter 900 · line 1</div>
</div>
<div class="spec">
<span class="disp" style="font-size: 64px; letter-spacing: -0.02em">240</span>
</div>
</div>
<div class="type-row">
<div>
<div class="tok">body</div>
<div class="m">Inter 500 · sentence case · line 1.6</div>
</div>
<div class="spec">
<p style="font-weight: 500; font-size: 18px; line-height: 1.6; max-width: 640px">
Body runs Inter at weight 500, sentence case — the calm against the heavy uppercase
display.
</p>
</div>
</div>
<div class="type-row" style="border-bottom: 0">
<div>
<div class="tok">label</div>
<div class="m">Space Grotesk 600 · uppercase · 0.08em</div>
</div>
<div class="spec"><span class="pill">Section Eyebrow</span></div>
</div>
</section>
<!-- COMPONENTS -->
<section>
<div class="sec-head">
<span class="pill" style="background: var(--blue)">Components</span>
<h2>Borders & Hard Shadows</h2>
<span class="sp"></span>
</div>
<div class="cgrid">
<div class="s4">
<div class="cnote">Feature card + notch + icon</div>
<div class="fcard">
<div class="notch"></div>
<div class="ic" style="background: var(--pink)">A</div>
<div class="t">Bordered Block</div>
<div class="bd">4px ink border + 8px hard offset shadow, square corners.</div>
</div>
</div>
<div class="s4">
<div class="cnote">Stat cards (tilted) + deco dot</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px">
<div class="statcard" style="transform: rotate(-2deg)">
<div class="dot" style="background: var(--pink)"></div>
<div class="n">240</div>
<div class="l">Output</div>
</div>
<div class="statcard" style="transform: rotate(2deg)">
<div class="dot" style="background: var(--blue)"></div>
<div class="n">3.4×</div>
<div class="l">Faster</div>
</div>
</div>
</div>
<div class="s4">
<div class="cnote">CTA · list number · stripe</div>
<span class="btn">Click Here →</span>
<div style="display: flex; align-items: center; gap: 14px; margin-top: 24px">
<span class="listnum">01</span
><span style="font-family: var(--disp); font-weight: 500; font-size: 15px"
>Numbered list bullet</span
>
</div>
<div class="stripe" style="margin-top: 24px"></div>
</div>
</div>
<div class="cgrid" style="margin-bottom: 0">
<div class="s6">
<div class="cnote">Label pills — pastel variants</div>
<div style="display: flex; gap: 16px; flex-wrap: wrap">
<span class="pill" style="background: var(--pink)">Pink</span
><span class="pill" style="background: var(--blue)">Blue</span
><span class="pill" style="background: var(--green)">Green</span
><span class="pill" style="background: var(--yellow)">Yellow</span
><span class="pill" style="background: var(--cream)">Cream</span>
</div>
</div>
<div class="s6">
<div class="cnote">Icon squares + star burst</div>
<div style="display: flex; align-items: center; gap: 20px">
<span
style="
width: 64px;
height: 64px;
border: 3px solid var(--black);
background: var(--green);
display: grid;
place-items: center;
font-family: var(--disp);
font-weight: 700;
font-size: 26px;
"
>B</span
>
<span
style="
width: 64px;
height: 64px;
border: 3px solid var(--black);
background: var(--yellow);
display: grid;
place-items: center;
font-family: var(--disp);
font-weight: 700;
font-size: 26px;
"
>C</span
>
<span
style="
width: 64px;
height: 64px;
background: var(--pink);
border: 3px solid var(--black);
clip-path: polygon(
50% 0%,
61% 35%,
98% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
2% 35%,
39% 35%
);
"
></span>
</div>
</div>
</div>
</section>
<!-- COMPOSITIONS -->
<section>
<div class="sec-head">
<span class="pill" style="background: var(--yellow)">Frames</span>
<h2>Frame Compositions</h2>
<span class="sp"></span>
</div>
<div class="gallery">
<div class="fw">
<div class="flabel"><b>Cover</b><span>· identity · cream · decorations · left</span></div>
<div class="frame bf1">
<div class="dg"></div>
<div class="star"></div>
<div class="rect"></div>
<div class="body">
<span class="pill">Frame System</span>
<h3>Loud, bordered, crooked.</h3>
</div>
</div>
</div>
<div class="fw">
<div class="flabel"><b>Feature Cards</b><span>· catalog · blue ground · 3-up</span></div>
<div class="frame bf2">
<div class="body">
<span class="pill">Three Laws</span>
<h3>Cards on Grounds</h3>
<div class="row">
<div class="c">
<div class="ic" style="background: var(--pink)">A</div>
<div class="t">Border</div>
<div class="bd">4px ink on all.</div>
</div>
<div class="c">
<div class="ic" style="background: var(--green)">B</div>
<div class="t">Shadow</div>
<div class="bd">8px hard offset.</div>
</div>
<div class="c">
<div class="ic" style="background: var(--yellow)">C</div>
<div class="t">Tilt</div>
<div class="bd">A little crooked.</div>
</div>
</div>
</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Stat Grid</b><span>· data · green ground · tilted · the dense frame</span>
</div>
<div class="frame bf3">
<div class="body">
<span class="pill">By The Numbers</span>
<h3>The Tally</h3>
<div class="row">
<div class="sc">
<div class="dot" style="background: var(--pink)"></div>
<div class="n">240</div>
<div class="l">Output</div>
</div>
<div class="sc">
<div class="dot" style="background: var(--blue)"></div>
<div class="n">3.4×</div>
<div class="l">Faster</div>
</div>
<div class="sc">
<div class="dot" style="background: var(--yellow)"></div>
<div class="n">12K</div>
<div class="l">Shipped</div>
</div>
</div>
</div>
</div>
</div>
<div class="fw">
<div class="flabel">
<b>Closing Plate</b><span>· closer · black ground · yellow shadow</span>
</div>
<div class="frame bf4">
<div class="body">
<div class="cardf">
<div class="star"></div>
<span class="pill">The End</span>
<h3>Stay loud.</h3>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- PRINCIPLES -->
<section style="border-bottom: none">
<div class="sec-head">
<span class="pill" style="background: var(--green)">Rules</span>
<h2>Frame Rules</h2>
<span class="sp"></span>
</div>
<div class="dos">
<div class="do-card do">
<h4>Do</h4>
<ul>
<li>4px borders + 8px shadows on primary cards; 3px + 4px on chrome.</li>
<li>Cycle pastel grounds across frames; keep the rhythm.</li>
<li>Inter 800–900 uppercase, negative tracking, for all display.</li>
<li>Open every region with a label-pill eyebrow.</li>
<li>Tilt decorations ±2°–12°; add one to every frame.</li>
</ul>
</div>
<div class="do-card dont">
<h4>Don't</h4>
<ul>
<li>No rounded corners (save the stat-deco dot); no blurred shadows.</li>
<li>No colored borders (black only, save the close-frame white).</li>
<li>No sentence-case Inter display; no sixth pastel.</li>
<li>No label rendered as plain text — pill or nothing.</li>
<li>Don't blow a headline edge-to-edge — fit to measure.</li>
</ul>
</div>
</div>
</section>
<!-- CAPTIONS — preview of caption-skin.html (the preset's lower-third karaoke look) -->
<style>
/* mirrors caption-skin.html; the skin's --cap-* vocab mapped to this preset's tokens */
.capdemo {
--cap-ink: var(--black);
--cap-canvas: var(--white);
--cap-accent: var(--yellow);
--cap-accent-2: var(--cream);
}
.capdemo .stage {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 16.67cqh;
display: flex;
align-items: center;
justify-content: center;
z-index: 4;
}
.capdemo .pill {
max-width: 80cqw;
padding: 1.4cqw 3cqw 1.7cqw;
background: var(--cap-canvas);
border: 0.4cqw solid var(--cap-ink);
border-radius: 0;
box-shadow: 0.8cqw 0.8cqw 0 var(--cap-ink);
}
.capdemo .line {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.12em 0.32em;
font-family: var(--disp);
font-weight: 900;
text-transform: uppercase;
font-size: 3.2cqw;
line-height: 1.12;
letter-spacing: -0.02em;
}
.capdemo .w {
display: inline-block;
padding: 0 0.1em;
color: color-mix(in srgb, var(--cap-ink) 40%, var(--cap-canvas));
}
.capdemo .w.spoken {
color: var(--cap-ink);
}
.capdemo .w.active {
color: var(--cap-ink);
background: var(--cap-accent);
box-shadow: 0 0 0 0.08em var(--cap-accent);
}
.capdemo .bandline {
position: absolute;
left: 0;
right: 0;
bottom: 16.67cqh;
border-top: 0.15cqw dashed rgba(0, 0, 0, 0.4);
z-index: 3;
}
.capdemo .bandtag {
position: absolute;
right: 3cqw;
bottom: calc(16.67cqh + 0.7cqw);
font-family: var(--mono);
font-weight: 600;
text-transform: uppercase;
font-size: 0.9cqw;
letter-spacing: 0.08em;
color: var(--black);
opacity: 0.55;
z-index: 4;
}
</style>
<section>
<div class="sec-head">
<span class="pill" style="background: var(--yellow)">Captions</span>
<h2>Lower-Third Karaoke</h2>
<span class="sp"></span>
</div>
<div class="gallery">
<div class="fw">
<div class="flabel"><b>Active line</b><span>· spoken · current · upcoming</span></div>
<div class="frame capdemo" style="background: var(--cream)">
<div
class="dg"
style="
position: absolute;
inset: 0;
opacity: 0.3;
background-image: radial-gradient(
circle,
var(--black) 0.15cqw,
transparent 0.15cqw
);
background-size: 3cqw 3cqw;
z-index: 1;
"
></div>
<div class="bandline"></div>
<div class="bandtag">caption band · 16.7%</div>
<div class="stage">
<div class="pill">
<div class="line">
<span class="w spoken">Built</span>
<span class="w spoken">for</span>
<span class="w active">teams</span>
<span class="w">who</span>
<span class="w">ship.</span>
</div>
</div>
</div>
</div>
</div>
<div class="fw">
<div class="flabel"><b>Settled line</b><span>· all spoken · stamp cleared</span></div>
<div class="frame capdemo" style="background: var(--blue)">
<div class="stage">
<div class="pill">
<div class="line">
<span class="w spoken">Ship</span>
<span class="w spoken">faster</span>
<span class="w spoken">today.</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="foot">
<span class="l">BlockFrame</span>
<span class="r">Frame Showcase · Atoms Sacred · Composition Free</span>
</div>
<template id="__bundler_thumbnail">
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="#FFFDF5" />
<rect x="16" y="34" width="46" height="32" fill="#FE90E8" stroke="#000" stroke-width="4" />
<rect x="22" y="40" width="46" height="32" fill="#C0F7FE" stroke="#000" stroke-width="4" />
<path
d="M74 16l4 11h12l-9 7 3 11-10-6-10 6 3-11-9-7h12z"
fill="#F7CB46"
stroke="#000"
stroke-width="2"
/>
</svg>
</template>
</body>
</html>
BlockFrame — Frame (video / frame layer)
Overview
BlockFrame at frame scale is a maximalist neobrutalist system on five laws: every region has a 4px black border, every elevated element an 8px hard offset shadow, every corner square, every accent a saturated pastel, and every layout allowed to be a little crooked. The joy is the deliberate collision — bordered cards meeting bordered cards, shadows stacking, tilted decorations puncturing the grid.
The voice is Inter at weight 800–900 in tight uppercase with negative tracking (display) + weight 500 sentence body, and Space Grotesk weight 600 uppercase 0.08em as the label/chrome voice. Five candy pastels (pink/blue/green/yellow/cream) cycle as full-bleed grounds across frames — the color cycling is the primary rhythm. Depth is hard offset shadow (8px/4px, solid black, zero blur, bottom-right); the close-frame's 12px yellow shadow is the one colored exception.
Key characteristics at frame scale:
- 4px black borders + 8px hard shadows on primary cards; 3px + 4px on chrome (weights coupled).
- Five-pastel palette cycled as full-bleed grounds; black/white/off-white structural.
- Inter 800–900 uppercase negative-tracked display; Space Grotesk label chrome.
- Square corners everywhere (only the stat-deco dot is round); tilted decorations puncture the grid.
- Label-pills open every region; star bursts, stripe blocks, dot grids are reusable attention units.
- Comfortably dense — packed reads as authoritative; empty corners read as broken.
The Frame
Frame Craft Bar
Three eyeball tests gate every frame before any structural check:
- Squint — one Inter display moment dominates at 3–6× its neighbor; cards read as a system, not rivals.
- Silence — cover/quote/close keep air (decorations, not content); the feature-card and stat grids are the dense exception.
- Restraint — the 4px→08px / 3px→4px coupling holds; black borders only (white on close); pastel ground cycles one per frame; no sixth pastel.
- Reference — aim at a zine / 1990s sticker-book / toy-packaging spread (bordered blocks, tilted decorations); failure looks like a flat, borderless, blurred-shadow web card grid.
- Primary: 1920×1080 (16:9). Display authored in `cqw` (
px ÷ 1920 × 100 = cqw). - Vertical: 1080×1920 (9:16). Square: 1080×1080 (1:1).
- Safe area:
slide-pad~3.1cqw; decorations may bleed off edges.
The container law (load-bearing). Every frame ground sets container-type: size; ALL frame-relative units are cqw/cqh against it — never vw. Borders/shadows scale in cqw so the 4px↔8px coupling holds proportionally; corners stay square.
Colors
Tokens identical to the source. {colors.offwhite} is the default ground, but frames cycle through {colors.cream}/blue/pink/green/yellow grounds — the cycle is the rhythm. {colors.black} is every border + structural text; {colors.white} is card fills. The five pastels are interchangeable with no semantic meaning — pair by juxtaposition (pink+blue+green trio, cream+yellow warm pair). {colors.yellow} is the CTA + the one colored (close) shadow; {colors.black} ground is the close surface. No sixth pastel.
Typography
Two ramps. The reading/chrome ramp (Inter body 0.95cqw weight 500, Space Grotesk labels in px) carries copy + chrome; the display ramp (Inter heading-md 2.1cqw → heading-xl 5cqw, weight 800–900 uppercase) carries every headline + stat.
- Legibility floor: any load-bearing line ≥ 1.4cqw; px labels are chrome only.
- Fit-to-measure: size the headline to its length. Cap the block at ≤ 78cqw; ≤3 words →
heading-xl; 4–6 →heading-lg; 7+ →heading-md. - Inter display is uppercase, weight 800–900, negative-tracked (−0.01..−0.03em); body weight 500 sentence case; Space Grotesk labels uppercase 0.08em. No sentence-case display, no untracked display.
Depth & Surface
Hard offset shadow, solid black, zero blur, bottom-right:
- 0.8cqw (8px) primary cards; 0.4cqw (4px) chrome; 0.6cqw (6px) hover.
- Border-based depth — the 4px/3px borders do much of the lift; shadow makes it "elevated."
- Tilt — ±2°–12° rotation breaks the grid for perceived dimension.
- Inverted close — 12px YELLOW shadow (and 6px white on close-btn) — the only colored shadows.
Ceiling: no blurred shadow, no rounded corner (save the stat-deco dot), no gradient depth.
Shapes
- 0 radius everywhere except the 12px stat-deco dot (50%). Square corners are the structural identity.
Components
- card-elevated / card-small — the bordered+shadowed content cards (weight-coupled).
- label-pill — the universal eyebrow (border+shadow+pastel). button-primary — yellow CTA.
- star-burst / stripe-block / bg-dot-grid / tilt-deco — the reusable decoration units (one per frame min).
- stat-deco-dot — the lone round shape. close-frame — the inverted black+white+yellow-shadow closer.
Frame Treatments
Recipe: ground · container · composes · focal · chrome · accent · silence · Fixed/Free · density.
Cycle the ground color; add ≥1 decoration per frame; open with a label-pill.
1 · Cover (identity · move: decorations puncture · left)
Ground {colors.cream} (or offwhite) + faint dot-grid. Composes label-pill, heading-xl, tilt-deco rect, star-burst, counter. Focal a 2–3 line Inter heading-xl uppercase, left, under a label-pill; tilted pastel rects + a star burst puncture the right. Chrome counter pill. Accent the decorations' pastels. Silence right third holds decorations. Fixed 4px/8px coupling, uppercase display, square. Free title, decoration placement/colors. Density comfortable.
2 · Feature Cards (catalog · move: 3-up bordered grid · blue ground)
Ground {colors.blue}, slide-pad. Composes label-pill, heading-lg, 3× card-elevated (icon-square + card-title + body). Focal three white bordered+shadowed cards. Chrome label-pill eyebrow. Accent the pastel icon-squares (pink/green/yellow). Silence tight — dense by design. Fixed 4px border + 8px shadow, square, uppercase card-titles. Free card content, icon hues. Density dense-exception.
3 · Stat Grid (data · move: tilted stat cards · green ground)
Ground {colors.green}, slide-pad. Composes label-pill, heading-lg, 3× card-small (tilted −2/+2°, stat-deco dot, stat-number + label). Focal three tilted bordered stat cards. Accent the deco-dots' pastels + black stat numerals. Silence moderate. Fixed alternating tilt, 3px+4px, round deco-dot only. Free figures (from script), dot hues. Density dense-exception.
4 · Closing Plate (closer · move: inverted black · centered)
Ground {colors.black}. Composes close-frame (4px white border, 12px yellow shadow), label-pill (inverted), close-title, star-burst. Focal a white close-title inside the white-bordered frame with the yellow offset shadow; a pink star punctures a corner. Accent the yellow shadow + pink star. Silence ~50%. Fixed white-on-black, 12px yellow shadow (only here), square. Free sign-off, star placement. Density low.
5 · Quote (quote · move: bordered quote frame · pink ground)
Ground {colors.pink} (or offwhite). Composes label-pill, quote-text in a card-elevated, attribution. Focal an Inter quote-text (900 uppercase) inside a white bordered+shadowed frame. Accent the ground + one decoration. Silence moderate. Fixed uppercase quote, 4px/8px. Free quote, ground. Density comfortable.
6 · Timeline (process · move: stepped bordered cards · offwhite)
Ground {colors.offwhite}. Composes label-pill, heading-lg, 3–4 card-small steps + step-connectors. Focal a row of bordered step cards linked by black connector bars, each a pastel + step-num. Accent the step pastels. Silence moderate. Fixed 3px+4px steps, square, connectors. Free steps, hues. Density standard.
Composition Rules
Do
- Pair 4px borders with 8px shadows, 3px with 4px — the coupling is non-negotiable.
- Cycle pastel grounds across frames; keep the deck visually rhythmic.
- Set Inter display uppercase, 800–900, negative-tracked; open every region with a label-pill.
- Render shadows solid black, zero blur, bottom-right; add ≥1 decoration (tilt/star/stripe/dots) per frame.
- Use yellow for CTAs; use the inverted black + 12px yellow shadow close-frame for the closer.
- Tilt decorations ±2°–12°; lean left on most frames.
Don't
- No rounded corners (save the stat-deco dot); no blurred shadows.
- No colored borders (black only, save the close-frame white); no sixth pastel.
- No sentence-case Inter display; no untracked display; no label as plain text.
- Don't keep everything perfectly aligned (tilt is the signature); don't leave corners empty.
- Don't blow a headline edge-to-edge — fit to measure.
Aspect-Ratio Behavior
| Treatment | 16:9 | 9:16 | 1:1 |
|---|---|---|---|
| Cover | title left, decorations right | title top, decorations below | title, fewer decorations |
| Feature Cards | 3 across | 3 stacked | 2+1 |
| Stat Grid | 3 tilted across | 3 tilted stacked | 2×2 |
| Closing | centered close-frame | centered, taller | centered |
| Quote | quote frame | quote stacked | centered |
| Timeline | horizontal steps | vertical (connectors hidden) | 2×2 |
slide-pad holds on the short edge; re-step display above the 1.4cqw floor. Decoration count drops on tighter ratios so the frame stays bordered, not cluttered.
Approved Entities
No real customers, logos, or vendors are defined in the source — render any such mark as a placeholder. Pastel fills, decorations, and tilts are content-agnostic.
Numerals & Claims (hard rule)
Never invent figures, stats, or counts at frame scale. Render slots as — figure —, {metric}, N×. Stat cards and charts carry placeholders until the script supplies values. Slide counters and list numbers are decorative.
Pre-Render Self-Audit
- Squint — one Inter display moment dominates; cards read as a system.
- Silence — only feature/stat grids run dense; cover/quote/close keep air with decorations.
- Borders/shadows — 4px↔8px / 3px↔4px coupling holds; solid black, zero blur.
- Color — pastel ground cycles; black borders only (white on close); no sixth pastel.
- Type — Inter uppercase 800–900 negative-tracked, fit-to-measure; Space Grotesk labels 0.08em; ≥1.4cqw floor.
- Shape — square corners (only stat-deco dot round); ≥1 tilt/decoration per frame.
- Fabrication — every numeral traces to the script, else placeholder.
Known Gaps
- Motion intentionally out of scope. frame.md specifies composition only; the source toggles slides via display, no transition.
- Inter + Space Grotesk via Google Fonts. CJK: Noto Sans SC 900 (sentence case — the uppercase signal drops); lean harder on borders/shadows/decoration to carry the brutalist identity.
- 9:16 / 1:1 are guidance; verify the floor and that decoration count scales down.
- Star bursts (clip-path), stripe blocks, dot grids, and tilts are CSS-only; no external imagery is required.
Bold / Energetic
Product launches, social media, announcements, high-energy content.
#FFBE0B #FB5607 #FF006E #8338EC #3A86FF
#F72585 #7209B7 #3A0CA3 #4361EE #4CC9F0
#EF476F #FFD166 #06D6A0 #118AB2 #073B4C
#FF595E #FFCA3A #8AC926 #1982C4 #6A4C93
#9B5DE5 #F15BB5 #FEE440 #00BBF9 #00F5D4
#390099 #9E0059 #FF0054 #FF5400 #FFBD00
#3D348B #7678ED #F7B801 #F18701 #F35B04
#FFBC42 #D81159 #8F2D56 #218380 #73D2DEClean / Corporate
Explainers, tutorials, presentations, professional content.
#FFFCF2 #CCC5B9 #403D39 #252422 #EB5E28
#22223B #4A4E69 #9A8C98 #C9ADA7 #F2E9E4
#3D5A80 #98C1D9 #E0FBFC #EE6C4D #293241
#2B2D42 #8D99AE #EDF2F4 #EF233C #D90429
#353535 #3C6E71 #FFFFFF #D9D9D9 #284B63
#E7ECEF #274C77 #6096BA #A3CEF1 #8B8C89
#CFDBD5 #E8EDDF #F5CB5C #242423 #333533
#2F6690 #3A7CA5 #D9DCD6 #16425B #81C3D7Related skills
How it compares
Use hyperframes-creative for brand and narration specs; use hyperframes-animation when the task is motion rules and runtime APIs.
FAQ
What does hyperframes-creative cover?
hyperframes-creative covers non-animation direction: design spec handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand decisions for HyperFrames videos.
How does hyperframes-creative relate to hyperframes-animation?
hyperframes-creative owns brand and design specs; hyperframes-animation owns atomic motion rules, scene blueprints, and runtime adapters. Use both after hyperframes-core defines composition structure.
Is Hyperframes Creative safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.