Square a button group and the choice drawn as one

Plan step 22, actions.md § Missing (Square-by-default button groups): M3
lists "Default shape | Round, square" as a button-group configuration and
neither `<x-button-group>` nor `<x-group>` had a `shape` prop, so a group
of square buttons still drew round outer corners.

`shape="square"` passes the square corner scale to every child through
`data-shape` and two new variables in groups.css: `--group-outer` is what
the two ends of a group round to (half the height when round, the corner
its inner edges take when square — M3's square table, 4/8/8/16/20dp), and
`--group-square`/`--group-square-pressed` give a standard group's buttons
the scale `<x-button shape="square">` draws. The selected rules keep
`--group-full`, so a selected button in a square group rounds: M3 has a
toggle inside a group swap shape on selection either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 06:40:14 +02:00
co-authored by Claude Opus 5
parent 88c46001fb
commit 5d0e9bc12f
6 changed files with 96 additions and 11 deletions
+36 -7
View File
@@ -13,6 +13,16 @@
* full, the inner corners small, smaller still while pressed, and a selected segment fully round
* (ConnectedButtonGroupSmallTokens and the M3 Expressive connected button group spec).
*
* Shape (`shape="square"`, M3's "Default shape | Round, square" configuration): `--group-outer`
* is what the two ends of a group round to — half the height for a round group, and for a square
* one the same corner its inner edges take, which is the square table M3 publishes for connected
* groups (4/8/8/16/20dp by size). A square *standard* group has no inner edges, so it hands its
* buttons the square corner scale `<x-button shape="square">` draws (`--group-square`, and
* `--group-square-pressed` under the finger) instead of asking the caller to write it on each
* one. Selection morphs the other way in both: M3 has a toggle inside a group "swap shape
* square/round on selection", so a selected button in a square group turns round, which is why
* the selected rules keep `--group-full` rather than `--group-outer`.
*
* Split button (`<x-split-button>`): the same idea for two halves, on its own corner scale —
* SplitButton*Tokens gives the inner corner 4/4/4/8/12px at rest and *grows* it to 8/12/12/20/20px
* under the finger, the opposite of a connected group, so `--split-inner*` is separate from
@@ -34,13 +44,32 @@
--group-inner: var(--md-sys-shape-corner-sm);
--group-inner-pressed: var(--md-sys-shape-corner-xs);
--group-full: 1.25rem;
--group-square: var(--md-sys-shape-corner-md);
--group-square-pressed: var(--md-sys-shape-corner-sm);
--group-outer: var(--group-full);
}
[data-button-group][data-size='xs'] { --group-pad: 1rem; --group-grow: 4px; --group-inner: var(--md-sys-shape-corner-xs); --group-inner-pressed: 2px; --group-full: 1rem; }
[data-button-group][data-size='sm'] { --group-pad: 1rem; --group-grow: 6px; --group-full: 1.25rem; }
[data-button-group][data-size='md'] { --group-pad: 1.5rem; --group-grow: 8px; --group-full: 1.75rem; }
[data-button-group][data-size='lg'] { --group-pad: 3rem; --group-grow: 16px; --group-inner: var(--md-sys-shape-corner-lg); --group-inner-pressed: var(--md-sys-shape-corner-md); --group-full: 3rem; }
[data-button-group][data-size='xl'] { --group-pad: 4rem; --group-grow: 20px; --group-inner: var(--md-sys-shape-corner-lg-increased); --group-inner-pressed: var(--md-sys-shape-corner-lg); --group-full: 4.25rem; }
[data-button-group][data-size='md'] { --group-pad: 1.5rem; --group-grow: 8px; --group-full: 1.75rem; --group-square: var(--md-sys-shape-corner-lg); --group-square-pressed: var(--md-sys-shape-corner-md); }
[data-button-group][data-size='lg'] { --group-pad: 3rem; --group-grow: 16px; --group-inner: var(--md-sys-shape-corner-lg); --group-inner-pressed: var(--md-sys-shape-corner-md); --group-full: 3rem; --group-square: var(--md-sys-shape-corner-xl); --group-square-pressed: var(--md-sys-shape-corner-lg); }
[data-button-group][data-size='xl'] { --group-pad: 4rem; --group-grow: 20px; --group-inner: var(--md-sys-shape-corner-lg-increased); --group-inner-pressed: var(--md-sys-shape-corner-lg); --group-full: 4.25rem; --group-square: var(--md-sys-shape-corner-xl); --group-square-pressed: var(--md-sys-shape-corner-lg); }
/* A square group's ends take the corner its inner edges take — M3's square table for connected
groups — and a square standard group hands its buttons the button's own square scale. */
[data-button-group][data-shape='square'] { --group-outer: var(--group-inner); }
[data-button-group='standard'][data-shape='square'] > * {
border-radius: var(--group-square);
}
[data-button-group='standard'][data-shape='square'] > :active:not(:disabled, [aria-disabled='true']) {
border-radius: var(--group-square-pressed);
}
[data-button-group='standard'][data-shape='square'] > [aria-pressed='true'] {
border-radius: var(--group-full);
}
[data-button-group='standard'] > :not([data-icon-button]):active:not(:disabled, [aria-disabled='true']) {
padding-inline: calc(var(--group-pad) + var(--group-grow));
@@ -107,14 +136,14 @@
[data-button-group='connected'] > :first-child,
[data-split='leading'] {
border-start-start-radius: var(--group-full);
border-end-start-radius: var(--group-full);
border-start-start-radius: var(--group-outer);
border-end-start-radius: var(--group-outer);
}
[data-button-group='connected'] > :last-child,
[data-split='trailing'] {
border-start-end-radius: var(--group-full);
border-end-end-radius: var(--group-full);
border-start-end-radius: var(--group-outer);
border-end-end-radius: var(--group-outer);
}
/*