Skip to content

Reading a Reference

A UI principle for coding agents. Also covers reference site, reference screenshot, how was this built, reverse engineer an effect, explain interface, layer stack, and 3 more.

Show all 9 aliases

reference site, reference screenshot, how was this built, reverse engineer an effect, explain interface, layer stack, measured derived inferred, screenshot reconstruction, brand reference

A user who hands you a site or a screenshot is showing you what they want. Two different things are worth reading from it, and they need different tools. The brand is the tokens: colours, fonts, radii, shadows. extract-from-url and extract-design-system read those and return an oklch token sheet that create-design-system can build from. The rest is technique: how the hero gradient is built, why the cards feel lifted, how the list animates in. A token sheet has no slot for any of that, so this doc covers how to read it honestly and carry it across.

Read the Brand With the Extractor, Not by Eye

Run the extractor first. It fetches the stylesheets, renders the page, samples what the role elements actually look like, and merges both with the rendered truth last. That beats reading variable names, and it beats guessing a hex value from a screenshot. Pass the fenced sheet it returns to create-design-system as brandCss, verbatim.

Then say what the sheet does not carry. It records the palette, the type, the radius and the shadow scale. It does not record an oversized blurred gradient behind the header, a frosted panel, a glow, a stagger on load, or an easing curve. Those are the parts a user usually points at when they say "like this", so read them separately with the method below.

Find the Layers, Not the Element

Ask what makes a gradient and the answer is almost never one declaration. Visual effects are stacks, and the stack is the explanation.

A hero gradient is commonly four things at once:

  • An element oversized past its container and pushed partly outside it, so no edge is ever visible.
  • A multi-stop gradient at low alpha, often four stops around 20% opacity.
  • A large filter: blur(), which turns the discrete stops into a wash.
  • Sometimes a layer above with backdrop-filter, which frosts whatever shows through.

Report the stack in paint order with the declaration doing the work on each layer. A reader who has the stack understands the effect. A reader given only the linear-gradient() does not, because the blur and the oversize produce most of what they were looking at.

Three things hide the stack. Pseudo-elements carry effects more often than elements do, so read ::before and ::after on every candidate. Idle values such as filter: blur(0px) and transform: none are what an animation library leaves behind, not effects, so filter them out. A generated list of twelve gradient stops is one technique: a utility that follows an easing curve to stop banding. Name the technique rather than pasting the stops.

Do not read paint order from vertical position or z-index. Equal or auto z-indices fall back to DOM order, and a z-index only compares with others inside its own stacking context. document.elementsFromPoint(x, y) returns the real front-to-back stack at one pixel.

Explain the Mechanism, Not the Readout

A table of measured values is not an explanation. Each layer needs the technique that produces it and the perceptual job it does.

Take an opacity that runs from 0 to 0.85 by the 20% mark and reaches 1 at the end of 1500ms. That is the readout. The explanation is that 85% of the fade lands in the first 300ms and the last 15% takes the remaining 1200ms, so the layer arrives at once and never reads as finished. A linear fade over the same duration cannot do that.

What you read is compiled output, not what the author wrote. Three animations on one element, one each for opacity, filter, and transform, is what a stagger helper compiles to. Name the technique and keep the artifact as its evidence. Chasing the library name is the wrong thing to do: a bundled build exposes no global, and the technique transfers to any library while the name transfers to none.

Measured, Derived, Inferred

Every claim carries one of three tiers, stated rather than implied:

TierMeansExample
MeasuredRead off the page or sampled from pixels. Reproducible.filter: blur(50px), --radius: 0.625rem
DerivedComputed from measurements."Four stops, evenly spaced to 100%", "1496px wide in a 1440px viewport"
InferredA judgement about intent. Never stated as fact."Oversized so no edge lands inside the viewport"

Inventing a plausible value and presenting it as measured is the one failure that makes the whole reading worthless. "Roughly 50px of blur, unmeasured" is useful. A box-shadow you made up because it looks right is not.

A Screenshot Is a Reconstruction

Without the page there is no code to read, so the answer changes in kind. You are proposing how it could be built to look like that, and you say so.

Exact from pixelsRatios onlyUnavailable
Colour valuesType sizesTokens and their names
Contrast between any two sampled coloursSpacing valuesThe stack and styling system
Which colours repeat and whereRadii, stroke and border weightsBreakpoints, motion, easing, every state but the captured one

You do not know the capture's scale, so never report a px size from an image alone. Where the image contains body copy, assume 16px, express everything as a multiple of it, and say you did. Sample colours from the pixels rather than describing them. Convert them to oklch. Measure contrast on every pair you can isolate: that number is exact, and it is the most valuable one an image gives you. Read type by category, geometric sans or humanist or slab, never by name. Where the page is live, ask for the URL, because one measurement replaces the whole estimate.

Close on What Transfers

Do not end with code that rebuilds the effect. Anything assembled from compiled output is a lookalike, and whoever pastes it inherits values tuned to a viewport, a token set, and a typeface they do not have.

Close on the recipe in words: the layers, their order, and the one or two values doing the perceptual work. Then build it here from the principle that owns the technique, with the user's own tokens: depth for shadows and lift, reveal-techniques and motion-orchestration for entrances, edge-fades and ripple-refraction for the softer surface effects, color for the palette.

Name what would not survive being copied: a licensed typeface, a brand hue the user has no right to, a pre-rendered raster shadow, a blur radius tuned to a width you cannot see. And name what you could not read at all. A cross-origin stylesheet, a canvas, or a WebGL shader is an honest stopping point.

Checklist

  • The extractor read the brand; nobody guessed a hex value from a screenshot
  • Every effect the user pointed at is reported as a layer stack in paint order
  • Every claim carries Measured, Derived, or Inferred
  • A screenshot answer says it is a reconstruction and names what it could not know
  • The closing recipe is in words, built here from the owning principle and the user's tokens
  • Anything that would not transfer, or could not be read, is named

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-ui-principle({ topic: "reading-a-reference" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems