Skip to content

Button groups — Expressive extension

components specs/components/button-groups-expressive.kmd

Material 3 Expressive extension to base button groups (specs/components/buttons.kmd §Button groups). Adds 5 sizes (XS-XL), connected appearance (no gap + shared corners), shape/motion transformations on hover/press via spring. Strictly extends; baseline buttons.kmd remains valid.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Button groups Expressive

Strict extension to buttons.kmd §Button groups. Compatible: code using base button groups remains valid; Expressive opt-in per surface OR per preset.

Princípios

  1. Connected by default — sem gap entre items; visual unified group.
  2. 5 sizes XS-XL — granular sizing extending baseline 3 sizes.
  3. Shape morph on press — corner radius pulls toward pressed item.
  4. Scale on hover — 1.0 → 1.05 via spring (desktop + tablet hover).
  5. Reduced-motion safe — animations skipped under prefers-reduced-motion.

R1 — Anatomy (extension)

Base anatomy per buttons.kmd §Button groups. Expressive adds:

   normal:    ┌──┬──┬──┬──┐
              │ A│ B│ C│ D│
              └──┴──┴──┴──┘
   hover B:   ┌──┬────┬──┬──┐   ← B scales 1.05 + neighbors compress
              │ A│  B │ C│ D│
              └──┴────┴──┴──┘
   press C:   ┌──┬──┬──┬──┐
              │ A│ B│CCC│ D│   ← C grabs visual focus
              └──┴──┴──┴──┘

R2 — 5 sizes

SizeItem height (dp)Padding L+RMin item width
XS28848
S321056
M401664
L482072
XL562480

R3 — Connected appearance

  • No gap entre items (0dp).
  • Shared inner border (1dp; color outline-variant).
  • Group radius: shape-corner-small or shape-corner-medium (per preset; xs/sm in expressive default).
  • Items at edges share group radius; inner items square.

R4 — Hover state

Hover an item (desktop/mouse OR tablet stylus-hover):

  • Scale: 1.0 → 1.05 over 200ms spring motion-spatial-fast (per motion.kmd R9.1).
  • Neighbors compress slightly (max 5% width reduction) to make room.
  • Group radius shifts toward hovered item.
  • Color shift: hovered item background surface-container-highest.

R5 — Press state

Press item:

  • Scale: 1.0 → 0.97 (subtle "click" feel) via spring.
  • Group radius "pulls toward" pressed item (radius asymmetric briefly).
  • Color: primary-container (filled style) OR primary text (outlined).
  • Release: spring back to default state.

R6 — Selected state (for toggle groups)

Selected item in toggle group:

  • Background: secondary-container (tonal).
  • Persistent (not press-flash).
  • Shape: filled in corner radius of group.

Multi-select OR single-select per host config.

R7 — Surface bindings

SurfaceAPI
FlutterExtension of KoderButtonGroup (per buttons.kmd); pass expressive: true flag
Web<koder-button-group expressive>
ComposeKoderButtonGroup(expressive = true)
SwiftUIidem
CLI / TUIFlat text; expressive ignored

R8 — Acessibilidade

  • Buttons individually focusable per buttons.kmd.
  • Hover scale: visual only — does NOT affect tab order.
  • Reduced-motion: hover scale + radius shift disabled; color shift remains.
  • Touch target ≥ 28dp per size XS (sizes S+ already meet 32+).

R9 — i18n

Inherits from buttons.kmd. No new keys.

R10 — Per-preset variation

PresetExpressive button group
material3Subtle expressive (smaller scale 1.02; weaker shape morph)
material_expressiveFull expressive (defaults from this spec)
material2Disabled — falls back to baseline buttons.kmd
terminal_classicPlain [A][B][C] text — no expressive
brutalistNo hover scale; no shape morph; sharp corners only
cyberpunk_neonDefault + glow on hovered/pressed
minimalist_monoHover changes underline only; no scale/morph

T-suite

  • T1 Render 3-item group XS → height 28dp; min width 48dp each.
  • T2 Hover item: scale 1.0 → 1.05 via spring; neighbors compress.
  • T3 Press item: scale → 0.97; release → springs back.
  • T4 Group radius asymmetric during press; reverts on release.
  • T5 Selected state: persistent secondary-container fill.
  • T6 Reduced-motion: no scale + no shape morph; color shift kept.
  • T7 Touch target ≥ 28dp for XS; S+ ≥ 32dp.
  • T8 Disable expressive (flag false): falls back to base buttons.kmd behavior.
  • T9 A11y: tab order unchanged by scale.
  • N1 Material2 preset: expressive ignored (graceful).

Requirements (testable)

Requirement: Five expressive sizes {#req-button-groups-expressive-sizes}

An Expressive button group SHALL support exactly five sizes XS, S, M, L, XL, each rendering its items at the item height, horizontal padding, and minimum item width defined in R2, extending the baseline three sizes without altering them.

Scenario: XS size renders at the R2 dimensions

  • GIVEN an Expressive button group with 3 items configured at size XS
  • WHEN the group is rendered
  • THEN each item renders at item height 28dp
  • AND each item minimum width is 48dp
  • AND each item horizontal padding (L+R) is 8dp per the R2 table

Scenario: XL size renders at the R2 dimensions

  • GIVEN an Expressive button group with 3 items configured at size XL
  • WHEN the group is rendered
  • THEN each item renders at item height 56dp
  • AND each item minimum width is 80dp
  • AND each item horizontal padding (L+R) is 24dp per the R2 table

Requirement: Connected appearance {#req-button-groups-expressive-connected}

An Expressive button group SHALL render its items connected — zero gap between adjacent items, a shared 1dp inner border in color outline-variant, with only the edge items carrying the group corner radius and inner items kept square.

Scenario: Items are connected with no gap

  • GIVEN an Expressive button group with 4 items A, B, C, D
  • WHEN the group is rendered
  • THEN the gap between adjacent items is 0dp
  • AND adjacent items share a 1dp inner border colored outline-variant

Scenario: Only edge items carry the group radius

  • GIVEN an Expressive button group with 4 items A, B, C, D
  • WHEN the group is rendered
  • THEN items A and D apply the group corner radius on their outer corners
  • AND inner items B and C render with square corners

Requirement: Hover and press feedback {#req-button-groups-expressive-hover-press}

An Expressive button group SHALL provide spring-driven feedback: hovering an item scales it 1.0 to 1.05 over a 200ms spring while neighbors compress by at most 5% width, and pressing an item scales it to 0.97 and springs back to the default state on release.

Scenario: Hovering an item scales it and compresses neighbors

  • GIVEN a rendered Expressive button group on a hover-capable surface
  • WHEN the pointer hovers item B
  • THEN item B scales from 1.0 to 1.05 via a 200ms spring
  • AND neighboring items compress in width by no more than 5%
  • AND item B background becomes surface-container-highest

Scenario: Pressing then releasing springs back

  • GIVEN a rendered Expressive button group
  • WHEN item C is pressed
  • THEN item C scales toward 0.97
  • AND the group radius becomes asymmetric pulling toward item C
  • WHEN the press is released
  • THEN item C springs back to scale 1.0
  • AND the group radius reverts to its default symmetric state

Requirement: Persistent selected state {#req-button-groups-expressive-selected}

In a toggle group an Expressive item that is selected SHALL show a persistent secondary-container tonal background that remains after the press flash ends, honoring the host's single-select or multi-select configuration.

Scenario: Selected item keeps its tonal fill

  • GIVEN an Expressive toggle button group
  • WHEN a user selects item B
  • THEN item B background is secondary-container
  • AND the fill persists after the press interaction ends rather than flashing away

Requirement: Accessibility and reduced-motion {#req-button-groups-expressive-a11y}

An Expressive button group SHALL keep each item individually focusable with tab order unaffected by hover scaling, SHALL keep touch targets at least 28dp at size XS, and under prefers-reduced-motion SHALL disable the hover scale and radius shift while retaining the color shift.

Scenario: Hover scaling does not change tab order

  • GIVEN a rendered Expressive button group with items A, B, C, D focusable in order
  • WHEN item B is hovered and scales to 1.05
  • THEN keyboard tab order remains A, B, C, D unchanged

Scenario: Reduced-motion disables scale but keeps color

  • GIVEN prefers-reduced-motion is active
  • WHEN a user hovers an item in an Expressive button group
  • THEN the hover scale and radius shift are not applied
  • AND the hovered-item color shift is still applied
  • AND touch targets remain at least 28dp at size XS

References