refactor(ui): one extended FAB for every screen's primary action, with a gap in it
Twenty screens end their bottom bar in an `ExtendedFloatingActionButton` --
twenty-one buttons, one screen having two -- and eighteen of those were built
from the content-lambda overload with an `Icon` directly followed by a `Text`.
That overload puts nothing between the two, so the icon touched the label on
every one of them: visible in any render, and unnoticed because every FAB in
the app looked the same. Thirteen of the eighteen also carried the same
twenty-line block for looking disabled, because M3 gives a FAB no `enabled`.
**`ExtendedFab`, once, in widgets/buttons.** A label, an icon slot, `enabled`
and `onClick`. It draws `space150` between icon and label, which is the 12dp
M3 names `ExtendedFabEndIconPadding`; it borrows the disabled colours every
other button in the app uses, marks the node disabled so a screen reader does
not announce a button it is happy to press, and drops the press -- so the
thirteen call sites lose their `if (!canX) return@…` guards along with the
block and the `buttonColors` each of them declared. An icon slot rather than
an image, because two screens put a progress indicator where the icon was
while the action they started is out, and they still do.
**Not M3's `text`/`icon` overload, which would have spaced them for free.**
Its source wraps the label in `clearAndSetSemantics {}`: the label leaves the
merged semantics tree, a screen reader is told only what the icon says, and a
test cannot find the button by what it says -- five screen tests do. The
three FABs that were on that overload move to the widget too, and two of them,
Home's "New chat" and "Start the key ceremony", had a decorative icon beside
the cleared label and so no accessible name at all until now.
`ReadOnlyEntrancesJvmTest` had gone to the unmerged tree to find "New chat"
for this reason; the habit is harmless and stays. Every icon's description is
as it was, since several tests reach their FAB by it.
`ExtendedFabJvmTest` pins the decision: found by its label in the merged tree,
the icon before the label with a gap between, and disabled both announced and
refusing the press. The two docs that recorded the old state -- the
conformance account's "left alone" and the npub plan's unmerged-tree note --
each say what superseded them.
Verified with :composeApp:m3Audit (all budgets met, 0 dp literals in spacing
positions), :composeApp:compileDebugKotlinAndroid and :composeApp:jvmTest
(1088 tests, 3 new).
Replayed onto Mantra by docs/curated-to-mantra.md: AcceptCuratedSuggestionScreen.kt: deleted, as this commit deletes it upstream; AddGroupPostScreen.kt: deleted, as this commit deletes it upstream; EditGroupCuratedSchemaScreen.kt: deleted, as this commit deletes it upstream; EditGroupNostrProfileScreen.kt: deleted, as this commit deletes it upstream; EditGroupRelaysScreen.kt: deleted, as this commit deletes it upstream; GroupCuratedEntriesScreen.kt: deleted, as this commit deletes it upstream; ProposeGroupEventScreen.kt: deleted, as this commit deletes it upstream.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pulled-From: curated/curated@ca6c16beb0
This commit is contained in:
@@ -696,7 +696,10 @@ let the component apply the 38% state layer. No `FloatingActionButton` overload
|
||||
material3 1.10 takes `enabled` — the spec's position is that an unavailable FAB should not
|
||||
appear at all — so hand-computing the colours is the only way to show one, and the existing
|
||||
code already pairs it with `Modifier.semantics { disabled() }` so a screen reader does not
|
||||
announce a button it is happy to press. Left alone.
|
||||
announce a button it is happy to press. Left alone at the time; since done once, in
|
||||
`ExtendedFab`, which every screen's bottom-bar action now goes through and which also
|
||||
puts M3's own 12dp between the icon and the label — the content overload the screens were
|
||||
using put nothing there.
|
||||
|
||||
**Left for a person.** Eight more screens have two or more filled buttons competing:
|
||||
LandingScreen's "Sign in" beside "Create profile", SocialPreconditionScreen's "Invite a
|
||||
|
||||
@@ -36,6 +36,9 @@ floating action button's label by text has to search the unmerged tree, because
|
||||
`ExtendedFloatingActionButton` merges its label into the button's semantics — and
|
||||
on the merged tree an `assertDoesNotExist` for a hidden control is vacuously true.
|
||||
`ReadOnlyEntrancesJvmTest` uses the unmerged tree for every lookup for that reason.
|
||||
(More precisely, the `text`/`icon` overload *clears* the label's semantics, so the
|
||||
merged tree never had it; the shared `ExtendedFab` that replaced it keeps the label
|
||||
in the merged tree, and the test's habit is now only a habit.)
|
||||
|
||||
The library commit is on `claude/nostr-credentials` in the submodule, at `84cc44c`,
|
||||
one commit ahead of the library's `master` and bumped into the app by the Phase 2
|
||||
|
||||
Reference in New Issue
Block a user