Design: width-based fade truncation via <truncated-text> (plan for review)#460
Conversation
|
Staging deployment: https://timetracker-staging-claude-truncated-text-fade-des.fly.dev |
|
Thanks — all 7 folded into the doc (rev 2, commit pushed). Point by point:
Spike still ordered before the controller extraction, as you noted. Ready for another look. |
|
Done (rev 3, pushed The rev-2 commitments you called out are all in the doc: 24rem width policy ( |
|
Both fixed (rev 4,
|
|
Fixed (rev 5, Overflow hysteresis. The reveal button is now Added the button-width threshold test: overflow → button appears → grow the viewport just past where the text fits the no-button layout → Doc cleanup: the Decisions scope bullet now says menu items get a hover-only tooltip, consistent with the Menu contract section. |
|
I would request changes on this plan before implementation. The overall architecture is sound, but the reviewed head ( Findings
What is solidThe shared passive tooltip controller is a better fit than the menu engine. Closing on overflow→fit and positioning the reveal button out of flow correctly address state leakage and hysteresis. I also verified that the proposed After the four P1 items are resolved and the multi-game behavior is explicit, the plan should be ready to implement. Minor cleanup: #455 is already closed, and the lockfile currently resolves Tailwind 4.3.0 rather than the 4.1.18 named in the document. |
|
Thanks — this was a genuinely deep review; all four P1s and both P2s are resolved in rev 6 ( P1 #1 — spike measures the wrong element. Corrected: the acceptance is now the table's own P1 #2 — invalid, non-unique ids. Fixed: P1 #3 — icons/hit area. Added a P1 #4 — unlinked keyboard. The element manages an overflow-only P2 #5 — multi-game. Folded into P2 #6 — contracts. Minor: #455 left closed (no action); Tailwind version reference corrected to lockfile 4.3.0. |
|
Strong review — all 8 folded into rev 7 ( 1 (blocker — the big simplification). You're right: 2 (blocker — shipping bug). Correct and subtle: the 3. DEBUG render-time duplicate-id assertion in the Architecture (shared passive controller, self-capping element, spike-first) unchanged. |
Per review: generate the panel id and aria-describedby in the server markup (matching the _popover_html contract) so the a11y relationship exists before JS init, instead of client-side. Keep ids unique for duplicate names via a required pk-based id_seed (content-only seed would collide); one distinct id_seed per truncated cell (the name and sort-name cells share a row). Co-Authored-By: Claude Opus 4.8 <[email protected]>
Per review: - host is a Tailwind `group`; overflow-reactive children (clip mask, reveal button) use `group-data-[overflowing]:` — a bare `data-[overflowing]:` on a child matches the child's own attribute, not the host's, so fade/button wouldn't react while detection still "worked". - menu (caller-wrapped `<a>` ancestor) corrected to hover-only + tap-through: focus on the ancestor `<a>` can't propagate down into the host, so no focus-triggered tooltip there (same as pop-over). Focus tooltips apply to linked table names where the `<a>` is a descendant of the host. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Per review: an inline-flex sibling reveal button consumes ~24px when shown, shrinking the clip so data-overflowing stays true even after the viewport grows past where the text fits without the button — a stuck-open feedback loop. Position the button absolutely over the fade area (out of flow) so the clip's measured width is independent of the button. Add the button-width growth-threshold test. Make the Decisions/menu wording consistent (hover-only). Co-Authored-By: Claude Opus 4.8 <[email protected]>
- P1#1 spike measures the table's own overflow-x-auto scroll wrapper (not documentElement, which the wrapper hides); fallback constrains both cell table-sizing and child used width; assert at 390/640/768. - P1#2 ids use randomid(content=...) (seed= returns raw seed for long names) with a context+slot instance_key (navbar renders each session twice) + full-page id-uniqueness/aria-describedby-resolves invariant. - P1#3 add a `leading` slot: icons stay inside the link, focus ring outside the overflow-hidden clip; explicit linked/unlinked DOM nesting. - P1#4 unlinked tap=True (sort-name) gets an overflow-only, element-managed tabindex so desktop keyboard can reveal; short names gain no tab stop. - P2#5 multi-game bundles fold into TruncatedText (reveal="always" + tooltip_content=games-list); no competing popover, no char-truncation left. - P2#6 TableRow(data, columns=None) optional (production call outside StyledTable); controller content/arrow optional (pop-over fixture lacks content); e2e name-reveal selectors migrate to truncated-text. - Minor: #455 already closed; Tailwind lockfile is 4.3.0. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Deep review (2 blockers + 2 major + nits):
- finding 1 (big simplification): the clipped full text is already in the
a11y tree, so the default tooltip's aria-describedby duplicates the name.
Drop panel id + instance_key + the randomid/id-uniqueness machinery for the
common case; keep the ARIA relationship only for multi-game (differing
content). Deletes most of what revs 3-6 built.
- finding 2 (shipping bug): the font-condensed webfont swap doesn't change the
clip's border-box, so the ResizeObserver never re-fires and data-overflowing
goes stale. Re-measure all instances on document.fonts.ready.
- finding 3: DEBUG render-time duplicate-id assertion (not fixture-only).
- finding 4: 1px epsilon on scrollWidth>clientWidth (subpixel false-overflow).
- finding 5: icons always outside the clip (one structure, linked + unlinked).
- finding 6: generic reveal_label default ("Show full text").
- finding 7: reuse data-pop-over-{panel,content,arrow} for the panel anatomy.
- finding 8: corrected the "SR loses characters" claim.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Remove the Rev 2-7 changelog block, all review/finding/P1/P2 tags, and draft-relative narration; recast load-bearing rationale as present-tense design statements. No technical decision changed. Also reconcile the panel data-attribute wording (pop-over anatomy shared) in the controller section. Co-Authored-By: Claude Opus 4.8 <[email protected]>
97bb297 to
23679dc
Compare
Plan/design PR — no implementation yet. Review the design doc; once approved I'll implement it on a follow-up (or this) branch.
Adds
docs/superpowers/specs/2026-07-21-truncated-text-fade-design.md.Summary
Replace character-count name truncation (
truncate_info, fixed 30 chars,⋯reveal) with width-based truncation: a new<truncated-text>custom element clips by the cell/container width, fades the right edge with amask-imagegradient (no ellipsis), detects real overflow client-side viaResizeObserver, and gates the hover (desktop) / tap-button (mobile) reveal to appear only when actually clipped. Full text stays in the DOM, so screen readers read the whole name.Key decisions captured
max-w-[cap] min-w-0+ inneroverflow-hidden) so the auto-layout table column caps + clips — notable-fixed. Gated behind a Step-0 empirical spike (table-auto clipping is the one real CSS risk; fallback is amax-won the name<th>).[@media(hover:hover)]:hidden, from Touch-target size audit for small popover triggers (WCAG 2.5.8) #454) and now shows only when overflowing.attachMenuengine is click/keyboard-menu-shaped (roving focus,role=menu, click-toggle) and unsuitable for a passive tooltip — confirmed by exploration. Instead, extract pop-over's passive state machine into a reusabletooltip-behavior.tscontroller that bothpop-overand<truncated-text>ride, reusing the already-sharedpositionAnchored+bindPopupDismiss.NameWithIcon(linked + menu),LinkedPurchasesingle-game, sort-name column. Multi-game games-list stays an always-on info popover (not truncation). Menu items get fade-only + tap-through, resolving the Link-wrapped truncation tooltips still hover-only on touch #455 residual.Verification plan (for the implementation PR)
Empirical spike → full
make check→ vitest (overflow state machine + pop-over regression) → Playwright (desktop hover, mobile-emulated overflow button+fade+tap, short-name no-op) →test_html_validityinvariant → desktop/mobile/short-name screenshots.🤖 Generated with Claude Code