/* * : a choice of a few options drawn as M3 Expressive's connected button group, the * successor of the segmented button — native radios (checkboxes with `data-md-multiple`) under * segments that share the row, or hug their labels with `data-md-inline`. * * The segments are M3's buttons at the group's `data-md-size` (Button*Tokens: 32/40/56/96/136px * tall, 16/16/24/48/64px either side, 8/8/8/12/16px between icon and label, label-large, label- * large, title-medium, headline-small, headline-large), with the connected shape from * button-group.css: 2px apart, small inner corners, the chosen segment fully round. At `xs` and * `sm` a segment carries M3's 48px target and a 48px minimum width, which M3 asks for by name. * * `data-md-variant` is the toggle button's colours: `tonal` (secondary-container, chosen in * secondary), `filled` (surface-container behind on-surface-variant, chosen in primary) or * `outlined` (an outline-variant edge, chosen in inverse-surface). A checked input picks the * chosen colours (`:has(:checked)`), a disabled one the disabled treatment — on-surface at 10% * behind 38% text. Corners move on the fast spatial spring and colours on the fast effects spring * beside it. * * A segment renders the foundation's `md-state-layer` (foundation/interaction.css) on the label * itself, which reaches the same `:hover`/`:active` as the class expects; below `medium`'s smallest * two sizes it renders `md-touch-target` too. The real control is the `` inside, not the * label, so keyboard focus and the disabled state are read off it with `:has()` — the one * difference the class cannot draw, kept here as a refinement. * * The legend is label-large and the hint body-small, both in on-surface-variant; a validation * message takes the hint's place in error. `hint-class` lands on the hint, where a caller's class * outranks the colour here. */ @layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility; @import './icon.css'; @import './button-group.css'; @layer material.components { [data-md-group] { min-inline-size: 0; } [data-md-group-legend] { margin-block-end: var(--md-sys-measurement-space100); color: var(--md-sys-color-on-surface-variant); font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); font-variation-settings: normal; } [data-md-group] > [data-md-button-group] { display: flex; align-items: normal; inline-size: 100%; } [data-md-group][data-md-inline] > [data-md-button-group] { inline-size: fit-content; } [data-md-group-segment] { --md-group-container: var(--md-sys-color-secondary-container); --md-group-label: var(--md-sys-color-on-secondary-container); display: flex; flex: 1 1 0%; align-items: center; justify-content: center; min-inline-size: 0; background-color: var(--md-group-container); color: var(--md-group-label); white-space: nowrap; cursor: pointer; -webkit-user-select: none; user-select: none; transition-property: border-radius, padding, margin, bottom, background-color, color, box-shadow; transition-duration: var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration), var(--md-sys-motion-effects-fast-duration); transition-timing-function: var(--md-sys-motion-spatial-fast), var(--md-sys-motion-spatial-fast), var(--md-sys-motion-spatial-fast), var(--md-sys-motion-spatial-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast); /* Focus lands on the input inside, not the label, so it is read with :has(); the state layer's own opacity joins the ring, as the foundation class draws for a focused control. */ &:has(:focus-visible) { outline: 3px solid var(--md-sys-color-secondary); outline-offset: 2px; &::before { opacity: var(--md-sys-state-focus-state-layer-opacity); } } /* Disabled is read off the input too: the class's own :disabled/aria-disabled rule never fires on the label it never reaches. */ &:has(:disabled)::before { display: none; } /* The radio or checkbox stays in the page, for the form, the keyboard and a screen reader. */ & > input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border-width: 0; } } [data-md-group][data-md-inline] [data-md-group-segment] { flex: initial; } [data-md-group]:is([data-md-size='xs'], [data-md-size='sm']) [data-md-group-segment] { min-inline-size: var(--md-sys-measurement-space600); } [data-md-group][data-md-size='xs'] [data-md-group-segment] { gap: var(--md-sys-measurement-space100); block-size: 32px; padding-inline: var(--md-sys-measurement-space200); font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); font-variation-settings: normal; } [data-md-group][data-md-size='sm'] [data-md-group-segment] { gap: var(--md-sys-measurement-space100); block-size: 40px; padding-inline: var(--md-sys-measurement-space200); font: var(--md-sys-typescale-label-lg); letter-spacing: var(--md-sys-typescale-label-lg-tracking); font-variation-settings: normal; } [data-md-group][data-md-size='md'] [data-md-group-segment] { gap: var(--md-sys-measurement-space100); block-size: 56px; padding-inline: var(--md-sys-measurement-space300); font: var(--md-sys-typescale-title-md); letter-spacing: var(--md-sys-typescale-title-md-tracking); font-variation-settings: normal; } [data-md-group][data-md-size='lg'] [data-md-group-segment] { gap: 12px; block-size: 96px; padding-inline: var(--md-sys-measurement-space600); font: var(--md-sys-typescale-headline-sm); letter-spacing: var(--md-sys-typescale-headline-sm-tracking); font-variation-settings: normal; } [data-md-group][data-md-size='xl'] [data-md-group-segment] { gap: var(--md-sys-measurement-space200); block-size: 136px; padding-inline: var(--md-sys-measurement-space800); font: var(--md-sys-typescale-headline-lg); letter-spacing: var(--md-sys-typescale-headline-lg-tracking); font-variation-settings: normal; } [data-md-group-segment]:has(:checked) { --md-group-container: var(--md-sys-color-secondary); --md-group-label: var(--md-sys-color-on-secondary); } [data-md-group][data-md-variant='filled'] [data-md-group-segment] { --md-group-container: var(--md-sys-color-surface-container); --md-group-label: var(--md-sys-color-on-surface-variant); &:has(:checked) { --md-group-container: var(--md-sys-color-primary); --md-group-label: var(--md-sys-color-on-primary); } } [data-md-group][data-md-variant='outlined'] [data-md-group-segment] { --md-group-container: transparent; --md-group-label: var(--md-sys-color-on-surface-variant); border: 1px solid var(--md-sys-color-outline-variant); &:has(:checked) { --md-group-container: var(--md-sys-color-inverse-surface); --md-group-label: var(--md-sys-color-inverse-on-surface); border-color: transparent; } } [data-md-group-segment]:has(:disabled) { background-color: color-mix(in srgb, var(--md-sys-color-on-surface) 10%, transparent); color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent); cursor: not-allowed; } [data-md-group-label] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } [data-md-group-hint], [data-md-group-error] { margin-block-start: var(--md-sys-measurement-space50); font: var(--md-sys-typescale-body-sm); letter-spacing: var(--md-sys-typescale-body-sm-tracking); font-variation-settings: normal; } [data-md-group-hint] { color: var(--md-sys-color-on-surface-variant); } [data-md-group-error] { color: var(--md-sys-color-error); } }