Ir para o conteúdo
uiify

Tabs

Painéis com navegação por teclado entre abas.

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

Experimentar

Tabs

Automática

Receita mensalAtualizado agora
$12,480
Atividade recente12 novos eventos nesta semana
Configurações do espaço de trabalhoGerenciar integrantes e preferências

Manual

Receita mensalAtualizado agora
$12,480
Atividade recente12 novos eventos nesta semana
Configurações do espaço de trabalhoGerenciar integrantes e preferências
import { Tabs } from "@gunkin/uiify";

export default function Example() {
  return (
    <Tabs
      className=""
      id="account-tabs"
      defaultValue="profile"
      items={[
        {
          value: "profile",
          label: <span className="">Profile</span>,
          panel: <div className="">Profile settings</div>,
        },
        {
          value: "security",
          label: <span className="">Security</span>,
          panel: <div className="">Security settings</div>,
        },
      ]}
    />
  );
}
/* Tabs — list of triggers with an active underline; content panels below.
   Adapts to [data-orientation]; active state from [data-state="active"]. */

@layer components {
  [data-uiify-tabs] [data-part="trigger"] {
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border: none;
    background: none;
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    font-weight: var(--weight-medium);
    cursor: pointer;
    border-block-end: 2px solid transparent;
    margin-block-end: calc(-1 * var(--border-width));
    transition: var(--transition-colors);
  }

  [data-uiify-tabs] [data-part="trigger"]:hover {
    color: var(--foreground);
  }

  [data-uiify-tabs] [data-part="trigger"][data-state="active"] {
    border-block-end-color: var(--primary);
    color: var(--foreground);
  }

  /* Panel visibility is owned by @gunkin/uiify/components/behavior.css (radio :checked). */

  [data-uiify-tabs] [data-part="panel"] {
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
  }

  [data-uiify-tabs] > [data-part="item"] {
    display: contents;
  }

  [data-uiify-tabs] {
    display: grid;
    grid-template-rows: auto auto;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    align-items: start;
    gap: 0;
  }

  [data-uiify-tabs] > [data-part="item"] > input {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  [data-uiify-tabs] > [data-part="item"] > label {
    display: inline-flex;
    grid-row: 1;
    padding-block: var(--space-sm);
    padding-inline: var(--space-md);
    border-block-end: 2px solid transparent;
    color: var(--muted-foreground);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    font-weight: var(--weight-medium);
    cursor: pointer;
  }

  [data-uiify-tabs] > [data-part="item"] > label:hover {
    color: var(--foreground);
  }

  [data-uiify-tabs] > [data-part="item"]:has(> input:checked) > label {
    border-block-end-color: var(--primary);
    color: var(--foreground);
  }

  [data-uiify-tabs] > [data-part="item"] > input:focus-visible + label {
    outline: none;
    box-shadow: 0 0 0 var(--ring-width) color-mix(in oklch, var(--ring) 16%, transparent);
  }

  [data-uiify-tabs] > [data-part="item"] > [data-part="panel"] {
    grid-column: 1 / -1;
    grid-row: 2;
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
  }

  /* Shared presence fade for the newly selected panel; the previous one leaves at once. */
  @media (prefers-reduced-motion: no-preference) {
    [data-uiify-tabs] > [data-part="item"] > [data-part="panel"] {
      transition: opacity var(--duration-fast) var(--ease-out);
    }

    @starting-style {
      [data-uiify-tabs] > [data-part="item"] > [data-part="control"]:checked ~ [data-part="panel"] {
        opacity: 0;
      }
    }
  }
}
import { Tabs } from "@gunkin/uiify";

export default function Example() {
  return (
    <Tabs
      className={
        "[&_[data-part=trigger]]:py-sm [&_[data-part=trigger]]:px-md [&_[data-part=trigger]]:border-0 [&_[data-part=" +
        "trigger]]:border-current [&_[data-part=trigger]]:border-none [&_[data-part=trigger]]:bg-none [&_[data-part=" +
        "trigger]]:bg-transparent [&_[data-part=trigger]]:text-muted-foreground [&_[data-part=trigger]]:text-sm [&_[" +
        "data-part=trigger]]:[line-height:var(--leading-normal)] [&_[data-part=trigger]]:font-[number:var(--weight-m" +
        "edium)] [&_[data-part=trigger]]:cursor-pointer [&_[data-part=trigger]]:[border-block-end-width:2px] [&_[dat" +
        "a-part=trigger]]:[border-block-end-style:solid] [&_[data-part=trigger]]:[border-block-end-color:transparent" +
        "] [&_[data-part=trigger]]:[margin-block-end:calc(-1_*_var(--border-width))] [&_[data-part=trigger]]:[transi" +
        "tion:var(--transition-colors)] [&_[data-part=trigger]:hover]:text-foreground [&_[data-part=trigger][data-st" +
        "ate=active]]:[border-block-end-color:var(--primary)] [&_[data-part=trigger][data-state=active]]:text-foregr" +
        "ound [&_[data-part=panel]]:text-sm [&_[data-part=panel]]:[line-height:var(--leading-normal)] [&>[data-part=" +
        "item]]:contents grid [grid-template-rows:auto_auto] [grid-auto-flow:column] [grid-auto-columns:max-content]" +
        " [align-items:start] gap-0 [&>[data-part=item]>input]:absolute [&>[data-part=item]>input]:[inline-size:1px]" +
        " [&>[data-part=item]>input]:[block-size:1px] [&>[data-part=item]>input]:[margin:-1px] [&>[data-part=item]>i" +
        "nput]:overflow-hidden [&>[data-part=item]>input]:[clip-path:inset(50%)] [&>[data-part=item]>input]:whitespa" +
        "ce-nowrap [&>[data-part=item]>label]:inline-flex [&>[data-part=item]>label]:[grid-row:1] [&>[data-part=item" +
        "]>label]:py-sm [&>[data-part=item]>label]:px-md [&>[data-part=item]>label]:[border-block-end-width:2px] [&>" +
        "[data-part=item]>label]:[border-block-end-style:solid] [&>[data-part=item]>label]:[border-block-end-color:t" +
        "ransparent] [&>[data-part=item]>label]:text-muted-foreground [&>[data-part=item]>label]:text-sm [&>[data-pa" +
        "rt=item]>label]:[line-height:var(--leading-normal)] [&>[data-part=item]>label]:font-[number:var(--weight-me" +
        "dium)] [&>[data-part=item]>label]:cursor-pointer [&>[data-part=item]>label:hover]:text-foreground [&>[data-" +
        "part=item]:has(>input:checked)>label]:[border-block-end-color:var(--primary)] [&>[data-part=item]:has(>inpu" +
        "t:checked)>label]:text-foreground [&>[data-part=item]>input:focus-visible+label]:[outline:none] [&>[data-pa" +
        "rt=item]>input:focus-visible+label]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%" +
        ",_transparent)] [&>[data-part=item]>[data-part=panel]]:[grid-column:1_/_-1] [&>[data-part=item]>[data-part=" +
        "panel]]:[grid-row:2] [&>[data-part=item]>[data-part=panel]]:text-sm [&>[data-part=item]>[data-part=panel]]:" +
        "[line-height:var(--leading-normal)] motion-safe:[&>[data-part=item]>[data-part=panel]]:[transition:opacity_" +
        "var(--duration-fast)_var(--ease-out)] motion-safe:starting:[&>[data-part=item]>[data-part=control]:checked~" +
        "[data-part=panel]]:opacity-0"
      }
      id="account-tabs"
      defaultValue="profile"
      items={[
        {
          value: "profile",
          label: <span className="">Profile</span>,
          panel: <div className="">Profile settings</div>,
        },
        {
          value: "security",
          label: <span className="">Security</span>,
          panel: <div className="">Security settings</div>,
        },
      ]}
    />
  );
}
/* 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('[&_[data-part=trigger]]:py-sm [&_[data-part=trigger]]:px-md [&_[data-part=trigger]]:border-0 [&_[data-part=trigger]]:border-current [&_[data-part=trigger]]:border-none [&_[data-part=trigger]]:bg-none [&_[data-part=trigger]]:bg-transparent [&_[data-part=trigger]]:text-muted-foreground [&_[data-part=trigger]]:text-sm [&_[data-part=trigger]]:[line-height:var(--leading-normal)] [&_[data-part=trigger]]:font-[number:var(--weight-medium)] [&_[data-part=trigger]]:cursor-pointer [&_[data-part=trigger]]:[border-block-end-width:2px] [&_[data-part=trigger]]:[border-block-end-style:solid] [&_[data-part=trigger]]:[border-block-end-color:transparent] [&_[data-part=trigger]]:[margin-block-end:calc(-1_*_var(--border-width))] [&_[data-part=trigger]]:[transition:var(--transition-colors)] [&_[data-part=trigger]:hover]:text-foreground [&_[data-part=trigger][data-state=active]]:[border-block-end-color:var(--primary)] [&_[data-part=trigger][data-state=active]]:text-foreground [&_[data-part=panel]]:text-sm [&_[data-part=panel]]:[line-height:var(--leading-normal)] [&>[data-part=item]]:contents grid [grid-template-rows:auto_auto] [grid-auto-flow:column] [grid-auto-columns:max-content] [align-items:start] gap-0 [&>[data-part=item]>input]:absolute [&>[data-part=item]>input]:[inline-size:1px] [&>[data-part=item]>input]:[block-size:1px] [&>[data-part=item]>input]:[margin:-1px] [&>[data-part=item]>input]:overflow-hidden [&>[data-part=item]>input]:[clip-path:inset(50%)] [&>[data-part=item]>input]:whitespace-nowrap [&>[data-part=item]>label]:inline-flex [&>[data-part=item]>label]:[grid-row:1] [&>[data-part=item]>label]:py-sm [&>[data-part=item]>label]:px-md [&>[data-part=item]>label]:[border-block-end-width:2px] [&>[data-part=item]>label]:[border-block-end-style:solid] [&>[data-part=item]>label]:[border-block-end-color:transparent] [&>[data-part=item]>label]:text-muted-foreground [&>[data-part=item]>label]:text-sm [&>[data-part=item]>label]:[line-height:var(--leading-normal)] [&>[data-part=item]>label]:font-[number:var(--weight-medium)] [&>[data-part=item]>label]:cursor-pointer [&>[data-part=item]>label:hover]:text-foreground [&>[data-part=item]:has(>input:checked)>label]:[border-block-end-color:var(--primary)] [&>[data-part=item]:has(>input:checked)>label]:text-foreground [&>[data-part=item]>input:focus-visible+label]:[outline:none] [&>[data-part=item]>input:focus-visible+label]:[box-shadow:0_0_0_var(--ring-width)_color-mix(in_oklch,_var(--ring)_16%,_transparent)] [&>[data-part=item]>[data-part=panel]]:[grid-column:1_/_-1] [&>[data-part=item]>[data-part=panel]]:[grid-row:2] [&>[data-part=item]>[data-part=panel]]:text-sm [&>[data-part=item]>[data-part=panel]]:[line-height:var(--leading-normal)] motion-safe:[&>[data-part=item]>[data-part=panel]]:[transition:opacity_var(--duration-fast)_var(--ease-out)] motion-safe:starting:[&>[data-part=item]>[data-part=control]:checked~[data-part=panel]]:opacity-0');

@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

Tabs

PropTipoPadrãoDescrição
id*string—Id base usado para nomear o grupo de opções e conectar cada aba ao painel.
items*TabsItem[]—Abas a renderizar, cada uma com valor, rótulo e painel opcional renderizado quando a aba é selecionada.
defaultValuestring—Valor da aba marcada na marcação renderizada no servidor, selecionando a aba inicial antes de qualquer JavaScript.
orientation"horizontal" | "vertical""horizontal"Direção em que a barra de abas é disposta.
classNamestring—Classe aplicada ao elemento raiz.
appendReactNode—Conteúdo renderizado no fim da barra de abas, após as abas.

Teclado

TeclaAção
← / →Mova o foco entre abas (horizontal)
Home / EndPrimeira / última aba
Space / EnterAtive a aba

Acessibilidade

Tablist + inputs radio nativos ou padrão de tabindex móvel quando aprimorado.

Pontos de integração de estilos

data-state
Esta página foi traduzida automaticamente e ainda não foi revisada.