Checkbox Basic
The checkbox mixin applies small or large checkbox styling to native inputs.
Styled input mixin
MediumLargeDisabled mdDisabled lg
Unchecked
Checked
Mixed
Group state in app code
import { css, on } from 'remix/ui'
import type { Handle } from 'remix/ui'
import checkbox, { type CheckboxState } from 'remix/ui/checkbox'
export default function Example() {
return () => (
<div mix={checkboxDemoCss}>
<section mix={sectionCss}>
<h2 mix={sectionLabelCss}>Styled input mixin</h2>
<div mix={headerCss}>
<span />
<span mix={labelCss}>Medium</span>
<span mix={labelCss}>Large</span>
<span mix={labelCss}>Disabled md</span>
<span mix={labelCss}>Disabled lg</span>
</div>
<div mix={rowCss}>
<span mix={stateLabelCss}>Unchecked</span>
<input aria-label="Unchecked medium" mix={checkbox()} />
<input aria-label="Unchecked large" mix={checkbox({ size: 'lg' })} />
<input
aria-label="Unchecked medium disabled"
disabled
mix={checkbox()}
/>
<input
aria-label="Unchecked large disabled"
disabled
mix={checkbox({ size: 'lg' })}
/>
</div>
<div mix={rowCss}>
<span mix={stateLabelCss}>Checked</span>
<input aria-label="Checked medium" defaultChecked mix={checkbox()} />
<input
aria-label="Checked large"
defaultChecked
mix={checkbox({ size: 'lg' })}
/>
<input
aria-label="Checked medium disabled"
defaultChecked
disabled
mix={checkbox()}
/>
<input
aria-label="Checked large disabled"
defaultChecked
disabled
mix={checkbox({ size: 'lg' })}
/>
</div>
<div mix={rowCss}>
<span mix={stateLabelCss}>Mixed</span>
<input
aria-label="Mixed medium"
indeterminate
mix={checkbox({ state: 'mixed' })}
/>
<input
aria-label="Mixed large"
indeterminate
mix={checkbox({ size: 'lg', state: 'mixed' })}
/>
<input
aria-label="Mixed medium disabled"
disabled
indeterminate
mix={checkbox({ state: 'mixed' })}
/>
<input
aria-label="Mixed large disabled"
disabled
indeterminate
mix={checkbox({ size: 'lg', state: 'mixed' })}
/>
</div>
</section>
<section mix={sectionCss}>
<h2 mix={sectionLabelCss}>Group state in app code</h2>
<PermissionsGroup />
</section>
</div>
)
}
type PermissionValue = (typeof permissionItems)[number]['value']
const permissionItems = [
{ label: 'Read', value: 'read' },
{ label: 'Write', value: 'write' },
{ label: 'Deploy', value: 'deploy' },
] as const
function PermissionsGroup(handle: Handle) {
let selectedPermissions = new Set<PermissionValue>(['read', 'write'])
function getParentState(): CheckboxState {
if (selectedPermissions.size === 0) {
return 'unchecked'
}
return permissionItems.every((item) => selectedPermissions.has(item.value))
? 'checked'
: 'mixed'
}
function setSelectedPermissions(
nextSelectedPermissions: Set<PermissionValue>,
) {
selectedPermissions = nextSelectedPermissions
void handle.update()
}
return () => {
let parentState = getParentState()
return (
<fieldset aria-labelledby="permissions-label" mix={groupCss}>
<legend id="permissions-label" mix={stateLabelCss}>
Permissions
</legend>
<label mix={optionCss}>
<input
checked={parentState === 'checked'}
indeterminate={parentState === 'mixed'}
mix={[
checkbox({ state: parentState }),
on('change', (event) => {
setSelectedPermissions(
event.currentTarget.checked
? new Set(permissionItems.map((item) => item.value))
: new Set<PermissionValue>(),
)
}),
]}
/>
All permissions
</label>
<div mix={nestedOptionsCss}>
{permissionItems.map((item) => (
<label key={item.value} mix={optionCss}>
<input
checked={selectedPermissions.has(item.value)}
mix={[
checkbox(),
on('change', (event) => {
let nextSelectedPermissions = new Set(selectedPermissions)
if (event.currentTarget.checked) {
nextSelectedPermissions.add(item.value)
} else {
nextSelectedPermissions.delete(item.value)
}
setSelectedPermissions(nextSelectedPermissions)
}),
]}
name="permissions"
value={item.value}
/>
{item.label}
</label>
))}
</div>
</fieldset>
)
}
}
const checkboxDemoCss = css({
display: 'grid',
gap: '28px',
width: 'min(100%, 34rem)',
})
const sectionCss = css({
display: 'grid',
gap: '12px',
})
const sectionLabelCss = css({
margin: 0,
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
fontSize: '12px',
lineHeight: '16px',
fontWeight: 650,
letterSpacing: 0,
color: 'rgba(16, 16, 16, 0.72)',
})
const headerCss = css({
display: 'grid',
gridTemplateColumns: '6.5rem repeat(4, minmax(4.25rem, 1fr))',
alignItems: 'end',
gap: '14px',
})
const rowCss = css({
display: 'grid',
gridTemplateColumns: '6.5rem repeat(4, minmax(4.25rem, 1fr))',
alignItems: 'center',
gap: '14px',
minHeight: '32px',
})
const labelCss = css({
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
fontSize: '11px',
lineHeight: '14px',
fontWeight: 600,
letterSpacing: 0,
color: 'rgba(16, 16, 16, 0.58)',
})
const stateLabelCss = css({
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
fontSize: '13px',
lineHeight: '18px',
fontWeight: 600,
letterSpacing: 0,
color: '#101010',
})
const groupCss = css({
display: 'grid',
gap: '8px',
margin: 0,
padding: 0,
border: 0,
})
const optionCss = css({
display: 'flex',
alignItems: 'center',
gap: '8px',
minHeight: '28px',
fontFamily: '"Inter Variable", Inter, ui-sans-serif, system-ui, sans-serif',
fontSize: '13px',
lineHeight: '18px',
fontWeight: 500,
letterSpacing: 0,
color: '#101010',
})
const nestedOptionsCss = css({
display: 'grid',
gap: '6px',
paddingLeft: '24px',
})