Axıs
技能目录

Visual Design System — AI Weekly

Visual Design System — AI Weekly

The rationale behind assets/project-template/src/theme.ts. Read this before changing any token; most visual bugs come from editing one scene instead of the theme.

1. Design intent

"Broadcast telemetry." A calm, dark, high-contrast stage where exactly one element is allowed to shout: the accent color. Everything else recedes. This is what makes a 3-second card feel readable at a glance on a phone screen.

Three rules govern every frame:

  1. One accent — #00D4FF marks keywords, the active node, and nothing else.
  2. Contrast hierarchy — white for claims, #9AA7BD for supporting detail, #2A3654 for inactive structure. Never introduce a fourth text color.
  3. Motion is directional — content enters from where the eye already is (headline from the left, meta from below), so movement reads as orientation.

2. Color

bgTop     #14213D   radial hotspot, top-left third
bgMid     #0A0F1E   dominant field
bgBottom  #05080F   vignette floor
text      #FFFFFF   headlines
textDim   #9AA7BD   summaries, sources
accent    #00D4FF   keywords, active node, CTA emphasis
nodeDim   #2A3654   inactive timeline

The background is a single shared gradient (BG) reused by every scene. This is deliberate: with hard cuts between scenes, an identical backdrop makes the cut invisible, so the five cards read as one continuous program.

Why #00D4FF: at ~9:1 contrast on #0A0F1E it stays legible on a phone, and its cyan hue is distinct from the warm emoji glyphs so the two never compete.

3. Typography

Font stack 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif — Inter for Latin/numerals, PingFang SC for Chinese. Do not rely on emoji fonts beyond the Apple Color Emoji set; pick glyphs inside that range or they render as tofu.

Role Size Weight Notes
Hero (intro) 168 900 Single line, overshoots to 1.08 then settles
News headline 64 800 max 2 lines, **kw** at accent + 800
Summary 34 400 1–2 lines, textDim
Meta (source·date) 28 500 bottom-fading, textDim with accent dot
Eyebrow 30 700 accent, letterSpacing: 6
CTA 130 900 breathing scale

Line height on headlines is 1.22 — CJK glyphs need the extra leading or multi-line headlines look cramped at this size. Do not tighten it.

4. Layout grid

0                    230            360                    1400   1920
│  timeline spine    │  content column              │ emoji │
  • Timeline spine at x=230, nodes at y = 190/365/540/715/890.
  • Text column: left: 360, right: 520 → ~1040px usable. The reserved 520px is what guarantees the headline can never collide with the emoji.
  • Emoji: 300px circle at right: 130, glyph 170px.
  • Safe margins: 120px horizontal, 80px vertical (keeps text clear of platform UI overlays on mobile re-crops).

Overflow rule: headline visual width ≤ 92 CJK units. The validator enforces this; exceeding it is a hard error, not a warning, because a 3-line headline would push the summary out of the safe area.

5. Motion vocabulary

Named springs in theme.ts (SPRING.hero / pop / glide) keep animation declarative. Guidelines:

  • Spring for entrances that should feel physical — hero drop, emoji pop.
  • Interpolate for everything linear — opacity, slides, glow ramps.
  • Always clamp both ends of interpolate; unclamped values blow up outside the range and produce NaN transforms.
  • No CSS transitions or keyframes. All motion derives from useCurrentFrame(). This is what makes rendering frame-accurate and reproducible — CSS animation timings drift between renders.

Per-card beat sheet (90 frames @ 30fps)

Beat Frames Motion
node ignite 0–24 glow ramp 0→1
headline 0–18 translateX(-140 → 0) + fade
summary 8–26 translateX(-90 → 0) + fade (staggered +8)
meta 24–46 translateY(34 → 0) + fade
emoji 0–~20 spring pop, scale 0→1
fade out 82–90 opacity → 0

The stagger (0 / +8 / +24) creates a cascade that reads as deliberate pacing rather than everything appearing at once.

6. Frame budget arithmetic

intro(60) + N × news(90) + outro = TOTAL

For N=5 this is 60 + 450 + 90 = 600 frames = 20.0s exactly. The user's brief (2 + 15 + 2) sums to 19s; the extra second is intentionally given to the outro so the CTA breathing gets a full cycle and the declared duration matches the real file. For N≠5, content.ts recomputes the outro rather than letting the duration drift.

Always verify with ffprobe after rendering. render.sh does this automatically and fails the job on any mismatch in width/height/duration.

7. Extending the system

  • New variant / re-skin → edit theme.ts only.
  • New scene type → compose from existing tokens; never introduce raw hex or magic pixel values in a scene file.
  • Aspect ratios (9:16, 1:1) → change COMP.width/height and re-derive LAYOUT; keep the ratio of contentRight to width roughly constant.