Ir para o conteúdo

Checkbox

Caixa de seleção baseada no input checkbox nativo.

Navegadores
Chrome 123+Firefox 121+Safari 17.5+
Nível
0
Tamanho compactado
448 B
Limite de layout forçado
0 ms
Limite do axe
0
Compatível com SSR
Sim
Contrato publicado
Ambiente de teste

Experimentar

Checkbox

Padrão

Indeterminate

Desativado

import { Checkbox } from "@gunkin/uiify";

export default function Example() {
  return <Checkbox className="" id="terms" label="Accept terms" />;
}
/* Checkbox, Radio, Switch — all native <input>s styled with appearance:none.
   State comes from :checked / :indeterminate (works uncontrolled) and the
   [data-state] / [data-disabled] attributes (set when controlled). Thumbs and
   checkmarks are pseudo-elements on the input itself. */

@layer components {
  :where([data-uiify-checkbox] > input, [data-uiify-radio]) {
    appearance: none;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    margin: 0;
    border: var(--border-width) solid var(--input);
    background-color: var(--card);
    display: inline-grid;
    place-content: center;
    cursor: pointer;
    transition: var(--transition-colors);
  }

  [data-uiify-checkbox] {
    --uiify-checkbox-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.6 8.6l3 3L12.4 4.8' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
      center / 100% 100% no-repeat;

    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    cursor: pointer;
    user-select: none;
    color: var(--foreground);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
  }

  [data-uiify-checkbox] > input {
    border-radius: var(--radius-xs);
    corner-shape: squircle;
  }

  [data-uiify-radio] {
    border-radius: var(--radius-full);
  }

  /* Checked fill (covers controlled [data-state] and uncontrolled :checked). */
  [data-uiify-checkbox] > input:checked,
  [data-uiify-checkbox] > input[data-state="checked"] {
    border-color: var(--primary);
    background-color: var(--primary);
  }

  /* Radio checked state is a thick accent ring around a white center, not a
     filled dot — matches the 2a/2b handoff's target/bullseye radio anatomy. */
  [data-uiify-radio]:checked,
  [data-uiify-radio][data-state="checked"] {
    border-width: 5px;
    border-color: var(--primary);
    background-color: var(--card);
  }

  /* Checkmark. */
  [data-uiify-checkbox] > input::before {
    content: "";
    inline-size: 1rem;
    block-size: 1rem;
    transform: scale(0);
    transition: transform var(--duration-fast) var(--ease-out);
    background-color: var(--primary-foreground);
    mask: var(--uiify-checkbox-mask);
  }

  [data-uiify-checkbox] > input:checked::before,
  [data-uiify-checkbox] > input[data-state="checked"]::before {
    transform: scale(1);
  }

  /* Indeterminate dash. */
  [data-uiify-checkbox] > input:indeterminate,
  [data-uiify-checkbox] > input[data-state="indeterminate"] {
    border-color: var(--primary);
    background-color: var(--primary);
  }

  [data-uiify-checkbox] > input:indeterminate::before,
  [data-uiify-checkbox] > input[data-state="indeterminate"]::before {
    transform: scale(1);
    mask: none;
    clip-path: inset(42% 12% 42% 12%);
  }

  :where([data-uiify-checkbox] > input, [data-uiify-radio]):disabled,
  :where([data-uiify-checkbox] > input, [data-uiify-radio])[data-disabled] {
    opacity: 0.5;
    cursor: not-allowed;
  }

  [data-uiify-checkbox]:has(> input:disabled) {
    opacity: 0.5;
    cursor: not-allowed;
  }

  [data-uiify-radio-group] {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
  }

  /* Switch — a pill track with a sliding thumb pseudo-element. */
  [data-uiify-switch] {
    appearance: none;
    position: relative;
    inline-size: 2.25rem;
    block-size: 1.25rem;
    margin: 0;
    flex: none;
    border: none;
    border-radius: var(--radius-full);
    background-color: var(--input);
    cursor: pointer;
    transition: background-color var(--duration-fast) var(--ease-out);
  }

  [data-uiify-switch]::before {
    content: "";
    position: absolute;
    inset-block-start: 2px;
    inset-inline-start: 2px;
    inline-size: calc(1.25rem - 4px);
    block-size: calc(1.25rem - 4px);
    border-radius: var(--radius-full);
    background-color: var(--gray-0);
    box-shadow: var(--shadow-1);
    transition: translate var(--duration-fast) var(--ease-out);
  }

  [data-uiify-switch]:checked,
  [data-uiify-switch][data-state="checked"] {
    background-color: var(--primary);
  }

  [data-uiify-switch]:checked::before,
  [data-uiify-switch][data-state="checked"]::before {
    translate: 1rem 0;
  }

  [data-uiify-switch]:disabled,
  [data-uiify-switch][data-disabled] {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

@layer components {
  label:has([data-uiify-radio]),
  [data-uiify-radio-group] label {
    user-select: none;
    cursor: pointer;
  }
}
import { Checkbox } from "@gunkin/uiify";

export default function Example() {
  return (
    <Checkbox
      className={
        "focus-visible:[outline:none] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ri" +
        "ng)_16%,_transparent)] [&>input]:appearance-none [&>input]:[inline-size:1.25rem] [&>input]:[block-size:1.25" +
        "rem] [&>input]:m-0 [&>input]:border-[length:var(--border-width)] [&>input]:border-solid [&>input]:border-in" +
        "put [&>input]:bg-card [&>input]:inline-grid [&>input]:place-content-center [&>input]:cursor-pointer [&>inpu" +
        "t]:[transition:var(--transition-colors)] [--uiify-checkbox-mask:url(\"data:image/svg+xml,%3Csvg%20xmlns=%27h" +
        "ttp://www.w3.org/2000/svg%27%20viewBox=%270%200%2016%2016%27%3E%3Cpath%20d=%27M3.6%208.6l3%203L12.4%204.8%2" +
        "7%20fill=%27none%27%20stroke=%27%23000%27%20stroke-width=%272%27%20stroke-linecap=%27round%27%20stroke-line" +
        "join=%27round%27/%3E%3C/svg%3E\")_center_/_100%_100%_no-repeat] inline-flex items-center gap-sm cursor-point" +
        "er select-none text-foreground text-sm [line-height:var(--leading-normal)] [&>input]:rounded-xs [&>input]:[" +
        "corner-shape:squircle] [&>input:checked]:border-primary [&>input:checked]:bg-primary [&>input[data-state=ch" +
        "ecked]]:border-primary [&>input[data-state=checked]]:bg-primary [&>input::before]:content-[''] [&>input::be" +
        "fore]:[inline-size:1rem] [&>input::before]:[block-size:1rem] [&>input::before]:[transform:scale(0)] [&>inpu" +
        "t::before]:[transition:transform_var(--duration-fast)_var(--ease-out)] [&>input::before]:bg-primary-foregro" +
        "und [&>input::before]:[mask:var(--uiify-checkbox-mask)] [&>input:checked::before]:[transform:scale(1)] [&>i" +
        "nput[data-state=checked]::before]:[transform:scale(1)] [&>input:indeterminate]:border-primary [&>input:inde" +
        "terminate]:bg-primary [&>input[data-state=indeterminate]]:border-primary [&>input[data-state=indeterminate]" +
        "]:bg-primary [&>input:indeterminate::before]:[transform:scale(1)] [&>input:indeterminate::before]:[mask:non" +
        "e] [&>input:indeterminate::before]:[clip-path:inset(42%_12%_42%_12%)] [&>input[data-state=indeterminate]::b" +
        "efore]:[transform:scale(1)] [&>input[data-state=indeterminate]::before]:[mask:none] [&>input[data-state=ind" +
        "eterminate]::before]:[clip-path:inset(42%_12%_42%_12%)] [&>input:disabled]:opacity-50 [&>input:disabled]:cu" +
        "rsor-not-allowed [&>input[data-disabled]]:opacity-50 [&>input[data-disabled]]:cursor-not-allowed [&:has(>in" +
        "put:disabled)]:opacity-50 [&:has(>input:disabled)]:cursor-not-allowed"
      }
      id="terms"
      label="Accept terms"
    />
  );
}
/* app.css */

@layer reset, ui, app, tokens, base, demo, components, utilities, theme;

@import "@gunkin/uiify/styles/reset";

@import "@gunkin/uiify/components/behavior.css";

@import "@gunkin/uiify/styles/tokens";

@import "@gunkin/uiify/styles/tailwind";

@import "tailwindcss/utilities.css";

@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }

@source inline('focus-visible:[outline:none] focus-visible:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [&>input]:appearance-none [&>input]:[inline-size:1.25rem] [&>input]:[block-size:1.25rem] [&>input]:m-0 [&>input]:border-[length:var(--border-width)] [&>input]:border-solid [&>input]:border-input [&>input]:bg-card [&>input]:inline-grid [&>input]:place-content-center [&>input]:cursor-pointer [&>input]:[transition:var(--transition-colors)] [--uiify-checkbox-mask:url("data:image/svg+xml,%3Csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20viewBox=%270%200%2016%2016%27%3E%3Cpath%20d=%27M3.6%208.6l3%203L12.4%204.8%27%20fill=%27none%27%20stroke=%27%23000%27%20stroke-width=%272%27%20stroke-linecap=%27round%27%20stroke-linejoin=%27round%27/%3E%3C/svg%3E")_center_/_100%_100%_no-repeat] inline-flex items-center gap-sm cursor-pointer select-none text-foreground text-sm [line-height:var(--leading-normal)] [&>input]:rounded-xs [&>input]:[corner-shape:squircle] [&>input:checked]:border-primary [&>input:checked]:bg-primary [&>input[data-state=checked]]:border-primary [&>input[data-state=checked]]:bg-primary [&>input::before]:[inline-size:1rem] [&>input::before]:[block-size:1rem] [&>input::before]:[transform:scale(0)] [&>input::before]:[transition:transform_var(--duration-fast)_var(--ease-out)] [&>input::before]:bg-primary-foreground [&>input::before]:[mask:var(--uiify-checkbox-mask)] [&>input:checked::before]:[transform:scale(1)] [&>input[data-state=checked]::before]:[transform:scale(1)] [&>input:indeterminate]:border-primary [&>input:indeterminate]:bg-primary [&>input[data-state=indeterminate]]:border-primary [&>input[data-state=indeterminate]]:bg-primary [&>input:indeterminate::before]:[transform:scale(1)] [&>input:indeterminate::before]:[mask:none] [&>input:indeterminate::before]:[clip-path:inset(42%_12%_42%_12%)] [&>input[data-state=indeterminate]::before]:[transform:scale(1)] [&>input[data-state=indeterminate]::before]:[mask:none] [&>input[data-state=indeterminate]::before]:[clip-path:inset(42%_12%_42%_12%)] [&>input:disabled]:opacity-50 [&>input:disabled]:cursor-not-allowed [&>input[data-disabled]]:opacity-50 [&>input[data-disabled]]:cursor-not-allowed [&:has(>input:disabled)]:opacity-50 [&:has(>input:disabled)]:cursor-not-allowed');

@source inline("[&>input::before]:content-['']");

@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }

/* Authored animation and registered properties used by the Tailwind skin.
 * Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
  @keyframes uiify-presence-in {
    from {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@layer components {
  @keyframes uiify-presence-out {
    to {
      opacity: 0;
      scale: var(--presence-scale);
    }
  }
}

@property --uiify-spinner-tail {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@property --uiify-spinner-length {
  syntax: "<angle>";
  inherits: false;
  initial-value: 36deg;
}

@layer components {
  @keyframes uiify-spinner-rotate {
    to {
      transform: rotate(360deg);
    }
  }
}

@layer components {
  @keyframes uiify-spinner-orbit {
    0% {
      --uiify-spinner-tail: 0deg;
      --uiify-spinner-length: 36deg;
    }

    50% {
      --uiify-spinner-tail: 90deg;
      --uiify-spinner-length: 324deg;
    }

    100% {
      --uiify-spinner-tail: 360deg;
      --uiify-spinner-length: 36deg;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-pulse {
    50% {
      opacity: 0.45;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-shimmer {
    to {
      background-position: -200% 0;
    }
  }
}

@layer components {
  @keyframes uiify-skeleton-reveal {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
}

@property --candle-body-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-body-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-slot {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 100%;
}

@property --candle-wick-height {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-wick-top {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --candle-x {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 0%;
}

@property --candle-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@property --line-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-prev-y {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

@property --line-count {
  syntax: "<integer>";
  inherits: false;
  initial-value: 1;
}

@property --line-scale {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

@layer components {
  @keyframes uiify-otp-input-caret-blink {
    50% {
      opacity: 0;
    }
  }
}

API

Checkbox

PropTipoPadrãoDescrição
id*string—Id do input nativo, também usado para conectar o elemento de rótulo.
labelReactNode—Conteúdo do rótulo renderizado ao lado do input, dentro de um elemento label que os envolve.
checkedboolean | "indeterminate"—Estado marcado, tornando a caixa de seleção controlada; "indeterminate" renderiza o estado misto.
defaultCheckedboolean—Estado marcado inicial da caixa de seleção não controlada, que passa a ser gerenciado pelo navegador.
disabledbooleanfalseDesabilita o input nativo e define o atributo data-disabled.
namestring—Name used when the checkbox is submitted with a form.
valuestring—Value submitted when the checkbox is checked.
classNamestring—Classe aplicada ao elemento label envolvente quando há um rótulo.
onCheckedChange(checked: boolean) => void—Chamado com o novo estado marcado quando o usuário alterna a caixa de seleção.
aria-labelstring—Nome acessível usado quando nenhum rótulo é fornecido.

Teclado

TeclaAção
SpaceAlterne quando estiver em foco

Acessibilidade

Input checkbox nativo. Estado indeterminado quando aplicável.

Pontos de integração de estilos

data-statedata-disabled