Hewyn interaction and material kit
How our surfaces are built and how they move
Every widget below is live. Press it, drag it, hold it. This page exists so a decision
about motion or material can be felt on a phone rather than argued about in a document.
Each widget is a class in hewyn-ui.css, so the dashboard, the report engine
and the purchase page adopt it by changing a class name.
One
Surface and material
Rounded boxes are the named design element, so they are a system rather than a habit. Four radii plus a pill, each with one job. Four surface behaviours, and a surface is exactly one of them. Separation comes from shadow and ground, not from a grey line around everything.
Radius scale
Pick by the job, not by the look. Five values in the whole product, and a child's radius is its parent's minus the padding between them. A sheet is a container, so it takes the container radius rather than one of its own.
999px
pills, toggles, action buttons
6px
chip, input, swatch, skeleton line
10px
control, list row, inner tile
16px
the card radius, the default
24px
sheet, container, dark hero blocks
Nested radius is optically corrected
An inner radius equals the outer radius minus the padding between them. Reuse the outer number and the inner corner looks fat and the gap looks uneven. Left is correct: a 24px container with 8px of padding holds a 16px card, which holds a 10px photo inside 6px of padding. Right repeats 24px at every level.
Correct. 24, then 16, then 10.
Wrong. 24 everywhere. The corners crowd each other and the gap reads as uneven even though it is not.
Elevation is shadow, never a border
Three levels. e1 is a single contact shadow, a hairline of depth. e2 and e3 pair that contact shadow with a wide ambient one that lifts the surface, because one blurred shadow on its own looks like a sticker. Every layer is tinted to the ink hue and sits between 4 and 18 percent, so the stack never greys the page.
Four surface behaviours
Raised
A card above the ground. White fill, ink-tinted shadow, no border. Use for anything a customer can act on or read as a unit.
Flush
Same plane as its ground. No shadow at all. Separated by fill alone. Use for grouping, context blocks, and anything that is not tappable.
Inset
Pressed into the ground. Inner shadow. Use for tracks, wells and gutters: the toggle track, the zone band, the segmented control's background.
Glass
Only ever over a photograph or over content that is still scrolling behind it. Over a flat ground it is just a dirty panel.
Hairlines sit at partial opacity
The default hairline is ink at 8 percent and separates rows inside a surface. The strong one, ink at 14 percent, opens a section, rings a secondary action and draws a focus ring. A hairline is never a card edge and never a button border, and a solid grey line is what makes a page look like a spreadsheet.
Correct. --h-line, ink at 8 percent. It reads as an edge, not as an object.
Wrong. A solid grey. It has its own colour temperature and fights the warm paper.
Two
Motion tokens
Four durations and four curves, declared once in
snippets/hewyn-os-tokens.liquid and aliased here. Everything in the kit
uses them and nothing declares its own milliseconds.
Duration
--h-t-press.--h-t-quick and --h-t-move, which were two names for one idea.--h-t-enter.--h-t-draw.Easing
cubic-bezier(.22, 1, .36, 1). The workhorse. Fast out of the gate and a long settle. This is what makes a press feel answered rather than played back.cubic-bezier(.65, 0, .35, 1). Moving between two known positions: the segment indicator, a marker sliding to its zone.--h-ease-out.cubic-bezier(.4, 0, .2, 1). A value moving between two known points. No character, and that is correct for data.cubic-bezier(.4, 0, 1, 1). Something leaving. It should not linger.Two rules that are not negotiable
- Transform and opacity only. No CSS transition or keyframe in the kit touches width, height, margin, padding or a position offset, so no interaction in the system can cause a reflow. The one exception is the disclosure, which interpolates height through the Web Animations API on a panel carrying
contain: content, which keeps that reflow inside the panel. - Reduced motion collapses, it never breaks. A single multiplier goes to 0 in the shared snippet and every duration goes to one frame with it. Opacity stays at 120ms, so a state change is still perceivable. Every control still works and still gives feedback, through elevation and colour instead of travel.
Three
Widgets that move
All of these are live and all of them use Alex's real demo numbers. Press and hold to feel the asymmetry: going down is instant, coming back up has weight.
Card, press and settle
Down at 120ms on ease-out, up at 200ms on the same curve. The shadow drops with it, which is what sells the press as physical. Hover lifts one pixel and raises to e3.
Toggle, with travel
The knob travels the full track and settles on ease-out, which is what an accept or reject decision should feel like. Hold it and the knob squashes on the X axis the way a physical switch compresses, then returns to round. On is ink rather than the optimal green, because a status colour is never a fill.
Vitamin D3, 4000 IU
Morning stack. Added because vitamin D is 24.6 ng/mL.
Omega-3, 2g EPA and DHA
Morning stack. Omega-3 Index 6.1 percent.
Locked by your clinician
Disabled state.
Option tile, selection you can feel
The selected ring is an inset box-shadow, never a border, so the box size does not change and no tile beside it moves by a pixel. The warm fill fades in on a pseudo-element and the check scales in on ease-out. Selecting raises the tile a level and rings it in ink, so material carries the state rather than colour. First group is single select, second is multi.
Chips scale down 5 percent on touch and invert to ink when on. The last one is disabled, and it does not move at all, which is the only honest way to say no to a tap.
Progress ring, drawing and interpolating
Draws from zero on entry over 600ms, and animates between values rather than jumping. The numeral rolls digit by digit, least significant first, rather than swapping. Press a system to move it. Ring colour is a status colour, never the action orange.
Zone band, marker travels on entry
The marker starts at the left edge and travels to its position over 600ms, which makes a reader notice where the value came from. Zones carry the status tints, never orange.
LDL cholesterol
152 mg/dL
Omega-3 index
6.1 percent
Rolling number, tabular figures
Each digit is a column of zero to nine translated on the Y axis, and each cell is a fixed tabular width. Nothing beside the number shifts, including the unit, even when the value crosses from two digits to three.
Disclosure, interpolated height
Opens over 320ms so the content below slides rather than being shoved, and the chevron
turns on the same curve. The panel carries contain: content, so the reflow
stays inside the panel and never reaches the page.
Your vitamin D reads 24.6 ng/mL against an optimal band of 30 to 100, and your DNA report shows a higher than average vitamin D requirement. Both point the same way, which is why this sits at the top of the morning stack.
Four of five systems held or improved. Omega-3 index moved from 5.4 to 6.1, VO2 max from 43.7 to 44.9, and biological age from 41.8 to 41.2 against a chronological age of 45.
Segmented control, sliding indicator
One indicator, moved with translateX and scaled on the X axis to the active segment's width, so segments of different widths work and nothing animates a layout property. Arrow keys move the selection.
Showing all 53 markers from the 26 June panel.
Bottom sheet
Springs up over 320ms, blurs the backdrop, scrolls internally, and can be dragged down by the grip. While your finger is down the transition is removed so the sheet tracks exactly, then restored on release so it springs home or away. The page behind never moves.
Skeletons, not spinners
A spinner says wait. A skeleton says this is the shape of what is coming, which makes the same wait feel shorter and stops the layout jumping when the content lands. The shimmer is a transform-only sweep, so it runs on the compositor.
Metabolic
Your engine room is strong
HbA1c 5.4 percent, fasting glucose 88
Four
Photography as a design element
Four treatments and nothing else. Every one of them puts a controlled layer between the photograph and the type, so nothing ever lands on raw image. Aspect ratio is always declared in CSS, never inherited from the file, so swapping an image can never change the height of a page.
A. Full bleed with a scrim
Wind down
Your evening stack
Magnesium, and lights down by ten.
The scrim runs from 78 percent ink at the type edge to fully clear at 72 percent up the frame, so the half with no type on it is still a photograph. Type sits in the bottom left at 22px of padding and is capped at 26 characters per line, which keeps it clear of the subject in every crop we shoot.
B. Inset in a rounded card
Nutrients
What to eat this cycle
Focus
The midday dip
The photo's radius is the card's radius minus the card's padding: 20 minus 10 is 10. The image scales up 3 percent inside a fixed frame on hover, so the frame itself never moves. That is the difference between an expensive card and a cheap one.
C. Duotone on the ink palette
Treated. Desaturated, then multiplied under an ink to sage duotone and screened back. It stops being stock and starts being Hewyn.
Untreated. The same file. Its own colour temperature fights the ink ground and pulls the eye off whatever is beside it.
D. Section divider
A wide, short strip at 32 by 9, feathered into the ground at both edges, carrying no type at all. That is the point of it. It is a breath between two blocks of content, not a slide.
The rules
- Ratios. 3 by 2 for a content photo, 4 by 5 for a portrait or a product in a scene, 32 by 9 for a divider, 1 by 1 for a thumbnail. Always declared in CSS. The page hero is the single exception that changes with the viewport: 4 by 5 on a phone, 16 by 9 from 600px, 21 by 9 from 1000px, because it is the only photo that has to hold a headline and a fixed 21 by 9 is 167px tall on a phone.
- Scrim depth. 78 percent ink under the type, clearing by 72 percent of the frame. If type still fails contrast at that depth, the crop is wrong, not the scrim.
- Where type sits. Bottom left, 22px in, capped at 26 characters per line. Never centred over a subject and never in the middle of a frame.
- Treatment on ink. Anything on the dark ground gets the duotone. An untreated photograph on ink always looks like it was pasted there.
And what a photograph must never do
- Never compete with data. A photo and a number do not share a card. If a customer has to read a value, the photo goes above it or beside it, not behind it.
- Never sit behind a number. No value, marker, score or range ever renders over an image, at any scrim depth.
- Never carry a claim. A photograph cannot imply a result or a medical conclusion. No before and after, no clinical setting, no white coat, nothing that reads as proof of an outcome.
- Never be the only thing. A photo-only section says nothing. Every photograph on a Hewyn screen either sets a scene for copy or is the touch target for a real destination.
Five
Composed
Three real Hewyn screens built only from the parts above, so the kit gets judged as a whole rather than as a parts bin.
A routine action row
Card press, toggle, disclosure and status pill in one row. Accepting an item is the most repeated interaction in the product, so it gets the most physical control in the kit.
Vitamin D3, 4000 IU
Morning, with fat. Retest in 12 weeks.
Vitamin D 24.6 ng/mL against an optimal band of 30 to 100, and a higher than average requirement on your DNA report.
Two oily fish meals a week
Plate change. Omega-3 index 6.1 percent.
An omega-3 index of 6.1 percent sits below the 8 percent band. Food moves this as reliably as a capsule does, so both are on the plan and you can take either.
A test card
Photo inset in a rounded card, nested radius, a status pill, and the one orange action on the whole card. This is the purchase page pattern.
Body composition
Done, 88DEXA scan
Body fat 18.2 percent, bone Z-score plus 0.8.
Digestion
Not testedGut microbiome
At home sample, results in about three weeks.
Six
How the existing sections adopt this
The kit was built so that adoption is a class change plus one data attribute, never a
rewrite. Add the two assets to a section's head, then swap the classes below. Nothing in
hewyn-ui.css targets a tag or an existing section class, so the two systems
can run side by side while a section is migrated one widget at a time.
Step one, in any section
layout/app.liquid already renders {% render 'hewyn-os-tokens' %} and loads both kit assets for every page on it. Anywhere else, render the snippet first. Every --h-* name is an alias of a token in it and resolves to nothing without it.{{ 'hewyn-ui.css' | asset_url }} as a stylesheet and {{ 'hewyn-ui.js' | asset_url }} as a deferred script, in that order, after the snippet.hui to the section's root element. That is what scopes every token. Add hui-ground-ink or hui-ground-paper to any block that changes ground.Step two, the swaps
button.rcard becomes class="hui-card hui-press". The system ring markup becomes <div class="hui-ring" data-hui-ring data-value="78"> and the existing score numeral is deleted, because the ring builds its own rolling one.button.stackcard becomes hui-disc with a hui-disc__head and a hui-disc__panel, which removes the section's own open and close height code.[data-toggle=accepted] becomes class="hui-toggle" data-hui-toggle and listens for hui:toggle instead of its own click handler.hui-band with data-hui-band and a ratio. Category accordions become hui-disc. The lens switcher becomes hui-seg.hui-sheet, which gets it drag to dismiss, backdrop blur and correct iOS scroll locking for free..hdx-card becomes hui-photo-card hui-press with the test photograph in a hui-photo hui-photo--3x2. The selected state becomes hui-tile, which is what stops the current 1px border from shifting the grid on selection.hui-tile with data-hui-tile="groupname" for single select. The progress header becomes a hui-ring. The assembling screen's placeholder becomes hui-skel plus hui-swap.Each of those is independent. A section can take the toggle this week and the ring next month, and the two look consistent in the meantime because the kit's resting states were drawn to match what is on the site today.
What not to do
- Do not add a component rule to a section. If a widget needs a variant, it goes in
hewyn-ui.cssas a modifier so every surface gets it. - Do not hard code a duration. Use the tokens. A stray 300ms is how a system stops feeling like one hand made it.
- Do not use orange for a status. It is the action colour and nothing else. A score, a band, a flag and a trend all use ok, watch or attention.
- Do not animate a layout property. If a motion seems to need width or height, it almost always wants transform and a fixed frame instead.
Blood panel, 26 June
Cholesterol profile
Optimal under 130 mg/dL
Optimal under 90 mg/dL
Optimal under 150 mg/dL
Optimal under 200 mg/dL
Optimal above 40 mg/dL
Optimal under 75 nmol/L
Drag the grip down, tap the backdrop, or press escape. The page behind has not moved.






























