<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox } from '@hina-ui/vue'
const notify = ref(true)
</script>
<template>
<Checkbox v-model="notify">Notify me about new chapters</Checkbox>
</template>
Usage
import { Checkbox } from '@hina-ui/vue'
The checkbox joins the box and its label into one clickable unit. v-model binds a boolean, and the value 'indeterminate' shows the mixed state. The default slot is the label; clicking either the label or the box toggles. Attributes it does not declare land on the inner box element.
Value: false
<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox, Stack, Text } from '@hina-ui/vue'
const remember = ref(false)
</script>
<template>
<Stack gap="sm">
<Checkbox v-model="remember">Keep me signed in</Checkbox>
<Text size="sm" tone="muted">Value: {{ remember }}</Text>
</Stack>
</template>
Examples
Description
description adds a line of supporting text under the label, one size smaller.
<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox } from '@hina-ui/vue'
const weekly = ref(true)
</script>
<template>
<Checkbox
v-model="weekly"
description="A digest of the week's updates every Monday. Unsubscribe any time."
>
Weekly newsletter
</Checkbox>
</template>
Settings rows
Use control-placement="end" to place the control after the text and block to fill the available width. The description stays below the label. start and end follow the text direction.
<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox } from '@hina-ui/vue'
const weekly = ref(true)
</script>
<template>
<Checkbox
v-model="weekly"
control-placement="end"
block
description="A digest of the week's updates every Monday. Unsubscribe any time."
>
Weekly newsletter
</Checkbox>
</template>
Indeterminate
The value 'indeterminate' shows a dash. The usual case is “select all”: the parent is indeterminate while some children are ticked, and clicking it ticks them all.
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Checkbox, Stack } from '@hina-ui/vue'
const types = ['Galgame', 'Light novel', 'Manga']
const picked = ref(['Galgame'])
const all = computed<boolean | 'indeterminate'>({
get: () =>
picked.value.length === 0
? false
: picked.value.length === types.length
? true
: 'indeterminate',
set: value => {
picked.value = value === true ? [...types] : []
},
})
function toggle(type: string, on: boolean | 'indeterminate') {
picked.value = on === true ? [...picked.value, type] : picked.value.filter(t => t !== type)
}
</script>
<template>
<Stack gap="sm">
<Checkbox v-model="all">All types</Checkbox>
<Stack gap="sm" class="ps-6">
<Checkbox
v-for="type in types"
:key="type"
:model-value="picked.includes(type)"
@update:model-value="toggle(type, $event)"
>
{{ type }}
</Checkbox>
</Stack>
</Stack>
</template>
Sizes
size is sm, md or lg; the box is 14, 16 or 18 pixels and the text follows.
<script setup lang="ts">
import { Checkbox, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="sm">
<Checkbox size="sm" model-value>Small</Checkbox>
<Checkbox size="md" model-value>Medium</Checkbox>
<Checkbox size="lg" model-value>Large</Checkbox>
</Stack>
</template>
States
invalid turns the box border to the danger colour; disabled disables the whole control.
<script setup lang="ts">
import { Checkbox, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack gap="sm">
<Checkbox invalid>I have read and accept the terms</Checkbox>
<Checkbox disabled>Disabled</Checkbox>
<Checkbox disabled model-value>Disabled and checked</Checkbox>
</Stack>
</template>
Box only
Without a label only the box renders, and it must then be named with aria-label. Row selection in a table is the typical case.
<script setup lang="ts">
import { ref } from 'vue'
import { Checkbox, Inline } from '@hina-ui/vue'
const rows = ref([true, false, false])
</script>
<template>
<Inline gap="sm">
<Checkbox
v-for="(_, index) in rows"
:key="index"
v-model="rows[index]"
:aria-label="`Select row ${index + 1}`"
/>
</Inline>
</template>
In a form
Inside a FormField the error message is rendered by the field and linked to the checkbox; validation rules and submission belong to the Form. The checkbox carries its own text, so the field needs no label.
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Checkbox, Form, FormField, Text } from '@hina-ui/vue'
const schema = v.object({
agreed: v.literal(true, 'Please read and accept the terms first'),
})
const values = reactive({ agreed: false as boolean | 'indeterminate' })
const saved = ref(false)
async function save() {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = true
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="agreed">
<Checkbox v-model="values.agreed">I have read and accept the terms of service</Checkbox>
</FormField>
<Button type="submit" :loading="submitting" class="self-start">Sign up</Button>
<Text v-if="saved" tone="muted" size="sm">Signed up.</Text>
</Form>
</template>
Behaviour
- Clicking the label or the box toggles. Tab lands on the box, Space toggles and Enter does not, as with a native checkbox.
- Clicking an indeterminate checkbox ticks it.
- Hovering anywhere on the control inks the box, pressing deepens it; the tick scales in and fades out.
Accessibility
- The box is a button with
role="checkbox"andaria-checked,mixedwhen indeterminate. The root is alabel, so the text is the accessible name. - Without a label, name the box with
aria-labeloraria-labelledby. invalidalso setsaria-invalid.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | boolean | 'indeterminate' | false | Whether ticked; 'indeterminate' for mixed |
size | 'sm' | 'md' | 'lg' | 'md' | Size |
description | string | — | Supporting text under the label |
controlPlacement | 'start' | 'end' | 'start' | Position of the control relative to its text |
block | boolean | false | Fill the available row width |
disabled | boolean | false | Whether the control is disabled |
invalid | boolean | false | Whether the control failed validation |
class | string | — | Classes appended to the root element |
Slots
| Slot | Payload | Description |
|---|---|---|
default | — | The label |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: boolean | The value changed |