Nº 100 of 520
Checkbox draw list
A ruled-paper to-do list where red-pencil checkmarks draw in, strike-throughs sweep across each task, and a 232px serif numeral counts progress.
Preview
Use it
Free to use · credit appreciated1 Once, in your project
npx skills add SusanAcharya/Design-Lounge 2 Then ask
Using the Design Lounge skill, build "Checkbox draw list" (piece checkbox-draw-list) 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º 100 · "Checkbox draw list" · designlounge.vercel.app -->
# Checkbox draw list
> **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.
## What it is
A pre-trip checklist in a fictional notes app, Margin. The list sits on a ruled sheet with a salmon margin line; each checkbox lives in the margin, and ticking it draws a red-pencil checkmark (280ms stroke) followed by a pencil line sweeping through the task text (360ms, 180ms later). A huge light-weight serif numeral on the left counts tasks done, with a row of nine tick segments underneath. It should feel like crossing things off a paper list: quiet, satisfying, slightly analogue. The detail worth copying is the sequencing: the check finishes before the strike starts, so the eye follows one stroke into the next.
## Reference behaviour
1. Initial state: nine tasks, five ticked (rows 1, 2, 3, 5, 7). Numeral reads "5", suffix "of 9", five red tick segments, note "4 left. Flight TP1351 leaves Thursday at 06:40." Header right reads "5 done · 4 to go".
2. Click anywhere on a task row (the whole row is the `<label>`): the native checkbox toggles.
3. Ticking: the 18px rounded square dims from ink to `--ink-3`; the checkmark path draws from 0 to full length in 280ms; after a 180ms delay a 1.5px red line scales from 0 to full width across the task text, origin left, over 360ms; the text colour fades to `--ink-3`.
4. Unticking: checkmark retracts over 280ms and the strike retracts immediately (no delay) over 360ms; text returns to ink.
5. Each change updates the numeral (it re-enters from 18px below at 20% opacity over 420ms), the "of N" suffix, the tick segments, the header summary and the note.
6. When all tasks are done the note becomes "All set. Boa viagem — see you on the 15th."
7. Type in the "Add a task and press Enter" row and press Enter: a new unticked row (tag "New") slides in from 8px above over 360ms; totals update. The list caps at 10 rows; the oldest-last row is dropped to keep the sheet in frame.
8. "Untick all" in the footer clears every box, replaying all retract animations together.
9. Hovering a row turns its checkbox square red.
## Structure
```
1280 × 800, padding 48 64 48 72, grid 400px | 1fr
┌───────────────────────────┬───────────────────────────────────────────────┐
│ MARGIN Sat 3 Oct │ ┌─────┬─────────────────────────────────────┐ │
│ │ │ │ Packing & errands 5 done · 4… │ │ 72
│ Before Lisbon (64 ital.) │ ├─────┼─────────────────────────────────────┤ │
│ sub, 300px max │ │ [✓] │ ~~Scan passport and send…~~ Docs │ │ 56 each
│ │ │ [ ] │ Change €60 into coins… (Wed) Money│ │
│ │ │ … │ nine rows │ │
│ 5 │ │ + │ Add a task and press Enter │ │
│ of 9 (232px / 48px) │ │ │ hint Untick all │ │
│ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ticks │ └─────┴─────────────────────────────────────┘ │
│ 4 left. Flight… │ margin line at x = 64px inside the sheet │
└───────────────────────────┴───────────────────────────────────────────────┘
```
… 144 more lines. Copy or download for the full brief. Loading the source…Brief
184 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.
What it is
A pre-trip checklist in a fictional notes app, Margin. The list sits on a ruled sheet with a salmon margin line; each checkbox lives in the margin, and ticking it draws a red-pencil checkmark (280ms stroke) followed by a pencil line sweeping through the task text (360ms, 180ms later). A huge light-weight serif numeral on the left counts tasks done, with a row of nine tick segments underneath. It should feel like crossing things off a paper list: quiet, satisfying, slightly analogue. The detail worth copying is the sequencing: the check finishes before the strike starts, so the eye follows one stroke into the next.
Reference behaviour
- Initial state: nine tasks, five ticked (rows 1, 2, 3, 5, 7). Numeral reads “5”, suffix “of 9”, five red tick segments, note “4 left. Flight TP1351 leaves Thursday at 06:40.” Header right reads “5 done · 4 to go”.
- Click anywhere on a task row (the whole row is the
<label>): the native checkbox toggles. - Ticking: the 18px rounded square dims from ink to
--ink-3; the checkmark path draws from 0 to full length in 280ms; after a 180ms delay a 1.5px red line scales from 0 to full width across the task text, origin left, over 360ms; the text colour fades to--ink-3. - Unticking: checkmark retracts over 280ms and the strike retracts immediately (no delay) over 360ms; text returns to ink.
- Each change updates the numeral (it re-enters from 18px below at 20% opacity over 420ms), the “of N” suffix, the tick segments, the header summary and the note.
- When all tasks are done the note becomes “All set. Boa viagem — see you on the 15th.”
- Type in the “Add a task and press Enter” row and press Enter: a new unticked row (tag “New”) slides in from 8px above over 360ms; totals update. The list caps at 10 rows; the oldest-last row is dropped to keep the sheet in frame.
- “Untick all” in the footer clears every box, replaying all retract animations together.
- Hovering a row turns its checkbox square red.
Structure
1280 × 800, padding 48 64 48 72, grid 400px | 1fr
┌───────────────────────────┬───────────────────────────────────────────────┐
│ MARGIN Sat 3 Oct │ ┌─────┬─────────────────────────────────────┐ │
│ │ │ │ Packing & errands 5 done · 4… │ │ 72
│ Before Lisbon (64 ital.) │ ├─────┼─────────────────────────────────────┤ │
│ sub, 300px max │ │ [✓] │ ~~Scan passport and send…~~ Docs │ │ 56 each
│ │ │ [ ] │ Change €60 into coins… (Wed) Money│ │
│ │ │ … │ nine rows │ │
│ 5 │ │ + │ Add a task and press Enter │ │
│ of 9 (232px / 48px) │ │ │ hint Untick all │ │
│ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ▬ ticks │ └─────┴─────────────────────────────────────┘ │
│ 4 left. Flight… │ margin line at x = 64px inside the sheet │
└───────────────────────────┴───────────────────────────────────────────────┘
<aside class="side" aria-label="Progress">: brand row,<h1>,.sub,.big(numeral + suffix,aria-hidden),.ticks(aria-hidden),<p class="note" aria-live="polite">.<main class="sheet">: header (<h2>+ summary),<ul>of<li>rows,<form class="add">, footer.- Each
<li>: visually hidden<input type="checkbox">followed by a<label>containing.box(64px wide margin cell holding the SVG),.t(task text wrapped in<s>used only as the strike host),.meta(optional due pill + tag).
Tokens
:root {
--bg: #ece6d8; /* desk */
--paper: #f8f4ea; /* sheet */
--rule: #dcd3c0; /* row rules, pills, inactive ticks */
--margin: #d98b7a; /* vertical margin line, 70% opacity */
--ink: #1f2a26; /* text, box stroke */
--ink-2: #55605a; /* secondary */
--ink-3: #8a8f86; /* done text, meta, placeholder */
--pencil: #c2412d; /* check, strike, ticks on, focus */
--serif: "Newsreader", Georgia, serif;
--sans: "Work Sans", system-ui, sans-serif;
--fs-numeral: 232px; --fs-of: 48px; --fs-h1: 64px; --fs-h2: 26px;
--fs-task: 19px; --fs-sub: 17px; --fs-body: 15px; --fs-meta: 12px;
--row: 56px; /* task row height */
--gutter: 64px; /* margin column */
--r-sheet: 4px; --r-box: 3px; --r-pill: 999px;
--shadow-sheet: 0 1px 0 #d6cdb9, 0 2px 0 #e9e2d2, 0 3px 0 #d6cdb9, 0 24px 40px -24px rgba(60,40,10,.3);
--t-fast: 160ms; --t-draw: 280ms; --t-strike: 360ms; --strike-delay: 180ms;
--ease: cubic-bezier(.2,.7,.2,1);
--ease-out: cubic-bezier(.16,1,.3,1);
}
Typography
| Role | Family | Size | Weight | Line-height | Tracking | Case / style |
|---|---|---|---|---|---|---|
| Numeral | Newsreader (opsz 72) | 232px | 300 | 0.8 | −0.06em | lining figures |
| “of 9” | Newsreader | 48px | 400 | 0.8 | 0 | italic |
| h1 | Newsreader | 64px | 300 | 1.0 | −0.02em | italic |
| Sub | Newsreader | 17px | 400 | 1.45 | 0 | roman |
| Sheet title | Newsreader | 26px | 400 | 1.1 | −0.01em | roman |
| Task | Newsreader | 19px | 400 | 1.2 | 0 | roman |
| Add input | Newsreader | 19px | 400 | 1.2 | 0 | italic |
| Brand | Work Sans | 12px | 600 | 1 | +0.16em | UPPERCASE |
| Note / body | Work Sans | 14px | 400 (600 lead) | 1.45 | 0 | sentence |
| Meta, pills | Work Sans | 12px | 400 | 1.2 | 0 | sentence |
Motion
| Element | Trigger | Property | From → To | Duration | Easing | Delay |
|---|---|---|---|---|---|---|
| Checkmark path | check | stroke-dashoffset | 1 → 0 (pathLength 1) | 280ms | --ease | 0 |
| Checkmark path | uncheck | stroke-dashoffset | 0 → 1 | 280ms | --ease | 0 |
| Strike line | check | transform scaleX | 0 → 1, origin left | 360ms | --ease | 180ms |
| Strike line | uncheck | transform scaleX | 1 → 0 | 360ms | --ease | 0 |
| Task text | toggle | color | ink ↔ ink-3 | 360ms | --ease | 0 |
| Box square | toggle/hover | stroke | ink → ink-3 / pencil | 160ms | linear-free default | 0 |
| Numeral | count change | translateY, opacity | 18px, .2 → 0, 1 | 420ms | --ease-out | 0 |
| Tick segment | count change | background | rule ↔ pencil | 360ms | --ease | 0 |
| New row | add | translateY, opacity | −8px, 0 → 0, 1 | 360ms | --ease-out | 0 |
Reduced motion: durations 1ms, delays 0, keyframes removed. Ticks and strikes still appear, instantly.
States
- Unchecked: ink square, no check, ink text.
- Checked:
--ink-3square, red check, red strike,--ink-3text. - Hover (row): square stroke turns
--pencil. - Focus-visible: 2px
--penciloutline 6px outside the checkbox SVG (the input is visually hidden, so styleinput:focus-visible + label .box svg). - Add row focused: row tints
rgba(194,65,45,.04). - All done: note copy changes; every tick segment red.
- Due soon:
.meta empill (1px--ruleborder, 999px radius, 2px 9px padding) before the tag.
Accessibility
- Real
<input type="checkbox">, visually hidden withopacity:0; width:1px; height:1px(notdisplay:none), labelled by the whole row<label>. Space toggles when focused. - Tab order: tasks top to bottom, add input, “Untick all” button.
- The
<s>element is presentational here (no native line-through); if you prefer semantics, addaria-label="done"state via the checkbox, not the text. - The note is
aria-live="polite", so totals are announced after each change; the numeral and ticks arearia-hiddenduplicates. - Contrast: ink on paper 13:1;
--ink-3done text 3.2:1 is intentionally reduced and paired with the strike; keep active tasks in full ink. - Rows are 56px tall, the full row is clickable.
Responsive rules
- ≥ 1280: as drawn.
- 1024: left column stays 400px, sheet narrows; tasks ellipsize (
white-space:nowrap; text-overflow:ellipsis). - < 1000: single column; numeral 140px; side panel stacks above the sheet; body scrolls.
- < 640: hide the tag text, keep due pills; margin column shrinks to 52px.
Acceptance checklist
- Five of nine tasks are ticked on load; numeral reads 5 and five of nine segments are red.
- Checkmark draws via
stroke-dashoffsetwithpathLength="1", over 280ms. - Strike starts 180ms after the check begins and sweeps left to right in 360ms.
- Unticking retracts the strike with no delay.
- The whole row toggles the checkbox; the checkbox is a native input.
- Numeral, suffix, segments, header summary and note all update on every change.
- Enter in the add row appends an unticked task with a slide-in; empty input does nothing.
- “Untick all” clears every row.
- Salmon margin line runs the full height of the sheet at x = 64px.
- Focus ring is visible on every checkbox and the footer button.
- Reduced motion keeps every state, without animation.
Implementation notes
Normalise the path length so one dash value works for any checkmark shape:
<svg viewBox="0 0 24 24">
<rect class="sq" x="3" y="3" width="18" height="18" rx="3"/>
<path class="ck" pathLength="1" d="M7 12.5l3.5 3.8L18.5 6"/>
</svg>
.ck { stroke: var(--pencil); stroke-width: 2.4; stroke-dasharray: 1; stroke-dashoffset: 1;
transition: stroke-dashoffset var(--t-draw) var(--ease); }
input:checked + label .ck { stroke-dashoffset: 0; }
Asymmetric delay lives on the checked selector only, so it applies going in but not coming out:
.t s { position: relative; display: inline-block; text-decoration: none; }
.t s::after { content: ""; position: absolute; left: -3px; right: -3px; top: 56%; height: 1.5px;
background: var(--pencil); transform: scaleX(0); transform-origin: left;
transition: transform var(--t-strike) var(--ease); }
input:checked + label .t s::after { transform: scaleX(1); transition-delay: var(--strike-delay); }
Restart the numeral animation by removing the class and forcing reflow:
num.classList.remove('bump'); void num.offsetWidth; num.classList.add('bump');
Common mistakes: using text-decoration: line-through (can’t animate); wrapping long tasks onto two lines so the strike only covers the first; display:none on the checkbox, which removes it from keyboard order.
Details
- Palette
- Type
- Newsreader · Work Sans
- Motion
- Subtle motion
- Build
- Quick build
- Tags