Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

    Components

    Switch

    See also: Switch field group.

    Full supportSupported since v123.Full supportSupported since v121.Full supportSupported since v17.5.

    Anatomy

    • label.ui-switch

      Container element.

    • <input>

      The switch input.

    • .ui-icon-unchecked

      An optional icon in the thumb when unchecked.

    • .ui-icon-checked

      An optional icon in the thumb when checked.

    • .ui-label

      The label.

    • .ui-end-text

      Supporting text displayed below the label.

    All switches should have an accessible name. Either provide a visible or visually-hidden label inside the component, or set aria-label on the input. Both approaches are fine.

    <!-- Checked -->
    <label class="ui-switch">
    <input name="switch-variants" type="checkbox" role="switch" checked />
    <span class="ui-sr-only">Label</span>
    </label>
    <!-- Unchecked -->
    <label class="ui-switch">
    <input name="switch-variants" type="checkbox" role="switch" />
    <span class="ui-sr-only">Label</span>
    </label>
    <!-- Checked & disabled -->
    <label class="ui-switch">
    <input
    name="switch-variants"
    type="checkbox"
    role="switch"
    checked
    disabled
    />
    <span class="ui-sr-only">Label</span>
    </label>
    <!-- Unchecked & disabled -->
    <label class="ui-switch">
    <input name="switch-variants" type="checkbox" role="switch" disabled />
    <span class="ui-sr-only">Label</span>
    </label>

    Visible label

    Render the label text inside an element with a .ui-label class. Also, don't miss the info on label accessibility.

    <label class="ui-switch">
    <input name="switch-visible-label" type="checkbox" role="switch" />
    <span class="ui-label">Label</span>
    </label>
    <label class="ui-switch">
    <input name="switch-visible-label" type="checkbox" role="switch" disabled />
    <span class="ui-label">Disabled</span>
    </label>
    <label class="ui-switch">
    <input name="switch-visible-label" type="checkbox" role="switch" />
    <span class="ui-label"
    >Long text bacon ipsum dolor amet prosciutto tenderloin biltong leberkas
    ribeye short ribs shankle tri-tip doner buffalo chislic meatloaf
    meatball.</span
    >
    </label>

    Label position

    <label class="ui-switch">
    <input name="switch-label-position" type="checkbox" role="switch" />
    <span class="ui-label">Default</span>
    </label>
    <label class="ui-switch ui-stack">
    <input name="switch-label-position" type="checkbox" role="switch" />
    <span class="ui-label">Stack</span>
    </label>

    End text

    <label class="ui-switch">
    <input
    name="switch-supporting-text"
    type="checkbox"
    role="switch"
    aria-describedby="switch-supporting-text-end-text-1"
    />
    <span class="ui-label">Default</span>
    <span class="ui-end-text" id="switch-supporting-text-end-text-1"
    >Supporting text</span
    >
    </label>
    <label class="ui-switch ui-stack">
    <input
    name="switch-supporting-text"
    type="checkbox"
    role="switch"
    aria-describedby="switch-supporting-text-end-text-2"
    />
    <span class="ui-label">Stack</span>
    <span class="ui-end-text" id="switch-supporting-text-end-text-2"
    >Supporting text</span
    >
    </label>

    Validation

    • Add [required] to the <input> element to toggle required styles.
    • Add data-invalid on the root element to toggle invalid styles. Make use of the end text to give extra feedback on the error.
    <div class="example-row ui-spacious">
    <label class="ui-switch">
    <input name="switch-validation" type="checkbox" role="switch" required />
    <span class="ui-label">Default</span>
    </label>
    <label class="ui-switch ui-stack">
    <input name="switch-validation" type="checkbox" role="switch" required />
    <span class="ui-label">Stack</span>
    </label>
    </div>
    <div class="example-row ui-spacious">
    <label class="ui-switch" data-invalid>
    <input
    name="switch-validation"
    type="checkbox"
    role="switch"
    aria-describedby="switch-validation-end-text-1"
    />
    <span class="ui-label">Default</span>
    <span class="ui-end-text" id="switch-validation-end-text-1"
    >Supporting text</span
    >
    </label>
    <label class="ui-switch ui-stack" data-invalid>
    <input
    name="switch-validation"
    type="checkbox"
    role="switch"
    aria-describedby="switch-validation-end-text-2"
    />
    <span class="ui-label">Stack</span>
    <span class="ui-end-text" id="switch-validation-end-text-2"
    >Supporting text</span
    >
    </label>
    </div>

    Spread

    Add the .ui-spread class to the <label class="ui-switch">to push the label to the left and the switch to the right. This is useful for full-width items like lists and menus.

    <label class="ui-switch ui-spread">
    <input
    name="switch-spread"
    type="checkbox"
    role="switch"
    aria-describedby="switch-spread-end-text-1"
    />
    <span class="ui-label">Notifications</span>
    <span class="ui-end-text" id="switch-spread-end-text-1"
    >Receive alerts when someone mentions you.</span
    >
    </label>
    <label class="ui-switch ui-spread">
    <input
    name="switch-spread"
    type="checkbox"
    role="switch"
    required
    aria-describedby="switch-spread-end-text-2"
    />
    <span class="ui-label">Required</span>
    <span class="ui-end-text" id="switch-spread-end-text-2"
    >You must accept this to proceed.</span
    >
    </label>
    <label class="ui-switch ui-spread">
    <input
    name="switch-spread"
    type="checkbox"
    role="switch"
    disabled
    aria-describedby="switch-spread-end-text-3"
    />
    <span class="ui-label">Disabled</span>
    <span class="ui-end-text" id="switch-spread-end-text-3"
    >This switch is disabled.</span
    >
    </label>
    <label class="ui-switch ui-spread" data-invalid>
    <input
    name="switch-spread"
    type="checkbox"
    role="switch"
    aria-describedby="switch-spread-end-text-4"
    />
    <span class="ui-label">Invalid Switch</span>
    <span class="ui-end-text" id="switch-spread-end-text-4"
    >There is an error with this switch.</span
    >
    </label>

    Sizes

    Add the .ui-small class on the <label class="ui-switch">for a smaller Switch variant.

    <div class="example-row">
    <label class="ui-switch ui-small">
    <input name="switch-sizes" type="checkbox" role="switch" checked />
    <span class="ui-sr-only">Small</span>
    </label>
    <label class="ui-switch">
    <input name="switch-sizes" type="checkbox" role="switch" checked />
    <span class="ui-sr-only">Default</span>
    </label>
    </div>
    <div class="example-row">
    <label class="ui-switch ui-small">
    <input name="switch-sizes" type="checkbox" role="switch" checked />
    <span class="ui-label">Small</span>
    </label>
    <label class="ui-switch">
    <input name="switch-sizes" type="checkbox" role="switch" checked />
    <span class="ui-label">Default</span>
    </label>
    </div>

    Icons

    <label class="ui-switch ui-small">
    <span class="ui-icon-unchecked" aria-hidden="true">
    <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    >
    <path
    fill="currentColor"
    d="M20.026 17.001c-2.762 4.784-8.879 6.423-13.663 3.661A10 10 0 0 1 3.13 17.68a.75.75 0 0 1 .365-1.132c3.767-1.348 5.785-2.91 6.956-5.146c1.233-2.353 1.551-4.93.689-8.463a.75.75 0 0 1 .769-.927a9.96 9.96 0 0 1 4.457 1.327c4.784 2.762 6.423 8.879 3.66 13.662"
    ></path>
    </svg>
    </span>
    <span class="ui-icon-checked" aria-hidden="true">
    <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    >
    <path
    fill="currentColor"
    d="M12 2a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 12 2m5 10a5 5 0 1 1-10 0a5 5 0 0 1 10 0m4.25.75a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5zM12 19a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 12 19m-7.75-6.25a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5zm-.03-8.53a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 0 1-1.06 1.06l-1.5-1.5a.75.75 0 0 1 0-1.06m1.06 15.56a.75.75 0 1 1-1.06-1.06l1.5-1.5a.75.75 0 1 1 1.06 1.06zm14.5-15.56a.75.75 0 0 0-1.06 0l-1.5 1.5a.75.75 0 0 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06m-1.06 15.56a.75.75 0 1 0 1.06-1.06l-1.5-1.5a.75.75 0 1 0-1.06 1.06z"
    ></path>
    </svg>
    </span>
    <input
    name="switch-icons"
    type="checkbox"
    role="switch"
    aria-label="Toggle theme"
    />
    </label>
    <label class="ui-switch">
    <span class="ui-icon-unchecked" aria-hidden="true">
    <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    >
    <path
    fill="currentColor"
    d="M20.026 17.001c-2.762 4.784-8.879 6.423-13.663 3.661A10 10 0 0 1 3.13 17.68a.75.75 0 0 1 .365-1.132c3.767-1.348 5.785-2.91 6.956-5.146c1.233-2.353 1.551-4.93.689-8.463a.75.75 0 0 1 .769-.927a9.96 9.96 0 0 1 4.457 1.327c4.784 2.762 6.423 8.879 3.66 13.662"
    ></path>
    </svg>
    </span>
    <span class="ui-icon-checked" aria-hidden="true">
    <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    >
    <path
    fill="currentColor"
    d="M12 2a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 12 2m5 10a5 5 0 1 1-10 0a5 5 0 0 1 10 0m4.25.75a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5zM12 19a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 12 19m-7.75-6.25a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5zm-.03-8.53a.75.75 0 0 1 1.06 0l1.5 1.5a.75.75 0 0 1-1.06 1.06l-1.5-1.5a.75.75 0 0 1 0-1.06m1.06 15.56a.75.75 0 1 1-1.06-1.06l1.5-1.5a.75.75 0 1 1 1.06 1.06zm14.5-15.56a.75.75 0 0 0-1.06 0l-1.5 1.5a.75.75 0 0 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06m-1.06 15.56a.75.75 0 1 0 1.06-1.06l-1.5-1.5a.75.75 0 1 0-1.06 1.06z"
    ></path>
    </svg>
    </span>
    <input
    name="switch-icons"
    type="checkbox"
    role="switch"
    checked
    aria-label="Toggle theme"
    />
    </label>

    Field group

    Use field groups to group related switches.

    Give every <input> in the group the same nameattribute so they're submitted together.

    See also: Form documentation.

    Legend
    <div class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group" role="group">
    <label class="ui-switch">
    <input name="switch-group" type="checkbox" role="switch" />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group" type="checkbox" role="switch" />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group" type="checkbox" role="switch" />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    </fieldset>
    </div>

    Direction

    Legend
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input name="switch-group-direction" type="checkbox" role="switch" />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group-direction" type="checkbox" role="switch" />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group-direction" type="checkbox" role="switch" />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    </fieldset>
    </form>

    Field description

    Can be placed above and below the fields.

    Legend

    Field description above fields

    Legend

    Field description below fields

    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <p class="ui-field-description">Field description above fields</p>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input
    name="switch-group-field-description-1"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-field-description-1"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-field-description-1"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    </fieldset>
    <fieldset class="ui-fieldset">
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input
    name="switch-group-field-description-2"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-field-description-2"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-field-description-2"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    <p class="ui-field-description">Field description below fields</p>
    </fieldset>
    </form>

    Disabled

    Attach the disabled attribute to the <fieldset> element.

    Legend
    <form class="ui-form">
    <fieldset class="ui-fieldset" disabled>
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input name="switch-group-disabled" type="checkbox" role="switch" />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group-disabled" type="checkbox" role="switch" />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input name="switch-group-disabled" type="checkbox" role="switch" />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    </fieldset>
    </form>

    Required

    Attach the required attribute to at least one of your <input> elements.

    These are required!
    <form class="ui-form">
    <fieldset class="ui-fieldset">
    <legend>These are required!</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input
    name="switch-group-required"
    type="checkbox"
    role="switch"
    required
    />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-required"
    type="checkbox"
    role="switch"
    required
    />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-group-required"
    type="checkbox"
    role="switch"
    required
    />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    </fieldset>
    </form>

    Validation

    Attach the data-invalid attribute to your <fieldset class="ui-fieldset"> element.

    Legend
    Something went wrong!
    <form class="ui-form">
    <fieldset class="ui-fieldset" data-invalid>
    <legend>Legend</legend>
    <div class="ui-field-group ui-row" role="group">
    <label class="ui-switch">
    <input
    name="switch-field-group-validation"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 1</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-field-group-validation"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 2</span>
    </label>
    <label class="ui-switch">
    <input
    name="switch-field-group-validation"
    type="checkbox"
    role="switch"
    />
    <span class="ui-label">Switch 3</span>
    </label>
    </div>
    <span class="ui-end-text">Something went wrong!</span>
    </fieldset>
    </form>

    Accessibility

    Role & attributes

    Role/attributeUsage
    role="switch"Required on the input element. Identifies the element that serves as a switch.

    Labels

    Accessible switches should have a label. The first two approaches are equally ok:

    ApproachUsage in Switch component
    Provide a label inside the elementUse a .ui-label child for a visible label, or a .ui-sr-only child to hide it visually while keeping it accessible. In Astro, set the hideLabel prop to render the slot content as .ui-sr-only.
    Add an aria-label on the inputUsed when there's no visible label inside the component (e.g. icon-only switches). In Astro, pass aria-label as a prop on the component and it will land on the input.
    Have a visible label that you reference with aria-labelledbyNot used.

    Keyboard support

    KeyFunction
    SpaceWhen Switch is focused it changes its state.
    Enter(Optional) When Switch is focused it changes its state.

    API

    Switch API

    TypeModifiersDefaultDescription
    Layout.ui-spread-Pushes the label and the switch to opposite ends.
    Layout.ui-stack-Stacks the label under the switch.
    Sizes.ui-small-The size of the element.
    Validation[data-invalid]-Shows error styles.

    Parts

    PartDescription
    label.ui-switchContainer element.
    <input>The switch input.
    .ui-icon-uncheckedAn optional icon in the thumb when unchecked.
    .ui-icon-checkedAn optional icon in the thumb when checked.
    .ui-labelThe label.
    .ui-end-textSupporting text displayed below the label.

    The input needs type="checkbox" and role="switch". Use .ui-sr-only instead of .ui-label to hide the label visually.

    Field group API

    TypeModifiersDefaultDescription
    Orientationdefault, .ui-row-The orientation of the element.

    Parts

    PartDescription
    .ui-field-groupContainer element.

    The root needs role="group". Wrap it in a .ui-fieldset with a <legend> to label it.

    Browser support

    Full supportSupported since v123.Full supportSupported since v121.Full supportSupported since v17.5.

    See also the full browser support guide.

    Installation

    See also

    @layer components.root {
    :where(.ui-switch) {
    --_motion: var(--motion, 1);
    --_accent-color: var(--primary);
    --_accent-contrast: var(--primary-contrast);
    --_dot-bg-color: light-dark(var(--gray-11), var(--gray-14));
    --_dot-inset: var(--size-1) auto auto var(--size-1);
    --_dot-outline-size: 0;
    --_dot-size: var(--size-3);
    --_track-bg-color: light-dark(var(--gray-3), var(--gray-8));
    --_track-height: var(--size-5);
    --_track-width: var(--size-8);
    --_transition-tf: var(--ease-4);
    --_transition-time: calc(0.2s * var(--_motion));
    align-items: center;
    color: var(--text-primary);
    display: inline-grid;
    gap: 0 var(--size-2);
    grid-auto-columns: auto;
    grid-auto-flow: column;
    inline-size: fit-content;
    :where(input[type="checkbox"][role="switch"]) {
    appearance: none;
    block-size: var(--_track-height);
    cursor: pointer;
    inline-size: var(--_track-width);
    margin: 0;
    position: relative;
    /* Track */
    &::before {
    background-color: var(--_track-bg-color);
    block-size: var(--_track-height);
    border: 1px solid var(--_dot-bg-color);
    border-radius: var(--radius-round);
    content: "";
    inline-size: var(--_track-width);
    inset: 0;
    position: absolute;
    }
    &:focus-visible {
    --focus-ring-color: currentColor;
    outline: var(--focus-ring-width) var(--focus-ring-style)
    var(--focus-ring-color);
    outline-offset: var(--focus-ring-offset);
    }
    /* Dot */
    &::after {
    background-color: var(--_dot-bg-color);
    block-size: var(--_dot-size);
    border-radius: var(--radius-round);
    content: "";
    inline-size: var(--_dot-size);
    inset: var(--_dot-inset);
    outline: var(--_dot-outline-size) solid var(--_dot-bg-color);
    outline-offset: -1px;
    position: absolute;
    }
    /* Checked */
    &:checked {
    &::before {
    background-color: var(--_accent-color);
    border-color: var(--_accent-color);
    transition:
    background-color var(--_transition-time) var(--_transition-tf),
    border-color var(--_transition-time) var(--_transition-tf);
    }
    /* Dot */
    &::after {
    --_dot-bg-color: var(--_accent-contrast);
    --_dot-outline-size: calc(var(--size-1) - 1px);
    inset-inline-start: calc(
    var(--_track-width) - var(--_dot-size) - var(--size-1)
    );
    }
    }
    /* Animation */
    /* Track */
    &::before {
    transition:
    background-color var(--_transition-time) var(--_transition-tf),
    border-color var(--_transition-time) var(--_transition-tf);
    }
    /* Dot */
    &::after {
    transition: all var(--_transition-time) var(--_transition-tf);
    }
    &:active:after {
    --_dot-outline-size: calc(var(--size-1) + 1px);
    }
    &:checked {
    &:active:after {
    --_dot-outline-size: calc(var(--size-1) + 1px);
    }
    }
    }
    /* Icons */
    &:has(.ui-icon-checked, .ui-icon-unchecked) {
    [role="switch"] {
    grid-column: 1 / -1;
    grid-row: 1;
    }
    &:has([role="switch"]:checked) {
    .ui-icon-unchecked {
    display: none;
    }
    .ui-icon-checked {
    display: block;
    }
    }
    .ui-icon-checked {
    display: none;
    }
    .ui-icon-unchecked,
    .ui-icon-checked {
    grid-column: 1;
    grid-row: 1;
    pointer-events: none;
    z-index: 1;
    svg {
    display: block;
    inline-size: var(--_dot-size);
    }
    }
    .ui-icon-unchecked {
    margin-inline-start: calc(
    var(--_track-width) - var(--_dot-size) - var(--size-1)
    );
    }
    .ui-icon-checked {
    margin-inline-start: var(--size-1);
    }
    }
    /* Required dot */
    &:has(:invalid) {
    .ui-label:after {
    color: var(--red);
    content: "*";
    inset: 0 -0.25ex auto auto;
    position: absolute;
    }
    }
    /* Disabled */
    &:has([disabled]) {
    cursor: not-allowed;
    opacity: 0.64;
    user-select: none;
    input {
    cursor: not-allowed;
    }
    }
    /* Label */
    .ui-label {
    color: var(--text-primary);
    font-size: var(--font-size-05);
    grid-column: 2;
    grid-row: 1;
    min-width: 0;
    padding-inline: 0 1ex;
    position: relative;
    user-select: none;
    }
    /* End text */
    :where(.ui-end-text) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    grid-column: 2;
    grid-row: 2;
    line-height: 1.5;
    z-index: 1;
    }
    /* Size */
    &.ui-small {
    --_dot-size: 0.75rem;
    --_track-height: var(--size-4);
    --_track-width: 2.5rem;
    }
    /* Spread layout */
    &.ui-spread {
    align-items: center;
    column-gap: var(--size-4);
    grid-auto-flow: unset;
    grid-template-columns: 1fr auto;
    inline-size: 100%;
    .ui-label {
    font-weight: 600;
    grid-column: 1;
    grid-row: 1;
    inline-size: fit-content;
    }
    input[role="switch"] {
    grid-column: 2;
    grid-row: 1;
    }
    &:has(.ui-icon-checked, .ui-icon-unchecked) {
    input[role="switch"],
    .ui-icon-checked,
    .ui-icon-unchecked {
    grid-column: 2;
    }
    }
    :where(.ui-end-text) {
    grid-column: 1;
    grid-row: 2;
    }
    }
    /* Stacked layout */
    &.ui-stack {
    grid-auto-columns: unset;
    justify-items: center;
    .ui-label {
    grid-column: 1/-1;
    grid-row: 2;
    margin-block-start: var(--size-1);
    padding-inline: 1ex;
    }
    .ui-end-text {
    grid-column: 1/-1;
    grid-row: 3;
    }
    }
    /* Validation */
    &[data-invalid] {
    input {
    border-radius: var(--radius-round);
    outline: 2px solid var(--color-9);
    }
    .ui-end-text {
    color: var(--color-9);
    }
    }
    }
    }
    @layer components.extended {
    /* Form */
    :where(.ui-form) {
    display: grid;
    gap: var(--size-8);
    hr {
    margin-block: 0;
    }
    }
    /* Fieldset */
    :where(.ui-fieldset) {
    all: unset;
    border: 0;
    border-radius: 0;
    display: grid;
    gap: var(--size-1);
    margin: 0;
    min-inline-size: 0;
    padding: 0;
    /* Legend */
    :where(legend, .ui-legend) {
    all: unset;
    color: var(--text-primary);
    font-weight: 600;
    margin-block-end: var(--size-3);
    padding: 0;
    &:has(+ :where(.ui-field-description)) {
    margin-block-end: 0;
    }
    }
    /* Field description / supporting text */
    :where(.ui-field-description) {
    color: var(--text-muted);
    font-size: var(--font-size-05);
    line-height: var(--font-lineheight-3);
    &:has(+ *) {
    margin-block-end: var(--size-3);
    }
    }
    /* End text */
    :where(.ui-end-text) {
    color: var(--text-muted);
    font-size: var(--font-size-0);
    line-height: var(--font-lineheight-3);
    }
    &:has(.ui-text-field.ui-row) {
    row-gap: var(--size-7);
    }
    /* Disabled */
    &[disabled] {
    opacity: 0.64;
    user-select: none;
    input,
    label,
    .ui-label {
    cursor: not-allowed;
    }
    }
    /* Error / validation */
    &[data-invalid] {
    :where(.ui-end-text) {
    color: var(--critical);
    }
    :where(.ui-checkbox, .ui-radio, .ui-switch) {
    --primary: var(--critical);
    :where(.ui-end-text) {
    color: var(--critical);
    }
    }
    :where(.ui-switch) {
    input {
    border-radius: var(--radius-round);
    outline: 2px solid var(--critical);
    }
    }
    }
    /* Required */
    &:has(:invalid) {
    :where(legend, .ui-legend) {
    padding-inline-end: 1ex;
    position: relative;
    &::after {
    color: var(--red);
    content: "*";
    inset: 0 -0.25ex auto auto;
    position: absolute;
    }
    }
    }
    }
    /* Field group */
    :where(.ui-field-group) {
    display: flex;
    flex-direction: column;
    gap: var(--size-4);
    /* If it only has checkboxes, radios, or switches */
    &:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)):not(
    :has(> :not(.ui-checkbox, .ui-radio, .ui-switch))
    ) {
    gap: var(--size-2);
    }
    /* If it only has buttons */
    &:has(.ui-button):not(:has(*:not(.ui-button))) {
    align-items: center;
    flex-direction: row;
    gap: var(--size-2);
    /* If it's not preceded by an hr */
    &:not(hr + &) {
    margin-block-start: var(--size-4);
    }
    }
    & + & {
    margin-block-start: var(--size-5);
    }
    /* Directions */
    &.ui-row {
    flex-direction: row;
    flex-wrap: wrap;
    &:has(> :where(.ui-checkbox, .ui-radio, .ui-switch)) {
    gap: var(--size-4);
    }
    }
    }
    }