fix(ui): refactor checkbox and consistent outlines (#817)

This commit is contained in:
Mees Frensel
2026-09-23 05:22:56 -04:00
committed by GitHub
parent 9abc37d473
commit 5b0a40c5d6
5 changed files with 21 additions and 26 deletions
@@ -31,7 +31,7 @@
const size = $derived(initialSize ?? labelProps.size ?? 'small');
const containerStyles = tv({
base: 'ring-offset-background focus-visible:ring-ring peer data-[state=checked]:bg-primary box-content overflow-hidden border-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
base: 'ring-offset-background peer data-[state=checked]:bg-primary box-content overflow-hidden border-2 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
variants: {
shape: styleVariants.shape,
borderColor: styleVariants.borderColor,
@@ -12,7 +12,11 @@
<a
href={resolved}
draggable="false"
class={cleanClass(underline && 'underline', className)}
class={cleanClass(
underline && 'underline',
'rounded-xs outline-primary outline-offset-1 focus-visible:outline-2',
className,
)}
target={external ? '_blank' : undefined}
rel={external ? 'noopener noreferrer' : undefined}
{...restProps}
@@ -16,7 +16,7 @@
{#if previous}
<a
href={previous.href}
class="hover:border-primary hover:text-primary flex items-center gap-2 rounded-xl border p-4 transition-colors"
class="hover:border-primary hover:text-primary flex items-center gap-2 rounded-xl border p-4 transition-colors outline-primary -outline-offset-1 focus-visible:outline-2"
>
<Icon icon={mdiChevronLeft} size="1.5rem" directional />
<div class="min-w-0">
@@ -30,7 +30,7 @@
{#if next}
<a
href={next.href}
class="hover:border-primary hover:text-primary flex items-center justify-end gap-2 rounded-xl border p-4 text-end transition-colors"
class="hover:border-primary hover:text-primary flex items-center justify-end gap-2 rounded-xl border p-4 text-end transition-colors outline-primary -outline-offset-1 focus-visible:outline-2"
>
<div class="min-w-0">
<Text color="muted" size="tiny">{t('navigate_next', translations)}</Text>
@@ -29,22 +29,16 @@
const enabled = $derived(checked && !disabled);
const wrapper = tv({
base: 'relative flex flex-col justify-center',
const bar = tv({
base: 'h-8 w-13 rounded-full border-2 outline-offset-2 focus-visible:outline-2',
variants: {
disabled: {
true: 'cursor-not-allowed opacity-38',
false: 'cursor-pointer',
},
},
});
const bar = tv({
base: 'h-8 w-13 rounded-full border-2',
variants: {
fillColor: {
default: 'border-light-400 bg-light-200 dark:border-gray-500',
primary: 'bg-primary-100 dark:bg-primary-200 border-transparent',
primary: 'bg-primary-100 dark:bg-primary-200 border-transparent outline-primary',
secondary: 'bg-light-200 dark:bg-light-300 border-transparent',
success: 'bg-success-100 dark:bg-success-200 border-transparent',
danger: 'bg-danger-100 dark:bg-danger-200 border-transparent',
@@ -55,12 +49,8 @@
});
const dot = tv({
base: 'absolute h-4 w-4 origin-center rounded-full transition-transform duration-100',
base: 'block size-4 rounded-full transition-transform duration-100 data-[state=unchecked]:translate-x-1.5 data-[state=unchecked]:rtl:-translate-x-1.5 data-[state=checked]:translate-x-6.5 data-[state=checked]:scale-150 data-[state=checked]:rtl:-translate-x-6.5',
variants: {
checked: {
true: 'translate-x-7 scale-150 rtl:-translate-x-7',
false: 'translate-x-2 rtl:-translate-x-2',
},
fillColor: {
default: 'bg-gray-600 dark:bg-gray-500',
primary: 'bg-primary',
@@ -74,7 +64,7 @@
});
const inputId = $derived(`input-${id}`);
const labelId = $derived(`label-${id}`);
const labelId = $derived(label ? `label-${id}` : restProps['aria-labelledby']);
const descriptionId = $derived(description ? `description-${id}` : restProps['aria-describedby']);
</script>
@@ -92,6 +82,7 @@
bind:checked
bind:ref
id={inputId}
class={bar({ disabled, fillColor: enabled ? color : 'default' })}
disabled={disabled || readOnly}
required={!!required}
aria-readonly={readOnly}
@@ -99,11 +90,6 @@
aria-describedby={descriptionId}
{...restProps}
>
<Switch.Thumb>
<span class={wrapper({ disabled })}>
<span class={bar({ fillColor: enabled ? color : 'default' })}> </span>
<span class={dot({ checked: enabled, fillColor: enabled ? color : 'default' })}></span>
</span>
</Switch.Thumb>
<Switch.Thumb class={dot({ fillColor: enabled ? color : 'default' })} />
</Switch.Root>
</div>
@@ -15,7 +15,12 @@
<ul class="mt-3 flex flex-col gap-2 border-s text-sm">
{#each items as item (item.id)}
<li class={item.level === 3 ? 'ps-6' : 'ps-3'}>
<a href="#{item.id}" class="text-muted hover:text-primary block transition-colors">{item.text}</a>
<a
href="#{item.id}"
class="text-muted hover:text-primary block transition-colors rounded-xs outline-primary outline-offset-1 focus-visible:outline-2"
>
{item.text}
</a>
</li>
{/each}
</ul>