Hide a disabled fab button on a compact window, as M3 removes a FAB

`<x-button fab>` is an extended FAB below `medium` and a filled button
from there, but a disabled one was still drawn as the FAB below
`medium`: the compact FAB's rule set no state of its own, so the button
stood greyed out and fixed over the content. M3 never disables a FAB:
"if its action is unavailable, remove the FAB entirely".

A `fab` given `disabled` (a `<button disabled>`, or a link's
`aria-disabled="true"`) now renders `data-md-unavailable`, and
button.css draws it `display: none` below `medium`, which also takes it
out of the accessibility tree and the Tab order; from `medium` it is
the disabled filled button it was. The mark comes from the prop, not
from `:disabled`, because a `spinner` puts `disabled` on its button
while the action runs, and that FAB is busy rather than unavailable:
hidden, it would vanish instead of showing its loading indicator. The
docblocks and the skill's button table say so.

A browser test renders an enabled fab with a slow spinner action, a
disabled one and a disabled link on a 393px window: the disabled two
are not rendered, the busy one stays on screen with its indicator, and
at 600px both disabled ones are the disabled button again; it fails
without the change in Chrome, Firefox and Safari (and in Chrome with
`:disabled` alone as the condition, on the busy FAB). A feature test
reads the mark and the rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Reinhold / reini
2026-09-17 07:00:40 +02:00
co-authored by Claude Opus 5
parent cf63f11bfb
commit 6d4487feb3
6 changed files with 104 additions and 3 deletions
+10 -1
View File
@@ -44,7 +44,9 @@
* primary-container at elevation 3 (ExtendedFabPrimaryTokens) — lifted clear of a bottom bar and
* of a snackbar on screen (`--material-bottom-bar`, `--material-snackbar-height`), because M3 puts a
* snackbar above a FAB and never over one. From `medium` it is the button it was written as.
* `data-md-responsive` on the label hides it below `expanded` (840px).
* `data-md-unavailable`, a `fab` given `disabled`, is not drawn below `medium`: M3 never shows a
* disabled FAB, but removes one whose action is unavailable. From `medium` it is the disabled
* button. `data-md-responsive` on the label hides it below `expanded` (840px).
*/
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@@ -397,5 +399,12 @@
inline-size: 24px;
block-size: 24px;
}
/* M3 never disables a FAB: "if its action is unavailable, remove the FAB entirely"
(docs/reference/m3/components-actions-communication-containment.md § FAB). Not drawn,
it is out of the accessibility tree and the Tab order too. */
[data-md-button][data-md-compact-fab][data-md-unavailable] {
display: none;
}
}
}