Create Your Own Component

Checkbox

Toggle options on or off.

/// DEMO START ///

Helper text here

Checked: false

/// DEMO END ///

1
<script lang="ts">
2
  import Checkbox from '$lib/components/base/checkbox.svelte';
3
  let checked = $state(false);
4
</script>
5

6
<Checkbox label="Label here" bind:checked>Helper text here</Checkbox>
7
<p>Checked: {checked}</p>
8

Source Code

Create checkbox component.

1
<script lang="ts">
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
  import type { Snippet } from 'svelte';
4
  import { circInOut } from 'svelte/easing';
5
  import { draw } from 'svelte/transition';
6
  import { twMerge } from 'tailwind-merge';
7

8
  type Props = HTMLInputAttributes & {
9
    children?: Snippet;
10
    class?: string;
11
    disabled?: boolean;
12
    label?: string;
13
  };
14

15
  let {
16
    checked = $bindable(false),
17
    children,
18
    class: className,
19
    disabled = false,
20
    label = '',
21
    ...props
22
  }: Props = $props();
23
</script>
24

25
<div>
26
  <!-- Hide input checkbox -->
27
  <input class="hidden appearance-none" type="checkbox" bind:checked {disabled} {...props} />
28

29
  <!-- Customize checkbox container here -->
30
  <label class={twMerge('flex items-center w-fit text-fg cursor-pointer', disabled && 'text-disabled-fg')}>
31
    <!-- Customize box here -->
32
    <button
33
      class={twMerge(
34
        'place-content-center grid mr-2 min-w-6 min-h-6', // layout and positioning
35
        'outline-primary outline-offset-2 hover:outline focus:outline', // outline
36
        'cursor-pointer rounded border', // visual
37
        checked && 'bg-primary',
38
        disabled && 'bg-bg cursor-not-allowed outline-none'
39
      )}
40
      onclick={() => (checked = !checked)}
41
      {disabled}
42
    >
43
      <div class="pointer-events-none">
44
        {#if checked}
45
          <!-- Customize icon snippet below -->
46
          <span class={twMerge(disabled ? 'text-disabled-fg' : 'text-primary-fg')}>
47
            {@render CheckMark()}
48
          </span>
49
        {:else}
50
          <!-- Customize icon snippet below -->
51
          {@render CrossMark()}
52
        {/if}
53
      </div>
54
    </button>
55

56
    <!-- Customize label here -->
57
    {label}
58
  </label>
59

60
  {#if children}
61
    <!-- Customize description here -->
62
    <div class={twMerge('ml-8 text-muted', disabled && 'text-disabled-fg')}>
63
      {@render children?.()}
64
    </div>
65
  {/if}
66
</div>
67

68
<!-- Customize icon for check mark -->
69
{#snippet CheckMark()}
70
  <svg class="size-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 -4 32 31" fill="none">
71
    <path
72
      in:draw={{ duration: 150, easing: circInOut }}
73
      stroke-width="5"
74
      d="M1 16L8 23L30.5 0.5"
75
      stroke="currentColor"
76
    />
77
  </svg>
78
{/snippet}
79

80
<!-- Customize icon for cross mark -->
81
{#snippet CrossMark()}
82
  <svg class="size-2.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 25" fill="none">
83
    <path in:draw={{ duration: 100, easing: circInOut }} stroke-width="4" d="M0.5 1L23.5 24" stroke="currentColor" />
84
    <path
85
      in:draw={{ delay: 100, duration: 100, easing: circInOut }}
86
      stroke-width="4"
87
      d="M23.5 1L0.5 24"
88
      stroke="currentColor"
89
    />
90
  </svg>
91
{/snippet}
92

Share treats:

Copyright © 2025 - Cliemtor Fabros