1. What void extract measures
Headless Chromium (SwiftShader on, so WebGL/video pages don't screenshot black) at 1440×900 and 390×844. Load + wait, wheel-scroll in steps (so Lenis/ScrollTrigger fire), capture computed styles, CSS, animations, inline-style diffs, network. Output schema: packages/cli/src/extract/types.ts (DesignDNA, schema version 1). Every inferred field is { value, confidence 0–1, evidence?[] }.
2. Fields and how to use them
dna.json path |
Meaning | Use it for |
|---|---|---|
typography.display |
H1 px (+ mobile px, px/viewport ratio), weight, line-height ratio, tracking in em, transform | the single best fingerprint; map to text-display* + --weight-display + --tracking-display |
typography.roles[] |
display/heading/body/ui/mono/label → real family name (hash-cleaned), weights, classification, free, licence, alternatives[] |
font choice; never self-host non-free families |
typography.styles[], sizes[] |
text-style histogram (size, weight, lh, tracking, family, character count) | workhorse size (usually 13–14px), mono texture, positive small-size tracking |
typography.scale |
fitted base + ratio, clamp() steps | only if confidence ≥ 0.6; otherwise keep void's scale |
typography.labels |
uppercase share, label tracking, px | --label-tracking, whether mono labels are the voice |
typography.features, textWrap |
OpenType features (cv01, ss03…), balance/pretty usage |
font-feature-settings override (only if the free alternative has them) |
color.scheme, respondsToColorScheme, alternate |
dark/light default and whether a second scheme exists | theme.default in site.config |
color.roles.* |
measured bg, bgSubtle, surface, fg, fgMuted, fgSubtle, line, brand (null = monochrome), selection | ramp relationships: L steps between tiers, hairline alpha |
color.neutral |
hue, chroma, temperature (cool/warm/neutral/green) | neutral hue/chroma for the generator |
color.accent |
hue, chroma, step-9 L, text on solid | accent L and chroma (change the hue: departure) |
color.palette[] (coverage, channels, roles) |
weighted colors per channel | accent share: restrained (1 hue, few elements) vs playful |
color.bands[] |
alternating dark/light section bands | whether to use bg-bg-subtle/inverted bands |
color.pixels |
dominant colors of the full screenshot, colorfulness, imagery share | how much of the feel comes from imagery (can't copy that) |
color.tokens |
void semantic tokens generated with the source's lightness feel | already baked into direction.css |
layout.contentWidth, containers, gutter |
content width at 1440, gutter desktop/mobile | --container-page, --gutter |
layout.sectionPadding |
desktop/mobile padding values | --section-y and rhythm variation (low confidence when spacing lives on children) |
layout.nav |
height, position, blur, translucent, floating, item count | header variant |
layout.hero |
align (left/center/split), h1 px, media side, height in vh, CTA count | hero archetype |
layout.density |
text blocks per viewport → airy/balanced/dense | spacing scale feel |
layout.radius |
knob px, value histogram, pill share | --radius, pill vs rounded buttons |
layout.shadows[] (kind ring/soft/hard/inset/layered) |
shadow grammar | elevation style (draft direction.css picks one) |
layout.texture |
grain, SVG filters, blend modes, backdrop blur | --grain-opacity, header blur |
components (in brief) |
button spec: height, padding, radius, font, bg/fg, shadow, transition | Button size/variant tuning |
motion.durations, easings, properties |
CSS transition/animation inventory | personality → duration tokens |
motion.animationsOnLoad/OnScroll, scrollDriven, viewTransitions, reducedMotionQueries |
what moves and how | what to rebuild; reduced-motion coverage (0 = don't copy the behaviour) |
motion.libraries |
GSAP, ScrollTrigger, Lenis, OGL/three, Lottie, motion… | effect-downgrades table |
motion.smoothScroll |
hijacked + library | never reproduce |
motion.cursor |
custom/hidden cursor | never hide the real cursor |
motion.effects[] |
named effects with evidence and a suggested rebuild | Step 4 |
motion.personality |
label + suggested tokens + summary | design statement line 5 |
tech.transfer |
total/JS/CSS/font/image/media KB, requests; third-party KB; HTML KB; webgl | the weight comparison |
structure.sections[] |
per-section composition, counts (images, cards, logos, columns), bg, padding, crop screenshot | section plan (adapt order; our content) |
structure.headings, ctaLabels |
outline and CTA wording | structure only; never reuse the words |
direction |
recommended void direction, confidence, per-direction scores with reasons, token overrides with why | Step 3 |
warnings[] |
capture problems (timeouts, consent walls, black WebGL) | what to verify manually |
3. Reliability
| Aspect | Reliable automatically | Sanity-check | Needs your judgement |
|---|---|---|---|
| Type | families in use, sizes, weights, lh, tracking, features, font files | alias → real typeface; display style when the H1 is hidden or split into spans | which free face matches; whether a serif accent is the voice or a one-off |
| Color | frequency-ranked bg/text/border, alpha, selection, theme-color | brand accent vs product-screenshot or syntax colors; dark bands inside light pages | temperature intent; photographic color |
| Layout | container, gutter, header, section padding on , centered ratio |
rhythm when spacing lives on children/margins; grid columns | section archetypes, narrative, what the hero shows |
| Components | radius, padding, heights, shadow strings, focus ring, :active |
which button is primary; hover states | whether a recipe works out of context |
| Motion | CSS transitions/keyframes, easings, scroll timelines, WAAPI, reduced-motion count, library presence | which elements animate (inline diffs show transforms, not intent); GSAP params as sets | choreography quality; essential vs decorative |
| Imagery | formats, sizes, counts, lazy/priority, video attributes | product UI vs illustration vs photo | art direction (never replicable) |
| Performance | transfer by type, requests, third parties | full-scroll totals include lazy + tracking pixels | trade-offs (a 2 MB video may be the brand) |
4. Manual fallback (no CLI, ~15 minutes in DevTools)
Open the site at 1440×900, disable cache, scroll the whole page once, then record:
- Display tuple. Inspect the largest heading in the fold (not just
h1; it may be hidden or split): Computed →font-family,font-size,font-weight,line-height(÷ size),letter-spacing(÷ size = em). - Workhorse text. Inspect nav links, buttons, card text: size/weight/tracking; is there mono? uppercase labels + tracking?
- Fonts. Network → Font: file names reveal real faces (
HaasGrotDisp-75Bold.otf,SuisseIntl_Book); count and KB. Check the licence before mapping. - Neutral ramp.
html/bodybackground; color of H1, body paragraph, muted meta text; the most common border color (Elements → Computed → filterborder). - Accent. Primary CTA background; link color; count roughly how many elements in the fold use it;
::selection(select text). - Radius mode. Primary button, cards, inputs, images. Pill ratio.
- Shadows. Copy
box-shadowfrom button, card, menu. Note ring layers, inset highlights, negative spread, tint. - Layout skeleton. Main container max-width and padding; section padding (several sections); header height/position/blur; how many headings are centered.
- Button spec. Height, padding, radius, font size/weight, transition string,
:activetransform (Styles pane →:activetoggle). - Motion inventory. Styles:
transitionvalues andcubic-beziers; Animations panel while loading and scrolling; Sources search forgsap,ScrollTrigger,lenis,three,ogl,lottie,animation-timeline. - Reduced motion. Rendering panel → emulate
prefers-reduced-motion: reduce, reload, scroll. Does content still appear? - Weight. Network summary after full scroll: transferred KB total; filter JS, Font, Img, Media.
- Screenshots. Full-size screenshot of fold and full page (Command menu → "Capture full size screenshot") at 1440 and 390 (device toolbar).
Write the numbers into the Step 2 table in inspire and continue.
5. Pitfalls
- Preloaders hide the real fold (hellohello ~8s). Measure after content appears; never reproduce a preloader.
- Consent banners and geo redirects (Stripe served
/in) change layout and colors; dismiss first; note the region. getAnimations()misses JS motion. GSAP/Framer/Lenis/canvas are invisible to it; rely on library detection and inline-style diffs.framer-motion/startViewTransitionstrings appear inside frameworks: weak evidence of use.- Product screenshots inflate the palette. Exclude colors inside images, canvases and app mocks before choosing an accent.
- CSS-in-JS (styled-components, Framer, StyleX) isn't in fetched CSS files; counts under-report.
- Hidden or split headings: hellohello's
h1is 1×1px; Stripe's H1 contains the subhead; use the largest visible text. - Units: tracking in em, line-height as a ratio, colors in OKLCH, fluid sizes as "vw at 1440 + floor", or values won't transfer across viewports.
- Mobile timeouts on media-heavy pages: mobile values may be missing (check
warnings). - Heavy isn't premium. The best-feeling sites in the teardown were not the heaviest (Paco 367 KB, Rauno 640 KB vs Family 15.3 MB); don't import weight to import feel.