ControlTile is the quick-settings tile: one icon, one short title, and an optional state — a toggle, a ring of progress around the icon, or a horizontal bar that replaces the title. It is a single focus target that owns its own material, focus and press treatment, corner shape, and accessible role.ControlTile builds on Container and accepts its props, so material, shape, tooltips, and interaction hooks all work here.import { useState } from 'react';
import { ControlTile } from '@wearables-ui-toolkit/mrbd';
import { brightnessIcon, wifiOffIcon, wifiOnIcon } from './icons';
export function QuickSettings() {
const [wifiEnabled, setWifiEnabled] = useState(true);
const [brightness, setBrightness] = useState(0.5);
const clamp = (next: number) => Math.min(1, Math.max(0, next));
return (
<div className="tile-row">
<ControlTile
title="Wi-Fi"
icon={wifiEnabled ? wifiOnIcon : wifiOffIcon}
iconAnimationKey={wifiEnabled ? 'on' : 'off'}
animateIconChanges
checked={wifiEnabled}
onClick={() => setWifiEnabled(value => !value)}
/>
<ControlTile
title="Brightness"
icon={brightnessIcon}
progress={brightness}
showHorizontalProgressBar
lockFocus
onIncrement={current => setBrightness(clamp(current + 0.1))}
onDecrement={current => setBrightness(clamp(current - 0.1))}
/>
</div>
);
}
import { ControlTile, IconTintColor } from '@wearables-ui-toolkit/mrbd';
import { downloadIcon } from './icons';
<ControlTile
title="Downloading"
icon={downloadIcon}
iconTintColor={IconTintColor.ACTION}
progress={completedFraction}
showCircularProgressBar
animateProgressChanges
/>;
.tile-row {
display: flex;
align-items: stretch;
gap: var(--uit-spacing-small);
}
.tile-row > * {
flex: 1 1 0;
min-width: 0;
}
ControlTileProps extends ContainerProps without children. Read that page for the full inherited surface.| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Short label rendered beside the icon. Also becomes the accessible name. |
titleMaxLines | number | 2 | Maximum number of title lines before the text is clamped. |
checked | boolean \| null | null | Toggle state. null or omitted means the tile is not a toggle. Controlled: the tile never flips it. |
icon | IconSource | — | Icon shown in the circular icon slot. Accepts inline vector data, an object with a uri, or a URL string. |
iconAnimationKey | string \| number | — | Stable identity for the current icon. A change to this value is what triggers the swap animation. |
animateIconChanges | boolean | false | Cross-fade and scale between icons when iconAnimationKey changes. |
animateProgressChanges | boolean | false | Animate showing and hiding a progress bar, and the icon scale that goes with it. |
iconTintColor | IconTintColor | — | Semantic tint for the glyph. Falls back to PRIMARY when unset. |
checkedIconTintColor | IconTintColor | — | Tint used while checked is true. Falls back to iconTintColor. |
iconContainerMaterial | ContainerMaterial \| null | — | Material for the circular icon backdrop. When omitted the tile uses its built-in overlay backdrop. |
checkedIconContainerMaterial | ContainerMaterial \| null | the library’s checked-icon material | Material for the icon backdrop while checked is true. Pass null to keep the ordinary backdrop while checked. |
showCircularProgressBar | boolean | false | Draw a progress ring around the icon. Requires progress. |
showHorizontalProgressBar | boolean | false | Replace the title with a horizontal bar. Requires progress, and wins when both progress props are true. |
progress | number | — | Progress value from 0 to 1, used by whichever progress bar is shown. |
lockFocus | boolean | false | Keep directional focus on this tile and turn off partial-focus rubberband feedback. |
onLockedFocusLost | () => void | — | Called when a locked tile leaves the focused or pressed state. |
onIncrement | (currentValue: number) => void | — | Called with the current progress when ArrowRight is released while the horizontal bar is shown. |
onDecrement | (currentValue: number) => void | — | Called with the current progress when ArrowLeft is released while the horizontal bar is shown. |
onClick | () => void | — | Activation handler for pointer clicks and Enter or Space. |
| Prop | Type | Default | Description |
|---|---|---|---|
material | ContainerMaterial | the tile’s own default material | Material for the tile surface. Memoize the instance. |
shapeProvider | ShapeProvider | a rounded rectangle at the medium corner radius | Geometry for clipping and material paths. |
disabled | boolean | false | Disables activation and arrow-key adjustment. |
ariaLabel | string | derived from title | Overrides the accessible name; required for icon-only tiles. |
onStateChange | (prev, next) => void | — | Observe default, focused, and pressed transitions. Still called when lockFocus is set. |
visualStateOverride | VisualState | — | Force a visual state instead of deriving it from interaction. Also drives the icon backdrop and bar appearance. |
partialFocusSupportedAxis | PartialFocusSupportedAxis | PartialFocusSupportedAxis.XY | Axes that get handoff and rubber-band feedback. Forced to None while lockFocus is set. |
tooltipText | string | — | Tooltip shown for the tile, useful when a clamped title hides the full label. |
role, ariaLabel, and aria-* value attributes before spreading the remaining inherited props, so anything you pass for those wins. That is how ariaLabel replaces the name derived from title. The same applies to role and the value attributes — override those only when you really do mean different semantics than the tile inferred.IconTintColor — semantic tints accepted by iconTintColor and checkedIconTintColor. Raw colour strings are deliberately not accepted.PRIMARY, SECONDARY, ACCENTON_CHECKEDACTION, POSITIVE, WARNING, NEGATIVE, INFOchecked is controlled. Flip it in onClick; the tile only renders what you pass. null or omitted means the tile is a plain button.progress is set. showCircularProgressBar or showHorizontalProgressBar on their own change nothing visible.true, the horizontal bar wins. The horizontal bar fades the title out and takes its place and the icon backdrop is suppressed; the circular ring is drawn inside the icon slot, keeps the backdrop, and leaves the title visible.progress gives it something to draw — and at the large size otherwise. It also scales up slightly in the horizontal-bar layout.checked is true, or when an icon container material is supplied — and only when there is an icon and showHorizontalProgressBar is not set. A supplied iconContainerMaterial replaces the built-in overlay and is drawn pinned to its focused appearance so the backdrop reads consistently; while checked is true the checked material cross-fades over it.checked is not true the tint is iconTintColor. While checked it is checkedIconTintColor, then iconTintColor, and finally ON_CHECKED when the default checked backdrop material is in use. With nothing set at all, the glyph falls back to PRIMARY.animateIconChanges cross-fades and scales the old glyph out and the new one in. It only runs when iconAnimationKey actually changes, so give each icon a stable key; without a key the icon swaps instantly.onClick.showHorizontalProgressBar is set and the tile is not disabled, ArrowLeft and ArrowRight are consumed so they do not move focus. On key release the tile calls onDecrement or onIncrement with the current progress, and only when progress is set. Your own onKeyDown and onKeyUp handlers are not called for those keys. The tile does not change progress itself — you compute and apply the next value.lockFocus consumes all four arrow keys so directional input stays on the tile, and it disables partial-focus rubberband feedback. Use it for continuous adjustment, give the user an obvious way out, and use onLockedFocusLost to notice when the tile stops being focused anyway.progress is set the tile reports role="slider" with aria-valuenow equal to progress and a range of 0 to 1. This applies whenever progress is non-null, even before a progress bar is shown, and the value is reported verbatim — keep it inside the range.role="switch" with aria-checked when checked is a boolean, and role="button" when checked is null or omitted.title. An icon-only tile has no name, so pass ariaLabel explicitly.title, falling back to Progress when there is no title. Give an adjustable tile a title so both readings agree.tooltipText, which is a better answer than raising titleMaxLines until the grid breaks.material instance so it is not recreated every render.iconAnimationKey whenever you animate an icon swap.progress whenever either progress prop is set so the tile reports the correct state.onIncrement and onDecrement. These callbacks report intent and do not move the bar themselves.ControlTile represents a setting.