Design Archive
中EN

Translating design constraints into CSS front-end can use as is

Front-end had no design tool account and could not see the constraints in the design file, and constraints only say "pinned to which edge", never why. I wrote a handoff sheet with a live preview for two title bars, so front-end can check their work by dragging the width.

L.13 / 14
CATEGORY
Study
YEAR
2026
STATUS
Note
TOOLS
css / flexbox / svg

entryWrite-up

On a 1920×1080 data screen, every chart card has a title bar along its top, made of a gradient plate, a top line with bright points at both ends, a short glowing bottom line, a double-arrow icon and a line of title text; later a second style was added for the top of an assistant panel, with an avatar, a subtitle, a button and a close icon. Components like this have many decorative layers, and when the card’s width changes, each layer behaves differently: some stretch, some move with the right edge, some stay put. In the design tool each layer is told which edge it is pinned to (a setting called constraints), and these relationships were all set; the question was how to hand those settings to front-end. At first this seemed to be only a matter of how to export, but in fact three things were stacked together. First, front-end has no design tool account and cannot see the design: dev mode, the inspect panel and node links do not exist for them, so the deliverable has to carry all the information itself. Second, constraints have only four meanings (pin to the start edge, pin to the end edge, keep the distance to the center, pin both edges) and cannot express design intent: they do not say whether the bottom line should keep a fixed width or scale proportionally when the card widens, nor that the title is a dynamic field that can be long or short; left unwritten, everyone on front-end will write it their own way. Third, the design itself has places that look right but are not exact in numbers, such as the reference for vertical centering or whether button padding includes the stroke; the eye cannot see them in the design tool, and in CSS they become 1 to 2px of error. The handoff sheet went through five versions, and nearly every one came from the same follow-up question: with this in hand, can front-end build it without asking me?

How the five versions evolved.

The first version walked the layers one by one through the design tool’s interface and read out all of each layer’s anchoring, position, size, fill, stroke, effects, text properties and vector path data, putting dozens of properties into one table to avoid missing any. The key practice this version settled was that the handoff sheet itself is written in the CSS being delivered. The title bar on the page is not a screenshot but the real .bar component, with a width slider next to it; drag the slider and you can see the plate stretch, the right end move with the right edge and the bottom line hold still.

Style A rendered at the design width of 500px.
Stretched to 760px: the plate and top line follow, the bottom line and glow stay fixed.

The second version changed the document’s tone to address front-end. The first version had a section written for myself on the delivery process, explaining how to send this out, and three consultative “items to confirm”, asking for example whether the bottom line was fixed or proportional and what was intended. But the reader of this document is front-end, not me. This version deleted the delivery process and turned every “to confirm” into an “implementation convention”, each giving its conclusion directly: implement in fixed pixels, add truncation, no overflow:hidden. Front-end just follows them with no need to wait for a reply; if the design side disagrees, the convention itself is changed, and front-end is not left to guess.

The third version removed the dependence on the design tool. I had added a “node ID” column to the table and written a line saying front-end could locate the layer directly in dev mode, and only then remembered that front-end had no account. So the whole document dropped every node ID, file link and tool term, and MIN, MAX, STRETCH and CENTER became “pin left, pin right, stretch, center”; where it had asked for the icon SVG to be exported from the design, front-end could not do that either, so the real vector path data was read out instead and inlined directly into the HTML. This version also showed, along the way, that the first version’s icon had been drawn from the thumbnail, guessed from the layer name: the “dot” in it was actually a diamond, not a circle, and it lined up as soon as the real path was swapped in. When real data exists, don’t guess, even if the guess looks about the same; when checking a deliverable, first assume the reader has no design tool access at all, and see whether the document still works.

The four anchoring meanings and the CSS for each.

The fourth version dealt with the title as a dynamic field. A follow-up question came up in review: the title text is a variable and can be long or short; had that been considered? Until then the conventions had only included a line recommending truncation. This version made truncation mandatory and put it straight into the code, giving the title flex:0 1 auto; min-width:0; text-overflow:ellipsis, and the preview page gained a text input and three presets of different lengths. This is a problem front-end often runs into: a flex item’s default minimum width is its content width, so however long the text, it does not shrink, and without min-width:0 the ellipsis never appears.

A 20-character title on a narrow 380px card: it truncates properly, with 6px to spare before the right end.

The fifth version brought in the second, panel title bar style. Comparing the two styles, the 6 decorative layers had exactly the same structure, differing only by a few pixels of vertical offset. Instead of two sets of frame code, the code pulls out one .bar base class, with 6 CSS custom properties (such as --plate-top and --cap-top) switching the offsets; the .bar--panel modifier changes only those 6 values, the height and the background. The second style had an avatar bitmap front-end could not get, so it was exported as a transparent PNG and encoded as a string of text written straight into the HTML (called a data URI), with an “Assets” tab on the page to copy it from directly; the gradient halo line around the avatar, the button icon and the close × were all turned into inline SVG paths as well.

One shared skeleton, switched by custom properties.
Style B rendered at 500px.
Style B stretched to 760px: the button and close stay right, and the text area takes all remaining width.

Reading the data showed the icon’s center at 27.45 and the title’s at 26.5, while the title bar’s geometric center is 25.5; neither matched it. Working it out made the reason clear: the plate starts at y=4, the top 4px is a transparent line area, and the plate’s center is 27.5; centering in the design meant centering on the plate, not on the whole title bar, and that 2px difference is visible to the eye. The fix is to have the content layer fill the plate area first, then set align-items:center.

Vertical centering is based on the plate, not on the whole title bar.

The button’s size was off as well. In the design tool an inner stroke is drawn inside the padding, while a CSS border is counted separately; the design gives the button’s padding as 8/16, and copied into CSS as is, it renders at 102×40 instead of 100×38. So the code rewrites the padding as the values with the stroke taken out, and adds a comment. Unless this difference is spelled out, front-end could measure the design any number of times and never find it.

The design draws the bottom line at a fixed width, but with the card stretched to 760px it covers only 41% of the width. Rather than throwing the problem back at front-end, I gave a default in the conventions first: implement in fixed pixels, and also write down the percentage it corresponds to if made proportional. That way front-end has something to build, and the design side keeps room to change it. The anchoring meanings are objective and translate into CSS without ambiguity; what needs a person to write it out separately is design intent, such as whether the bottom line scales, how long a title can get, and whether the outer layer may set overflow:hidden.

The final deliverable is a standalone HTML file: first the shared frame skeleton, with a table of the 6 decorative layers’ offsets in both styles and the shared CSS; next style A’s live preview, content positioning table and code; then the same for style B, plus an “Assets” tab; last, 6 implementation conventions, 3 general and 3 for style B only. Hovering the mouse over a table row highlights the matching layer in the preview, so when front-end sees the “bottom line” row in the table, they can match it straight to that line on the page. Before sending it out, I used a browser with no interface, run from the command line (called a headless browser), to measure element coordinates on the page automatically, and checked every layer: in style B, the avatar is at (16,20), the status dot at (56,22), the title at (78,17), the button at (336,25) with a size of 100×38, and the close icon at (472,38), all matching the design.

Hovering a table row highlights the matching layer in the preview.

processBefore & after

Anchoring meanings and CSS

Pin left
follows the start edge, width unchanged
Pin right
follows the end edge, width unchanged
Stretch
both edges pinned, width follows the container
Center
keeps its distance to the center

Values settled in the handoff sheet

Title bar height
chart card 51px · panel 92px
Vertical centering
based on the plate (starting at y=4), plate center 27.5
Button
padding 7/15, rendering at 100×38 (the design’s 8/16 includes a 1px inner stroke)
Bottom line
fixed at 312px; if made proportional, 62.4%
Avatar
96×96 transparent PNG shown at 48×48, inlined as a data URI
Title presets
3 · 11 · 20 characters; at 380px wide the ellipsis sits 6px from the right end

Centering and truncation

css
/* the content layer fills the plate area, then centers vertically */
.bar__content {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--plate-top);
  bottom: var(--plate-bottom);
  display: flex;
  align-items: center;
}
.bar__title {
  flex: 0 1 auto;
  min-width: 0; /* without this line the ellipsis never appears */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

resultTakeaways

Finding
Both title bars share one set of frame code, switched by 6 custom properties; every icon, bitmap and path is embedded in a single HTML file, and a headless browser checked layer by layer that positions match the design.
Parameters
content layer fills the plate, then align-items:center · button padding 7/15 (the design's 8/16 includes a 1px inner stroke) · title flex:0 1 auto + min-width:0 + ellipsis · bottom line fixed at 312px
Open
With the bottom line fixed at 312px, a card stretched to 760px leaves it at only 41% of the width, which looks short; the conventions keep a proportional form (62.4%) that the design side can still switch to.

logChangelog

2026-10
First entry

Related experiments

note
L.08(STUDY)2026

A hero image, from full-screen to a spec

responsivecssobject-fit+1
note
L.12(STUDY)2026

One solver, ten resolutions, no distortion

responsivecssclamp+1
note
L.01(STUDY)2026

(1) Choosing a base model

chest-xraypytorchrad-dino+1
← LabL.13 / 14
1 / 1

100%