Skip to content

feat: a shortcut legend as a dropup on the bottom edge - #266

Merged
BernardJen merged 1 commit into
mainfrom
feat/shortcut-legend
Aug 20, 2026
Merged

feat: a shortcut legend as a dropup on the bottom edge#266
BernardJen merged 1 commit into
mainfrom
feat/shortcut-legend

Conversation

@BernardJen

Copy link
Copy Markdown
Contributor

The mirror of the input dropdown, anchored to the bottom edge: same hover-to-reveal, same
0.3s slide, same touch-open class, same cursor handling, corners rounded on the side away
from the edge it hangs off. Escape closes it alongside the other panels.

It reads from the same list as everything else

Rows come from shortcuts.js, making this the third consumer after the keydown handler and
the Settings table (#258). It shows the same rows as that table rather than a shortened
"important ones" set — that would be a fourth hand-maintained list, which is what the shared
list exists to remove. A test pins the two to identical chips.

Wider than the dropdown on purpose. The dropdown is a picker capped at 400px; this is a
reference table and the wall is 5:1, so an auto-fit grid turns twelve rows into three rows of
four columns instead of one column twelve deep.

Measured at a 1500px viewport: 1100×183, four 244px tracks, on the bottom edge, no
scrolling, nothing truncated.

Two things measuring caught that reasoning would not have

A fixed-position element is shrink-to-fit, so max-width caps it but never expands it —
and repeat(auto-fit, minmax(...)) needs a definite width to work out its column count.
With only a max the panel sized to its content minimum: 467px wide and one column at a
1500px viewport, which is precisely the long-list-off-the-bottom the grid was there to
prevent. Fixed with an explicit width: min(1100px, 92vw).

Three of twelve labels were ellipsising at 1100px, including both "(if the remote
keyboard is enabled)" caveats — the part that makes those two rows mean anything. Labels now
wrap. Uneven row heights are the cheaper cost; a legend that hides what a key does defeats
itself. Same trade as the dual-column names in #263.

Tests

13 new, 582 → 595. Verified by breaking each:

Mutation Tests failed
render a shortened set (a fourth list) 4
drop the explicit width 1
truncate labels instead of wrapping 1
remove the Escape close 1

A note on how this was verified

The Browser pane reports document.hidden: true and never goes visible, so the transition
clock is frozen and getComputedStyle returns the pre-transition value indefinitely. Reading
the panel's transform after a toggle showed translateY(0) in both states, which looked
like a broken CSS rule and was not — same trap as #262, and I nearly "fixed" a non-problem.

Disabling the transition makes the change land in one style recalculation, which is readable
regardless of the clock: closed translateY(183) fully off-screen, open translateY(0) on
the bottom edge, trigger 0 → 0.7 with the cursor, arrow rotated.

Not verified

The slide itself in motion, for the same frozen-clock reason. The geometry at both ends is
measured; the 0.3s between them is inherited verbatim from the dropdown, which has been
shipping.

The mirror of the input dropdown, anchored to the bottom edge instead of the top:
same hover-to-reveal, same 0.3s slide, same touch-open class, same cursor
handling, corners rounded on the side away from the edge it hangs off.

Rows come from shortcuts.js, which makes this the third consumer of that list
after the keydown handler and the Settings table (#258). It shows the SAME rows as
the table rather than a shortened "important ones" set -- that would have been a
fourth hand-maintained list, which is the thing that list exists to remove. A test
asserts the two carry identical chips.

Wider than the dropdown on purpose. The dropdown is a picker capped at 400px; this
is a reference table and the wall is 5:1, so an auto-fit grid turns twelve rows
into three rows of four columns instead of one column twelve deep. Measured at a
1500px viewport: 1100x183, four 244px tracks, sitting on the bottom edge, no
scrolling.

Two things measuring caught that reasoning would not have:

**A fixed-position element is shrink-to-fit, so `max-width` caps it but never
expands it** -- and `repeat(auto-fit, minmax(...))` needs a DEFINITE width to work
out its column count. With only a max the panel sized to its content minimum: 467px
wide and one column at 1500px, which is precisely the long-list-off-the-bottom the
grid was there to prevent. Fixed with an explicit `width: min(1100px, 92vw)`.

**Three of twelve labels were ellipsising at 1100px**, including both
"(if the remote keyboard is enabled)" caveats -- the part that makes those two rows
mean anything. Labels now wrap. Uneven row heights are the cheaper cost; a legend
that hides what a key does defeats itself. Same trade as the dual-column names in
#263.

Escape closes it, alongside the other panels.

13 new tests, 582 -> 595. Verified by breaking each: dropping the explicit width
fails 1, truncating labels 1, removing the Escape close 1, and rendering a
shortened set 4 -- including the test that pins the legend and the Settings table
to identical chips.

One note on verification method. The Browser pane reports document.hidden: true and
never goes visible, so the transition clock is frozen and getComputedStyle returns
the pre-transition value indefinitely. Reading the panel's transform after a toggle
showed translateY(0) in both states, which looked like a broken rule and was not --
it was the same trap as #262. Disabling the transition makes the change land in one
style recalculation, which is readable regardless: closed translateY(183) fully
off-screen, open translateY(0) on the bottom edge, trigger 0 -> 0.7 with the
cursor.
@BernardJen
BernardJen merged commit 74958eb into main Aug 20, 2026
1 check passed
@BernardJen
BernardJen deleted the feat/shortcut-legend branch August 20, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant