Take the duration chips off the list, and let the list say what it was asked - #95
Open
EnesYilmazcode wants to merge 2 commits into
Open
Take the duration chips off the list, and let the list say what it was asked#95EnesYilmazcode wants to merge 2 commits into
EnesYilmazcode wants to merge 2 commits into
Conversation
…s asked The chip bar kept asking "how long?" on every row of every answer, on the one screen whose whole job is showing answers. Read off the rule that shipped it was 61px (44px min-height, .5rem padding top and bottom, a 1px border-top) against a 57px result row, and PEEK is 0.38, so on a 390x844 phone it was 19% of the 321px peeked sheet: slightly more than one whole answer, permanently. The room screen had already stopped showing it. #chips is deleted rather than hidden. Hiding is one character of diff and leaves a four-button radiogroup, a roving-tabindex handler, an arrow-key handler and 22 lines of CSS in the shell every phone downloads, all wired to a node nothing renders, and check-dead-code.mjs cannot see any of it because the handlers are still reachable from DOMContentLoaded. The chip bar was the only place the list stated the duration. paintList renders note + strip + caveat + rows and the strip is empty as soon as one row meets the ask, so the list gains one quiet non-interactive line above the rows: You asked for 2h00. It says what was ASKED, not what is offered: "Free for 2h00" over rows that can be shorter is a promise the list does not keep. It spends dur(state.needed), the same call the strip and the empty screen already make two lines away, so one figure on one screen has one vocabulary, and it is the only honest rendering of "rest of day", which is not a fixed length. The empty screen does not get the line, because it opens with an h2 that states the answer in words already. paintChips becomes paintDuration and only syncs the four .opt buttons on the question screen. attachChips and its DOMContentLoaded call are gone. #back is unchanged and still reads "Back to the question" on the list, which is now the only route to the duration. Three tests hold both halves together, and one holds the label. Measured with gzip -9: index.html 36,329 to 35,397 raw, 11,365 to 11,270 gzipped; js/app.js 95,137 to 96,034 raw, 32,494 to 32,841 gzipped. Net +252 bytes gzipped on the shell, all of it the comments explaining the two decisions. docs/media/list.webp and list-full.webp still show the chip bar. scripts/shoot.mjs needs a Chrome that is not on this machine, and no test reads the chips out of those frames, so the suite is green on a stale picture. They have to be reshot. Closes #85 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM
…new page Two defects found reviewing this branch, both in the half of #85 that is about not lying. The line said "You asked for 30 min." to someone who pressed "rest of day". neededMinutes() returns Math.max(30, latestEnd - now), so inside the last half hour of the index's day the clamp wins and dur() prints the floor instead of the ask. Measured on the shipped index, it is wrong every day: Mon 21:26-21:54, Tue 21:16-21:44, Wed 21:21-21:49, Thu 21:16-21:44, Fri 20:06-20:34, Sat 15:31-15:59 all render "30 min" for a button that does not say 30 min, indistinguishable from the button that does. At 08:00 the same call printed the 12h15 the app derived rather than the thing the user chose. The line names the button for "rest of day" and keeps dur() for the three fixed lengths, which is true at every minute of the day. The second is why this could not ship as it stood. sw.js serves navigations network-first and assets cache-first, so the first load after a deploy runs the new index.html against the js/app.js still in the shell cache. That app.js calls attachChips() as the second statement of its DOMContentLoaded listener, and its first line is $('chips').querySelectorAll('.chip'). With #chips deleted that is a TypeError, and it lands before #back, popstate, attachSheet() and boot() are wired: every returning visitor gets a screen frozen on "finding campus..." over four disabled buttons with no back arrow and no error. Re-stamping SHELL_CACHE does not save that load, because the old worker is the one serving it, and js/pwa.js only auto-reloads while the page is untouched, which a frozen screen will not stay. An empty hidden #chips costs 30 bytes and makes the old forEach run over nothing. It is dated in the markup and pinned by a test, and goes after one release. The guard test asserted `id="chips"` was absent, so it now pins the shim as empty and temporary instead. Its stylesheet patterns move to \b: the rule this change actually deleted was `.row, .chip, .opt, ... {`, and a comma is not whitespace, a brace or a bracket, so the old pattern walked past the shape it was written to catch. Comments are stripped before those scans, as sw.test.mjs already does, so prose naming the thing cannot read as the thing returning. Shell gzip figure re-measured over the new bytes: 85,456 on GNU gzip 1.12, 85,300 on node's zlib, 0.183% apart against a 1% tolerance. The header named gzip 1.14; the tool that produced this number is 1.12. 670 tests, 666 pass, 0 fail. check-dead-code --strict: 83 files, no dead code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #85
What changed
The chip bar is gone from the page.
#chips, its four.chipbuttons, itsrole="radiogroup", its 22 lines of CSS,attachChips(the roving-tabindex and arrow-key handler), the$('chips').hidden = name !== 'list'line inshowPane, and the#chipsarm of the sheet's drag exclusion.The list gained the line the chips were carrying.
paintListnow rendersnote + asked() + strip + caveat + rows, whereasked()is one paragraph:.askedis prose at the weight of.prov: dim,.8rem, no border, no target, nothing to press.paintChipsbecamepaintDurationand now only does the half that was never about chips — toggling.primaryon the four#ask .opt[data-min]buttons.Why the line says what it says
Two decisions, both about not overclaiming:
stripgoes empty as soon as one row meets the ask, and the rows under it can be shorter. A heading reading "Free for 2h00" over those rows is a promise the list does not keep. "You asked for 2h00." is true in all four ofpaintList's states, including the one whose strip says nothing near you is free for that long.dur(state.needed), the same call the strip and the empty screen already make two lines away, rather than the chips' own30m / 1h / 2h / rest of day. One figure on one screen gets one vocabulary. It is also the only honest rendering of "rest of day", which is minutes to the last minute the schedule covers and not a fixed length.The empty-results branch does not get the line: it is not a silent list, it opens with an
h2that states the answer in words, and its last branch already printsdur(state.needed)in a sentence of its own.Hide vs delete: deleted
Deleted, deliberately.
$('chips').hidden = trueis one character of diff and leaves a four-button radiogroup, a roving-tabindex handler, an arrow-key handler and 22 lines of CSS in the shell every phone downloads, all wired to a node nothing renders.scripts/check-dead-code.mjscannot see any of it — the handlers stay reachable fromDOMContentLoaded, so they are live code by every measure the checker has, pointed at nothing. It is still green (83 files, no dead code), which is exactly why hiding would have been the worse outcome: the checker would have gone on reporting clean over a dead control, and the next person to readattachChipswould have had no way to tell.#backUnchanged, and confirmed:
showListsetsaria-labeltoBack to the question,index.htmlcarries the same string on#back, and the four.optbuttons it lands on are the same four choices in a bigger target. That is now the only route to the duration, so a test pins the label, the markup and the four choices together.Numbers
Sizes, measured here with
gzip -9:index.htmljs/app.jsNet +252 bytes gzipped on the shell: markup and CSS came off, and the comments explaining the two decisions above went on.
The 61px bar / 57px row / 19%-of-a-321px-sheet figures in the commit message and in
docs/DECISIONS.mdare arithmetic on the shipped CSS and onPEEK = 0.38, not a rendered measurement — no browser ran in this environment. They are the issue's own figures, and they check out against the rules that were in the file.Tests
npm test: 669 tests, 665 pass, 0 fail, 4 skipped. Baseline on main was 666 / 662 / 0 / 4, so the three new tests are the whole difference and nothing regressed.Added to
scripts/test/screens.test.mjs:#chips, no.chip, noradiogroupin the markup or the stylesheet, and nothing injs/app.jsstill reaching for themasked()is in theinnerHTMLconcatenation, it is a<p>and not a control, it spendsdur(state.needed), and it does not say "free for"Mutation-checked: deleting
asked() +from the concatenation fails the second test, and the suite passes again when it is restored.Docs
docs/a11y-contract.md: new section "What the list is a list of" (the line, its weight, what it is not allowed to claim, and#back's name); the two stale chip clauses under Live regions and Tap targets rewrittendocs/DECISIONS.md: an appended dated entry recording hide-vs-delete, the two copy decisions, the measured byte cost, and what is not measuredREADME.mdanddocs/BLUEPRINT.md: "duration chip" → the question screen's buttons, where the prose describes the live app.docs/BACKLOG.mdand olderDECISIONS.mdentries are history and were left alone.js/dev.js,js/install.js,scripts/test/install.test.mjsandscripts/test/screens.test.mjsthat named the chips. The install-test comment records a real 36-case measurement, so it keeps the measurement and gains a line saying the chips have since gone and why the rule outlived them.What I did NOT do
docs/media/list.webpandlist-full.webpwere not reshot, and they still show the chip bar.scripts/shoot.mjsneeds Chrome or Chromium and there is none on this machine (CHROMEunset, nothing in/usr/bin, no Playwright cache). No test reads the chips out offrames.json— the alt-text checks hold README numbers against recorded screen text, and neither frame's text nor either alt string mentions the chips — soreadme.test.mjsis green on a stale picture rather than broken. Someone with a browser needs to runnode scripts/shoot.mjsbefore those frames are believed again. Nothing was skipped or weakened to get there.sw.jswas not re-stamped.scripts/stamp-sw.mjswrites the currentHEADshort SHA and is documented as running in the job that commits. Running it here would have stamped the parent commit and put every parallel branch in conflict on the same line. The shell changed, so whoever deploys should stamp.js/engine.js:80's comment still saysRELAX_LADDER"mirrors the duration chips the UI offers" — true of the four durations, stale about the word. Left alone deliberately: the ranking engine is being edited in parallel.paintList's edit is one addedasked() +line in the concatenation and a block of new comments above the function, kept clear of the four-statestriplogic that The engine says a relaxed answer "has to admit it" and returns the rung that produced it; the app never reads it #90 is rewriting.🤖 Generated with Claude Code
https://claude.ai/code/session_017xiX95r6nnUUmhD3KwMnWM
Generated by Claude Code