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.

    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.

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch checked hideLabel>Label</Switch>
    <Switch hideLabel>Label</Switch>
    <Switch checked disabled hideLabel>Label</Switch>
    <Switch disabled hideLabel>Label</Switch>
    </template>
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s0-0"
    /><span class="ui-sr-only"><!--[-->Label<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s0-1"
    /><span class="ui-sr-only"><!--[-->Label<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    disabled
    aria-describedby="s0-2"
    /><span class="ui-sr-only"><!--[-->Label<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    disabled
    aria-describedby="s0-3"
    /><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.

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch>Label</Switch>
    <Switch disabled>Disabled</Switch>
    <Switch>
    Long text bacon ipsum dolor amet prosciutto tenderloin biltong leberkas
    ribeye short ribs shankle tri-tip doner buffalo chislic meatloaf meatball.
    </Switch>
    </template>
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s1-0"
    /><span class="ui-label"><!--[-->Label<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    disabled
    aria-describedby="s1-1"
    /><span class="ui-label"><!--[-->Disabled<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s1-2"
    /><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

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch>Default</Switch>
    <Switch stack>Stack</Switch>
    </template>
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s2-0"
    /><span class="ui-label"><!--[-->Default<!--]--></span><!----></label
    ><label class="ui-switch ui-stack"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s2-1"
    /><span class="ui-label"><!--[-->Stack<!--]--></span><!----></label
    ><!--]-->

    End text

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch>
    Default
    <template #end-text>Supporting text</template>
    </Switch>
    <Switch stack>
    Stack
    <template #end-text>Supporting text</template>
    </Switch>
    </template>
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s3-0"
    /><span class="ui-label"
    ><!--[-->
    Default
    <!--]--></span
    ><span id="s3-0" class="ui-end-text"
    ><!--[-->Supporting text<!--]--></span
    ></label
    ><label class="ui-switch ui-stack"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s3-1"
    /><span class="ui-label"
    ><!--[-->
    Stack
    <!--]--></span
    ><span id="s3-1" class="ui-end-text"
    ><!--[-->Supporting text<!--]--></span
    ></label
    ><!--]-->

    Validation

    • Add the required attribute on the component. It is forwarded to the underlying <input>.
    • Use the error prop to toggle invalid styles. It rendersdata-invalid on the root element. Make use of the end text to give extra feedback on the error.
    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <div class="example-row ui-spacious">
    <Switch required>Default</Switch>
    <Switch required stack>Stack</Switch>
    </div>
    <div class="example-row ui-spacious">
    <Switch error>
    Default
    <template #end-text>Supporting text</template>
    </Switch>
    <Switch error stack>
    Stack
    <template #end-text>Supporting text</template>
    </Switch>
    </div>
    </template>
    <!--[-->
    <div class="example-row ui-spacious">
    <label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s4-0"
    /><span class="ui-label"><!--[-->Default<!--]--></span><!----></label
    ><label class="ui-switch ui-stack"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s4-1"
    /><span class="ui-label"><!--[-->Stack<!--]--></span><!----></label
    >
    </div>
    <div class="example-row ui-spacious">
    <label class="ui-switch" data-invalid="true"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s4-2"
    /><span class="ui-label"
    ><!--[-->
    Default
    <!--]--></span
    ><span id="s4-2" class="ui-end-text"
    ><!--[-->Supporting text<!--]--></span
    ></label
    ><label class="ui-switch ui-stack" data-invalid="true"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s4-3"
    /><span class="ui-label"
    ><!--[-->
    Stack
    <!--]--></span
    ><span id="s4-3" class="ui-end-text"
    ><!--[-->Supporting text<!--]--></span
    ></label
    >
    </div>
    <!--]-->

    Spread

    Use the spread prop to push the label to the left and the switch to the right. This is useful for full-width items like lists and menus.

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch spread>
    Notifications
    <template #end-text>Receive alerts when someone mentions you.</template>
    </Switch>
    <Switch spread required>
    Required
    <template #end-text>You must accept this to proceed.</template>
    </Switch>
    <Switch spread disabled>
    Disabled
    <template #end-text>This switch is disabled.</template>
    </Switch>
    <Switch spread error>
    Invalid Switch
    <template #end-text>There is an error with this switch.</template>
    </Switch>
    </template>
    <!--[--><label class="ui-switch ui-spread"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s5-0"
    /><span class="ui-label"
    ><!--[-->
    Notifications
    <!--]--></span
    ><span id="s5-0" class="ui-end-text"
    ><!--[-->Receive alerts when someone mentions you.<!--]--></span
    ></label
    ><label class="ui-switch ui-spread"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s5-1"
    /><span class="ui-label"
    ><!--[-->
    Required
    <!--]--></span
    ><span id="s5-1" class="ui-end-text"
    ><!--[-->You must accept this to proceed.<!--]--></span
    ></label
    ><label class="ui-switch ui-spread"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    disabled
    aria-describedby="s5-2"
    /><span class="ui-label"
    ><!--[-->
    Disabled
    <!--]--></span
    ><span id="s5-2" class="ui-end-text"
    ><!--[-->This switch is disabled.<!--]--></span
    ></label
    ><label class="ui-switch ui-spread" data-invalid="true"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s5-3"
    /><span class="ui-label"
    ><!--[-->
    Invalid Switch
    <!--]--></span
    ><span id="s5-3" class="ui-end-text"
    ><!--[-->There is an error with this switch.<!--]--></span
    ></label
    ><!--]-->

    Sizes

    Set the small prop for a smaller Switch variant.

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <div class="example-row">
    <Switch small checked hideLabel>Small</Switch>
    <Switch checked hideLabel>Default</Switch>
    </div>
    <div class="example-row">
    <Switch small checked>Small</Switch>
    <Switch checked>Default</Switch>
    </div>
    </template>
    <!--[-->
    <div class="example-row">
    <label class="ui-switch ui-small"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s6-0"
    /><span class="ui-sr-only"><!--[-->Small<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s6-1"
    /><span class="ui-sr-only"><!--[-->Default<!--]--></span><!----></label
    >
    </div>
    <div class="example-row">
    <label class="ui-switch ui-small"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s6-2"
    /><span class="ui-label"><!--[-->Small<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s6-3"
    /><span class="ui-label"><!--[-->Default<!--]--></span><!----></label
    >
    </div>
    <!--]-->

    Icons

    <script setup lang="ts">
    import { Switch } from "opui-css/vue"
    </script>
    <template>
    <Switch small aria-label="Toggle theme">
    <template #icon-unchecked
    ><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
    ></template>
    <template #icon-checked
    ><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
    ></template>
    </Switch>
    <Switch checked aria-label="Toggle theme">
    <template #icon-unchecked
    ><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
    ></template>
    <template #icon-checked
    ><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
    ></template>
    </Switch>
    </template>
    <!--[--><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
    type="checkbox"
    role="switch"
    aria-label="Toggle theme"
    aria-describedby="s7-0"
    /><!----><!----></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
    type="checkbox"
    role="switch"
    aria-label="Toggle theme"
    aria-describedby="s7-1"
    /><!----><!----></label
    ><!--]-->

    Field group

    Use field groups to group related switches.

    The name prop will get passed down to each switch in the group.

    See also: Form documentation.

    Legend
    <script setup lang="ts">
    import { FieldGroup, FieldLegend, FieldSet, Form, Switch } from "opui-css/vue"
    </script>
    <template>
    <Form as="div">
    <FieldSet>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup name="switch-group-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    </FieldSet>
    </Form>
    </template>
    <div class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset">
    <!--[-->
    <legend class=""><!--[-->Legend<!--]--></legend>
    <div class="ui-field-group" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s8-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s8-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s8-2"
    /><span class="ui-label"><!--[-->Switch 3<!--]--></span><!----></label
    ><!--]-->
    </div>
    <!--]-->
    </fieldset>
    <!--]-->
    </div>

    Direction

    Legend
    <script setup lang="ts">
    import { FieldGroup, FieldLegend, FieldSet, Form, Switch } from "opui-css/vue"
    </script>
    <template>
    <Form>
    <FieldSet>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup direction="row" name="switch-group-direction-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    </FieldSet>
    </Form>
    </template>
    <form class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset">
    <!--[-->
    <legend class=""><!--[-->Legend<!--]--></legend>
    <div class="ui-field-group ui-row" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s9-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s9-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s9-2"
    /><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

    <script setup lang="ts">
    import {
    FieldDescription,
    FieldGroup,
    FieldLegend,
    FieldSet,
    Form,
    Switch,
    } from "opui-css/vue"
    </script>
    <template>
    <Form>
    <FieldSet>
    <FieldLegend>Legend</FieldLegend>
    <FieldDescription>Field description above fields</FieldDescription>
    <FieldGroup direction="row" name="switch-group-field-description-1-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    </FieldSet>
    <FieldSet>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup direction="row" name="switch-group-field-description-2-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    <FieldDescription>Field description below fields</FieldDescription>
    </FieldSet>
    </Form>
    </template>
    <form class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset">
    <!--[-->
    <legend class=""><!--[-->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
    type="checkbox"
    role="switch"
    aria-describedby="s10-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s10-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s10-2"
    /><span class="ui-label"><!--[-->Switch 3<!--]--></span><!----></label
    ><!--]-->
    </div>
    <!--]-->
    </fieldset>
    <fieldset class="ui-fieldset">
    <!--[-->
    <legend class=""><!--[-->Legend<!--]--></legend>
    <div class="ui-field-group ui-row" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s10-3"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s10-4"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s10-5"
    /><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
    <script setup lang="ts">
    import { FieldGroup, FieldLegend, FieldSet, Form, Switch } from "opui-css/vue"
    </script>
    <template>
    <Form>
    <FieldSet disabled>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup direction="row" name="switch-group-disabled-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    </FieldSet>
    </Form>
    </template>
    <form class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset" disabled>
    <!--[-->
    <legend class=""><!--[-->Legend<!--]--></legend>
    <div class="ui-field-group ui-row" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s11-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s11-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s11-2"
    /><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!
    <script setup lang="ts">
    import { FieldGroup, FieldLegend, FieldSet, Form, Switch } from "opui-css/vue"
    </script>
    <template>
    <Form>
    <FieldSet>
    <FieldLegend>These are required!</FieldLegend>
    <FieldGroup direction="row" name="switch-group-required-astro">
    <Switch required>Switch 1</Switch>
    <Switch required>Switch 2</Switch>
    <Switch required>Switch 3</Switch>
    </FieldGroup>
    </FieldSet>
    </Form>
    </template>
    <form class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset">
    <!--[-->
    <legend class=""><!--[-->These are required!<!--]--></legend>
    <div class="ui-field-group ui-row" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s12-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s12-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    required
    aria-describedby="s12-2"
    /><span class="ui-label"><!--[-->Switch 3<!--]--></span><!----></label
    ><!--]-->
    </div>
    <!--]-->
    </fieldset>
    <!--]-->
    </form>

    Validation

    Attach the data-invalid attribute to your Fieldset component.

    Legend
    Something went wrong!
    <script setup lang="ts">
    import { FieldGroup, FieldLegend, FieldSet, Form, Switch } from "opui-css/vue"
    </script>
    <template>
    <Form>
    <FieldSet data-invalid>
    <FieldLegend>Legend</FieldLegend>
    <FieldGroup direction="row" name="switch-group-validation-astro">
    <Switch>Switch 1</Switch>
    <Switch>Switch 2</Switch>
    <Switch>Switch 3</Switch>
    </FieldGroup>
    <span class="ui-end-text">Something went wrong!</span>
    </FieldSet>
    </Form>
    </template>
    <form class="ui-form">
    <!--[-->
    <fieldset class="ui-fieldset" data-invalid>
    <!--[-->
    <legend class=""><!--[-->Legend<!--]--></legend>
    <div class="ui-field-group ui-row" role="group">
    <!--[--><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s13-0"
    /><span class="ui-label"><!--[-->Switch 1<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s13-1"
    /><span class="ui-label"><!--[-->Switch 2<!--]--></span><!----></label
    ><label class="ui-switch"
    ><!----><!----><input
    type="checkbox"
    role="switch"
    aria-describedby="s13-2"
    /><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.

    Anatomy

    1. Container: label element
    2. Switch: & input type="checkbox" role="switch"
    3. Label (optional): & .ui-label
    4. End text (optional): .ui-end-text

    API

    Switch API

    Field group API

    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);
    }
    }
    }
    }