Give the switcher back its spacing by measuring the pill - #2204
Merged
Merged
Conversation
The Activities row reads as one solid block of words. The cause is not the padding — it is that every option was made as wide as the widest of them, so a pill of one seventh of the track would land exactly on one. "Favourites" with its icon is 98 pixels of content, a seventh of a timeline column is about 100, and that leaves roughly a pixel of padding either side. Every option paid the width of the longest one. The notifications page had already halved the switcher's padding to stop seven options running past the column, which made it tighter still. So the pill is measured off the option it sits under and the options are free to be as wide as their own words. "All" and "Polls" stop claiming the room "Favourites" needs, and the slack that frees — nearly 200 pixels across the seven — goes back into padding for all of them. Measured on a real page afterwards: every option now has about 34 pixels of it, where the widest had one, and the row still fits the column. The per-page squeeze is gone with it. The measurement is geometry and nothing else. The distance is taken from the *first* option rather than from the track, which cancels the track's padding out of both sides of the subtraction: the pill already sits after that padding, and travelling it twice is what put it three pixels off its option the first time I tried this. `offsetLeft` is physical while the pill is anchored to the inline start, so a right-to-left interface measures from the other edge and travels negative. The fallback is the old behaviour and is still exercised: until something has a width — the first paint, and jsdom — the pill is an equal share of the track positioned by index. A ResizeObserver watches the options rather than only the track, because they change width without it doing so: a translation arriving, a font loading, the labels giving way to their icons at a breakpoint. While here, one duplicate breakpoint goes. Two `@media (max-width: 500px)` blocks both hid the labels on a phone, and the first did it with `display: none` — which takes the label out of the accessibility tree and so defeated the second, written carefully to keep it there. A radiogroup of seven unnamed buttons is not a control anybody can use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Frank Karlitschek <frank@nextcloud.com>
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.
The Activities row reads as one solid block of words.
The cause is not the padding. Every option was made as wide as the widest of them, so that a pill of exactly one seventh of the track would land on one — "Favourites" with its icon is 98 pixels of content, a seventh of a timeline column is about 100, and that leaves roughly one pixel of padding either side. Every option paid the width of the longest one. The notifications page had already halved the switcher's own padding to stop seven options running past the column, which made it tighter still.
So the pill is measured off the option it sits under, and the options are free to be as wide as their own words. "All" and "Polls" stop claiming the room "Favourites" needs, and the slack that frees — nearly 200 pixels across the seven — goes back into padding for all of them. The per-page squeeze goes with it.
Measured on a running instance, before and after:
What the measurement is careful about
It is geometry and nothing else. The distance is taken from the first option rather than from the track, which cancels the track's padding out of both sides of the subtraction: the pill already sits after that padding, and travelling it twice is exactly what put the pill three pixels off its option the first time I tried this.
offsetLeftis physical while the pill is anchored to the inline start, so a right-to-left interface measures from the other edge and travels negative.The old behaviour is the fallback, and is still exercised. Until something has a width — the first paint, and jsdom — the pill is an equal share of the track positioned by index, exactly as before. Four tests cover the two paths and the switch between them.
A ResizeObserver watches the options, not only the track. They change width without the track doing so: a translation arriving, a font loading, the labels giving way to their icons at a breakpoint.
One thing found on the way
Two identical
@media (max-width: 500px)blocks both hid the labels on a phone, and the first did it withdisplay: none— which takes the label out of the accessibility tree and so silently defeated the second, written carefully to keep it there. A radiogroup of seven unnamed buttons is not a control anybody can use. The duplicate is gone; verified on a running instance that the label is now visually hidden and still in the tree.Checked
On a running instance: the pill lands within 0.2 px of the active option on every option clicked, the three-option switcher (My Feed / Local / Global) is unchanged at 343 px, and at a 620 px window the labels give way to icons with the control intact and the pill still aligned.
2145 JS tests and 5021 PHP tests pass; psalm, eslint, stylelint and the type check are clean, and the committed bundle matches a fresh build.
🤖 Generated with Claude Code