Calendar: the row grammar at calendar scale #189

Closed
opened 2026-08-23 22:54:05 +00:00 by nalum · 2 comments
Owner

Part of the calendar redesign. The design handoff (design_handoff_calendar, held outside the repo) is the source; the facts it depends on are repeated here so this issue stands alone.

One row grammar across month, week, agenda and the day sheet, so the four surfaces teach each other. Everything else in the calendar composes from it.

Order: mark, then title, with the mark carrying the type in its shape:

  • job — squircle mark, 5px radius at 17px
  • event — circle mark
  • unowned job — dashed squircle with ?
  • done job — the tick replaces the symbol in the same squircle, title struck at 55% opacity
  • late job — amber time plus the word late, the jobs ladder in row form

The mark is 17px with a 5px gap: one object before the title, never two. Two alternatives were measured at a true 134px month cell and rejected — a separate type glyph clipped titles at about 55px, and a right-edge mark fitted the titles but produced a ragged mark column and an order that disagreed with every other surface.

Wider rows — day sheet, agenda, week strip — also carry a tick box, because they have the width. The mark's shape simply agrees with it.

Deadlines and durations are told apart: was due 09:45 in amber for a job's due time, 18:00 - 19:00 in muted green for an event's span.

Depends on the jobs bundle's shared row component, token roles and minute ticker (#170, #171, #186): a calendar row is that component with a different payload.

Open, and inherited from the jobs bundle: whether an unowned job appears in calendar views at all depends on where the up-for-grabs pool lives.


Two requirements added after #213 shipped

The job sheet went out to the family's cluster three times before it was right.
Seven visual defects, none caught by a suite: pickers that unfolded forms
instead of opening, three Save buttons on a commit-on-blur screen, (optional)
labels, a reward's name used as a field label, an unstyled add row, checkboxes
with a ghosted tick, and a create form with a browser spinner. Every one had to
be found by a person looking at a screen.

1. Screenshot goldens are part of this ticket

Record gallery (Playwright) and Roborazzi cases for every surface this ticket
builds or visibly changes, and treat them as a deliverable rather than a
follow-up. Three of #213's defects — the unstyled add row, the ghosted
checkboxes, the spinner in the create form — were pixel faults that a golden
would have caught before a deploy did.

#172 deferred its goldens on the reasoning that later tickets would re-record
them. That reasoning was sound then and is not now: the row grammar and the
pickers are settled, so an image recorded here stays valid.

Mechanics, from AGENTS.md: a new case rides in the same commit as the code
that adds it — a commit that adds a gallery or Roborazzi case without its image
is red on its own, because check / web runs npm run test:screens and
android / build runs verifyRoborazziDebug. A re-recorded existing case
goes in its own commit, so the reviewable diff stays code.

2. Check for leaked form styling before you finish

Every visual defect on #213 came from one place: the shipped app's form
fragments reused inside the new row grammar. They pass every test, because the
tests assert the pieces exist rather than that the screen reads right.

Before you call this done, grep the surfaces you touched and report what you
found and what you did about each:

  • a raw <input> / OutlinedTextField / BasicTextField outside the row and
    picker components — a form field dropped into a list of rows
  • type="number", which renders browser spinner arrows
  • btn, link-btn, btn-quiet or TextButton where a row or a quiet row
    verb belongs — underlined links reading as broken text
  • (optional) in any label — §8 names that pattern as the form admitting it
    should not have asked
  • any Save button on a surface that commits on blur, including Save changes and Save the points
  • a disabled-and-grey control where law 5 wants it absent
  • a record's title used as a field row's label — that column is for a
    field's name (Who, When, Worth), and a long title wraps in it

If your ticket is the first to land after this was written, add the check to
AGENTS.md beside the goldens rule, so it stops being a thing I remember to say.


Update — the demo joins the bundle and is the locked design (2026-08-25)

The handoff now carries calendar-demo.dc.html — the wired, locked blueprint for visual and behaviour. Build from it, mapping its layer-1 tokens by role onto design/tokens.yaml (no new roles; the three shape roles — radius, radius-panel, radius-btn — are read from the theme, never derived). What it settles for this ticket:

  • The open question above is closed. An unowned job appears iff it is dated (a due date, a repeat rule, or a daily-times span); it draws the dashed-squircle ? mark, the hollow dot on the phone month, and sorts last within its slot. An undated pool job never reaches the calendar.
  • The done-tick rule is sharper: the tick replaces the symbol only in the narrow marks that have no tick box (month cells, the week band). A wide row keeps the owner's glyph when done — its tick box already says done, and the row still has to say who.
  • Points are visible before the tick, in the reward role: a celebrate pip on the corner of the mark at every size, and where the row has width a celebrate-filled pill — ✦ 25 pts before, a quiet ✦ 25 claimed after. Lateness is warning, never celebrate: was due 09:45 · late.
  • Ticking anywhere is a full completion site, and the stamp draws over the row that was ticked — day sheet, agenda, week strip, phone list or the card — with confetti only where points attach. Month cells carry no tick (no 44px target fits at that width); they open the record instead.
Part of the calendar redesign. The design handoff (`design_handoff_calendar`, held outside the repo) is the source; the facts it depends on are repeated here so this issue stands alone. One row grammar across month, week, agenda and the day sheet, so the four surfaces teach each other. Everything else in the calendar composes from it. **Order: mark, then title**, with the mark carrying the type in its shape: - job — squircle mark, 5px radius at 17px - event — circle mark - unowned job — dashed squircle with `?` - done job — the tick replaces the symbol in the same squircle, title struck at 55% opacity - late job — amber time plus the word *late*, the jobs ladder in row form The mark is 17px with a 5px gap: one object before the title, never two. Two alternatives were measured at a true 134px month cell and rejected — a separate type glyph clipped titles at about 55px, and a right-edge mark fitted the titles but produced a ragged mark column and an order that disagreed with every other surface. Wider rows — day sheet, agenda, week strip — also carry a tick box, because they have the width. The mark's shape simply agrees with it. **Deadlines and durations are told apart:** `was due 09:45` in amber for a job's due time, `18:00 - 19:00` in muted green for an event's span. Depends on the jobs bundle's shared row component, token roles and minute ticker (#170, #171, #186): a calendar row is that component with a different payload. Open, and inherited from the jobs bundle: whether an unowned job appears in calendar views at all depends on where the up-for-grabs pool lives. --- ## Two requirements added after #213 shipped The job sheet went out to the family's cluster three times before it was right. Seven visual defects, none caught by a suite: pickers that unfolded forms instead of opening, three Save buttons on a commit-on-blur screen, `(optional)` labels, a reward's name used as a field label, an unstyled add row, checkboxes with a ghosted tick, and a create form with a browser spinner. Every one had to be found by a person looking at a screen. ### 1. Screenshot goldens are part of this ticket Record gallery (Playwright) and Roborazzi cases for every surface this ticket builds or visibly changes, and treat them as a deliverable rather than a follow-up. Three of #213's defects — the unstyled add row, the ghosted checkboxes, the spinner in the create form — were pixel faults that a golden would have caught before a deploy did. `#172` deferred its goldens on the reasoning that later tickets would re-record them. That reasoning was sound then and is not now: the row grammar and the pickers are settled, so an image recorded here stays valid. Mechanics, from AGENTS.md: a **new** case rides in the same commit as the code that adds it — a commit that adds a gallery or Roborazzi case without its image is red on its own, because `check / web` runs `npm run test:screens` and `android / build` runs `verifyRoborazziDebug`. A **re-recorded existing** case goes in its own commit, so the reviewable diff stays code. ### 2. Check for leaked form styling before you finish Every visual defect on #213 came from one place: the shipped app's form fragments reused inside the new row grammar. They pass every test, because the tests assert the pieces exist rather than that the screen reads right. Before you call this done, grep the surfaces you touched and report what you found and what you did about each: - a raw `<input>` / `OutlinedTextField` / `BasicTextField` outside the row and picker components — a form field dropped into a list of rows - `type="number"`, which renders browser spinner arrows - `btn`, `link-btn`, `btn-quiet` or `TextButton` where a row or a quiet row verb belongs — underlined links reading as broken text - `(optional)` in any label — §8 names that pattern as the form admitting it should not have asked - **any Save button on a surface that commits on blur**, including `Save changes` and `Save the points` - a disabled-and-grey control where law 5 wants it absent - a record's title used as a field row's `label` — that column is for a field's name (`Who`, `When`, `Worth`), and a long title wraps in it If your ticket is the first to land after this was written, add the check to AGENTS.md beside the goldens rule, so it stops being a thing I remember to say. --- ## Update — the demo joins the bundle and is the locked design (2026-08-25) The handoff now carries `calendar-demo.dc.html` — the wired, locked blueprint for visual and behaviour. Build from it, mapping its layer-1 tokens by role onto `design/tokens.yaml` (no new roles; the three shape roles — radius, radius-panel, radius-btn — are read from the theme, never derived). What it settles for this ticket: - **The open question above is closed.** An unowned job appears iff it is dated (a due date, a repeat rule, or a daily-times span); it draws the dashed-squircle `?` mark, the hollow dot on the phone month, and sorts last within its slot. An undated pool job never reaches the calendar. - **The done-tick rule is sharper:** the tick replaces the symbol only in the narrow marks that have **no tick box** (month cells, the week band). A wide row keeps the owner's glyph when done — its tick box already says done, and the row still has to say *who*. - **Points are visible before the tick, in the reward role:** a `celebrate` pip on the corner of the mark at every size, and where the row has width a celebrate-filled pill — `✦ 25 pts` before, a quiet `✦ 25 claimed` after. Lateness is `warning`, never celebrate: `was due 09:45 · late`. - **Ticking anywhere is a full completion site, and the stamp draws over the row that was ticked** — day sheet, agenda, week strip, phone list or the card — with confetti only where points attach. Month cells carry no tick (no 44px target fits at that width); they open the record instead.
Author
Owner

Delivered in PR #259: CalendarRow.tsx / CalendarRow.kt carry the §2.1 grammar (squircle job, circle event, dashed unowned, tick-in-mark, celebrate pip and pill, was-due late line), pinned by conformance/render-calendar-row.json (13 cases) and the calrow goldens on both surfaces.

Delivered in PR #259: CalendarRow.tsx / CalendarRow.kt carry the §2.1 grammar (squircle job, circle event, dashed unowned, tick-in-mark, celebrate pip and pill, was-due late line), pinned by conformance/render-calendar-row.json (13 cases) and the calrow goldens on both surfaces.
Author
Owner

Delivered by PR #259 (row grammar, conformance fixture render-calendar-row.json + goldens), merged to main 2026-08-26.

Delivered by PR #259 (row grammar, conformance fixture render-calendar-row.json + goldens), merged to main 2026-08-26.
nalum closed this issue 2026-08-26 18:47:57 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app#189
No description provided.