sdxc

Type to search, or start from one of these:

SelectionIndicator

A small marker that stays invisible, while still reserving its own layout slot.

Sort packages by
Nothing shifts sideways as the marker moves.
View code
<div mix={[vstack({ gap: 1, align: "stretch" }), on<HTMLDivElement, "change">("change", readOrder)]}>
	<Header id="preview-sort-heading">Sort packages by</Header>
	<div role="group" aria-labelledby="preview-sort-heading" mix={[vstack({ gap: 0, align: "stretch" })]}>
		{orders.map((order) => (
			<label
				key={order.value}
				mix={[
					hstack({ gap: 2, align: "center" }),
					p(2),
					css({ cursor: "pointer", borderRadius: "var(--ui-radius-md)" }),
				]}
			>
				<SelectionIndicator aria-selected={order.value === selected ? "true" : "false"}>
					<CheckIcon />
				</SelectionIndicator>
				<input
					type="radio"
					name="preview-order"
					value={order.value}
					checked={order.value === selected}
					mix={[ariaChecked(), visuallyHidden()]}
				/>
				{order.label}
			</label>
		))}
	</div>
	<Text>Nothing shifts sideways as the marker moves.</Text>
</div>

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { SelectionIndicator } from "@sdxc/ui";

Renders a marker that stays present in the layout at all times but only becomes visible once its host carries aria-selected="true" or a truthy aria-current, so sibling content never reflows on selection change.

Examples

<Tabs.Trigger id="billing" aria-selected={activeTab === "billing"}>
	{t("settings.tabs.billing")}
	<SelectionIndicator
		aria-selected={activeTab === "billing"}
		mix={css({ position: "absolute", insetBlockEnd: "0", insetInline: "0", blockSize: "2px" })}
	/>
</Tabs.Trigger>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

Also accepts everything in TagProps<"div">.