[ @sdxc/u/state ]
precededBy
Sugar over when(":is({selector}) ~ &", input), the backward-looking twin of hasSibling().
import { precededBy } from "@sdxc/u/state";Quick reference
Every documented call, beside the CSS it emits.
| Call | CSS |
|---|---|
u.precededBy("input:checked", u.border("brand.solid")) | css({ ":is(input:checked) ~ &": { borderColor: "var(--ui-brand-bg-solid)" } }) |
u.precededBy("input:focus-visible", u.outline({ color: "brand", offset: 2 })) | css({ ":is(input:focus-visible) ~ &": { outlineColor: "...", outlineOffset: "2px" } }) |
Examples
Basic example
Pass a selector to precededBy() to style an element by a sibling that comes before it. Tick the tasks:
<ul mix={[vstack({ gap: 2 }), text("sm")]}>
<li>
<label mix={[hstack({ gap: 2, align: "center" })]}>
<input type="checkbox" checked mix={[accent()]} />
<span
mix={[
fg(),
precededBy("input:checked", [textDecoration("line-through"), fg("neutral.muted")]),
]}
>
Write the tests
</span>
</label>
</li>
<li>
<label mix={[hstack({ gap: 2, align: "center" })]}>
<input type="checkbox" mix={[accent()]} />
<span
mix={[
fg(),
precededBy("input:checked", [textDecoration("line-through"), fg("neutral.muted")]),
]}
>
Ship the release
</span>
</label>
</li>
</ul>Custom radio buttons
Hide each radio and paint the text after it with precededBy(), one pair per label. Click a segment, or tab in and use the arrow keys:
<fieldset
mix={[hstack({ gap: 1 }), p(1), rounded("lg"), border({ color: "neutral.border", width: 1 })]}
>
<label>
<input type="radio" name="view" checked mix={[visuallyHidden()]} />
<span
mix={[
fg("neutral.muted"),
cursor("pointer"),
precededBy("input:checked", [bg("brand.solid"), fg("brand.onSolid")]),
precededBy("input:focus-visible", outline({ color: "brand", offset: 2 })),
...,
]}
>
List
</span>
</label>
<label>
<input type="radio" name="view" mix={[visuallyHidden()]} />
<span
mix={[
fg("neutral.muted"),
cursor("pointer"),
precededBy("input:checked", [bg("brand.solid"), fg("brand.onSolid")]),
precededBy("input:focus-visible", outline({ color: "brand", offset: 2 })),
...,
]}
>
Grid
</span>
</label>
</fieldset>Responsive design
Wrap the call in a container query to scope it to one width.
u.at("md", u.precededBy("input:checked", u.border("brand.solid")))Customizing your theme
Customizing colors
Define a palette as --ui-color-{name}-{50..950} and map a tone onto it with the --ui-{tone}-* variables, the way theme.css maps the built-in ones:
:root {
--ui-color-info-50: oklch(0.97 0.02 230);
--ui-color-info-200: oklch(0.88 0.06 230);
--ui-color-info-500: oklch(0.62 0.16 230);
--ui-color-info-600: oklch(0.52 0.18 230);
--ui-color-info-900: oklch(0.3 0.1 230);
--ui-info-bg-tint: var(--ui-color-info-50);
--ui-info-bg-solid: var(--ui-color-info-600);
--ui-info-fg: var(--ui-color-info-600);
--ui-info-fg-muted: var(--ui-color-info-500);
--ui-info-fg-emphasis: var(--ui-color-info-900);
--ui-info-fg-on-solid: white;
--ui-info-border: var(--ui-color-info-200);
--ui-info-border-strong: var(--ui-color-info-600);
--ui-info-ring: var(--ui-color-info-500);
}declare module "@sdxc/u" {
interface ColorPalettes {
info: true;
}
interface SemanticTones {
info: true;
}
}The new name is then accepted wherever the scale is:
bg("info")Signature
u.precededBy(selector: string, input: UtilityInput<Node>)