Nº 539 of 543
Tactile stat tiles
Three raised stat tiles on one putty surface: a 72px answer with a delta in a well pill and a seven-bar chart in a well, two smaller tiles beside it, and a segmented control that swaps Night for Week.
Preview
Use it
Free to use · credit appreciated1 Once, in your project · Node 22.20+
npx skills add SusanAcharya/Design-Lounge 2 Then ask
Using the Design Lounge skill, build "Tactile stat tiles" (piece stat-tile-tactile) in my stack. Match its brief; restyle it onto my product's theme.
No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.
<!-- Design Lounge Nº 539 · "Tactile stat tiles" · www.designlounge.live -->
# Tactile stat tiles
> **Build brief for a coding agent.** Rebuild this piece in the reader's stack. If they haven't said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don't "improve" them. When a kit is locked, map the surface and inks onto the theme tokens and keep the two shadow tokens and the 1px edge.
## What it is
The morning summary of Hush, a fictional sleep app, as three neumorphic tiles. One is the answer: "Asleep 7h 42min" at 72px, a delta pill pressed into the tile ("38 min more than your average"), and a seven-bar chart sitting in a well, each bar a small raised block with the latest night filled ink. The other two tiles are smaller (36px figures) and carry one sentence each. A segmented control at the top right swaps Night for Week and rewrites every figure, delta and sentence. The detail worth copying is the hierarchy under a style that flattens everything: one display size, pills pressed in rather than coloured, and bars that are raised objects in a pressed tray, so the chart reads without a single hue.
## Reference behaviour
1. First frame: "Last night", segments Night (pressed) | Week. Tiles: Asleep 7h 42min, delta up "38 min more than your average", bars Wed to Tue at 62 / 70 / 48 / 80 / 66 / 58 / 90% with Tue filled ink; Deep sleep 1h 51min, delta up "24%", a sentence; Resting heart 54 bpm, delta down "2 below last week", a sentence.
2. On load the bars grow from 20% to full height over 600ms, 40ms apart (reduced motion: drawn at once).
3. Click Week: the title becomes "This week"; the figures become 7h 04min (avg), 1h 38min, 56 bpm; the deltas and sentences change; the bars stay (they already show the week).
4. Click Night: back to the first frame's values.
5. The pressed segment is raised and ink; the other is flat and `--ink-2`.
6. Nothing else is interactive; the tiles have no hover.
## Structure
```
1280 × 800, surface #E6E3DF; a 1040 set centred
Last night 22/800 ( Night | Week ) well, pressed raised
┌ tile 1.6fr, r24, raised ─────────────────┐ ┌ tile 1fr ─────────────┐ ┌ tile 1fr ──────────────┐
│ ASLEEP │ │ DEEP SLEEP │ │ RESTING HEART │
│ 7h 42min 72/800 │ │ 1h 51min 36/800 │ │ 54 bpm │
│ (↑ 38 min more than your average) well │ │ (↑ 24%) well │ │ (↓ 2 below last week) │
│ ┌ well tray ─────────────────────────┐ │ │ Mostly before 02:00… │ │ Lowest at 04:10… │
│ │ ▮ ▮ ▮ ▮ ▮ ▮ █ raised bars, 7 │ │ └───────────────────────┘ └────────────────────────┘
│ └────────────────────────────────────┘ │
│ Wed Thu Fri Sat Sun Mon Tue 11/600 │
└──────────────────────────────────────────┘
```
- `main.set` → `div.head` (`h1`, `div.seg[role=group]` of two `aria-pressed` buttons), `div.tiles`.
- Each `section.tile[aria-labelledby]` → `span.label`, `div.num` (spans for the figures, `small` for the units), `span.delta` (an arrow SVG and text), then either `div.bars[role=img][aria-label]` of seven `i` plus `div.bars-l` labels, or a `p`.
## Tokens
… 115 more lines. Copy or download for the full brief. Loading the source…Brief
155 linesBuild brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them. When a kit is locked, map the surface and inks onto the theme tokens and keep the two shadow tokens and the 1px edge.
What it is
The morning summary of Hush, a fictional sleep app, as three neumorphic tiles. One is the answer: “Asleep 7h 42min” at 72px, a delta pill pressed into the tile (“38 min more than your average”), and a seven-bar chart sitting in a well, each bar a small raised block with the latest night filled ink. The other two tiles are smaller (36px figures) and carry one sentence each. A segmented control at the top right swaps Night for Week and rewrites every figure, delta and sentence. The detail worth copying is the hierarchy under a style that flattens everything: one display size, pills pressed in rather than coloured, and bars that are raised objects in a pressed tray, so the chart reads without a single hue.
Reference behaviour
- First frame: “Last night”, segments Night (pressed) | Week. Tiles: Asleep 7h 42min, delta up “38 min more than your average”, bars Wed to Tue at 62 / 70 / 48 / 80 / 66 / 58 / 90% with Tue filled ink; Deep sleep 1h 51min, delta up “24%”, a sentence; Resting heart 54 bpm, delta down “2 below last week”, a sentence.
- On load the bars grow from 20% to full height over 600ms, 40ms apart (reduced motion: drawn at once).
- Click Week: the title becomes “This week”; the figures become 7h 04min (avg), 1h 38min, 56 bpm; the deltas and sentences change; the bars stay (they already show the week).
- Click Night: back to the first frame’s values.
- The pressed segment is raised and ink; the other is flat and
--ink-2. - Nothing else is interactive; the tiles have no hover.
Structure
1280 × 800, surface #E6E3DF; a 1040 set centred
Last night 22/800 ( Night | Week ) well, pressed raised
┌ tile 1.6fr, r24, raised ─────────────────┐ ┌ tile 1fr ─────────────┐ ┌ tile 1fr ──────────────┐
│ ASLEEP │ │ DEEP SLEEP │ │ RESTING HEART │
│ 7h 42min 72/800 │ │ 1h 51min 36/800 │ │ 54 bpm │
│ (↑ 38 min more than your average) well │ │ (↑ 24%) well │ │ (↓ 2 below last week) │
│ ┌ well tray ─────────────────────────┐ │ │ Mostly before 02:00… │ │ Lowest at 04:10… │
│ │ ▮ ▮ ▮ ▮ ▮ ▮ █ raised bars, 7 │ │ └───────────────────────┘ └────────────────────────┘
│ └────────────────────────────────────┘ │
│ Wed Thu Fri Sat Sun Mon Tue 11/600 │
└──────────────────────────────────────────┘
main.set→div.head(h1,div.seg[role=group]of twoaria-pressedbuttons),div.tiles.- Each
section.tile[aria-labelledby]→span.label,div.num(spans for the figures,smallfor the units),span.delta(an arrow SVG and text), then eitherdiv.bars[role=img][aria-label]of seveniplusdiv.bars-llabels, or ap.
Tokens
:root {
--surface: #e6e3df;
--ink: #2a2622; --ink-2: #56514b; --ink-3: #625c56;
--edge: #7d7770; /* 1px, 3.5:1 on the surface */
--light: rgba(255,255,255,.85); --shade: rgba(74,66,58,.22);
--raise: 10px 10px 24px var(--shade), -10px -10px 24px var(--light); /* tiles */
--raise-sm: 5px 5px 12px var(--shade), -5px -5px 12px var(--light); /* bars, pressed segment */
--well: inset 6px 6px 14px var(--shade), inset -6px -6px 14px var(--light); /* the bar tray */
--well-sm: inset 3px 3px 7px var(--shade), inset -3px -3px 7px var(--light); /* delta pills, the segment well */
--sans: "Sora", system-ui, sans-serif;
--r: 20px; --r-card: 24px; --r-tray: 16px;
--tray-h: 88px; --gap: 22px;
--t-micro: 200ms; --t-bar: 600ms;
--ease: cubic-bezier(.2, .7, .2, 1); --expo: cubic-bezier(.16, 1, .3, 1);
}
Typography
| Role | Family | Size | Weight | Line-height | Tracking | Case |
|---|---|---|---|---|---|---|
| The answer | Sora | 72px | 800 | 0.95 | −0.045em | tabular numerals; units 28px/600 --ink-2 |
| Small tile figure | Sora | 36px | 800 | 0.95 | −0.045em | units 18px/600 |
| Title | Sora | 22px | 800 | 1 | −0.025em | sentence |
| Delta | Sora | 13px | 600 | 1 | 0 | sentence, in a well pill |
| Sentence | Sora | 13px | 400 | 1.45 | 0 | sentence, --ink-2 |
| Segment | Sora | 13px | 600 | 1 | 0 | sentence |
| Label | Sora | 12px | 600 | 1 | +0.06em | UPPERCASE, --ink-3 |
| Bar labels | Sora | 11px | 600 | 1 | 0 | Wed…Tue, --ink-3 |
Motion
| Element | Trigger | Property | From → To | Duration | Easing |
|---|---|---|---|---|---|
.bars i | load | transform (scaleY, origin bottom) | 0.2 → 1, delay 0 to 240ms in 40ms steps | 600ms | --expo |
.seg button | press | box-shadow, color | none, --ink-2 → --raise-sm, --ink | 200ms | --ease |
| figures, deltas, sentences | range change | text | swapped at once | 0 | – |
Reduced motion: no bar growth, 1ms transitions.
States
- Tile: raised, 1px edge, no hover.
- Delta pill: a well with an arrow; up and down are different arrows, not colours.
- Bar: raised block with an edge; the latest period is filled
--ink. - Segment pressed / unpressed: raised ink / flat
--ink-2. - Focus-visible: 2px ink outline at 4px offset on the segments.
Accessibility
- Each tile is a
<section>labelled by its own label; the figures are real text (“7”, “h”, “42”, “min”), so a screen reader hears “7 h 42 min”. - The bar tray is
role="img"with anaria-label(“Hours asleep, the last seven nights”), since a label on a plaindivis prohibited; the labels under it are visual (aria-hidden) because the sentence and figure carry the data. Add a visually hidden list of the seven values if the chart is the only place they appear in your product. - Up and down deltas use different arrow glyphs and words (“more than”, “below”); nothing depends on colour.
- The segmented control is a
role="group"ofaria-pressedbuttons; the title changes with it. - Contrast:
--ink11.7:1,--ink-26.1:1,--ink-34.9:1 on the surface;--edge3.5:1 around tiles, pills, bars and the tray. - Hit targets: segments 34px tall in a 42px well.
Responsive rules
- ≥ 1280: three tiles, 1.6fr 1fr 1fr, gap 22.
- 1024–1279: the same at 1.4fr 1fr 1fr; the answer 60px.
- 768–1023: the big tile full width, the two small tiles in a row beneath.
- < 640: one column; the answer 56px; bar tray 72px; the head stacks title then segments.
- Dark pair: dark putty surface, shade
rgba(0,0,0,.45), lightrgba(255,255,255,.06), light ink, edge#4a453f; the latest bar fills with the light ink.
Acceptance checklist
Always
- One display-size figure on the view; the other tiles’ figures are at most half its size.
- One surface colour; tiles raised, pills and the bar tray pressed in, bars raised inside the tray; no third shadow colour and no hue for emphasis.
- Every tile, pill, bar and the tray has a 1px edge of at least 3:1 against the surface.
- Deltas show direction with an arrow and a word, never colour alone.
- The segmented control is
aria-pressedbuttons; switching rewrites the title, every figure, delta and sentence. - The bars grow in on load, staggered, and are still under reduced motion.
- Radii 20 to 24px on tiles, 16px on the tray, 999px on pills; bars 6px on top.
This demo
- Night: 7h 42min, +38 min, 1h 51min (+24%), 54 bpm (2 below); Week: 7h 04min avg, 1h 38min (23%), 56 bpm; bars 62 / 70 / 48 / 80 / 66 / 58 / 90 with Tue filled.
Implementation notes
Raised bars in a pressed tray:
.bars { height: 88px; padding: 10px 12px; border-radius: 16px; box-shadow: var(--well); border: 1px solid var(--edge);
display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; align-items: end; }
.bars i { height: var(--h); border-radius: 6px 6px 3px 3px; background: var(--surface); border: 1px solid var(--edge); box-shadow: var(--raise-sm);
transform-origin: bottom; animation: grow var(--t-bar) var(--expo) both; animation-delay: var(--d); }
.bars i.now { background: var(--ink); border-color: var(--ink); }
@keyframes grow { from { transform: scaleY(.2) } }
Figures as spans, so units stay in the text face at a smaller size and the number stays readable:
<div class="num"><span>7</span><small>h</small><span>42</span><small>min</small></div>
The range swap is a data object and innerHTML on known ids; no framework needed:
const d = D[b.dataset.r];
n1.innerHTML = d.n1; d1.innerHTML = arrow(true) + d.d1; p2.textContent = d.p2; // and so on
Variants. A tile with no chart drops the tray and keeps the sentence; a tile with a target shows it as a second pill (“goal 8h”) in the same well style, never as a coloured ring. For a dark pair, keep the latest bar in the light ink.
Common mistakes: four equal tiles in a row (the answer gets lost); a green or red delta instead of an arrow and a word; a coloured fill on the bars; shadows without the hairline, so the bars merge into the tray on a dim screen.
Details
- Palette
- Type
- Sora
- Motion
- Subtle motion
- Build
- Quick build
- Tags