Atomic UI Kit v2.2
molecule · nível 2 de 3

Molecules

Combinações simples de átomos com propósito único. Uma molécula resolve exatamente um problema de UX. Copie a unidade inteira.

Átomos combinados Propósito único Zero JS
M01

Field

A unidade fundamental de formulário: Label + Input + Hint/Error.

Como aparece no seu documento oficial.

Aparece no seu perfil.

0 / 160

<!-- Obrigatório -->
<div class="flex flex-col gap-1">
  <label for="campo"
         class="text-sm font-medium text-zinc-700">
    Rótulo
    <span class="text-red-500" aria-hidden="true">*</span>
  </label>
  <input id="campo" type="text" required
         class="w-full px-3 py-2 text-sm
                text-zinc-900 bg-white
                border border-zinc-300
                placeholder:text-zinc-400
                hover:border-zinc-400
                focus:outline-none focus:ring-2
                focus:ring-zinc-900 focus:border-transparent
                transition-colors" />
  <p class="text-xs text-zinc-500">Hint.</p>
</div>

<!-- Campo com erro -->
<input aria-invalid="true" aria-describedby="msg"
       class="... border-red-500 focus:ring-red-500"/>
<p id="msg" role="alert"
   class="text-xs text-red-600
          flex items-center gap-1.5">
  Mensagem de erro.
</p>

<!-- Textarea com contador -->
<textarea maxlength="160" ...></textarea>
<div class="flex justify-between">
  <p class="text-xs text-zinc-500">Hint.</p>
  <p class="mono text-xs text-zinc-400">0 / 160</p>
</div>
M03

Alert / Banner

Ícone + Título + Mensagem. Use role="alert" para urgentes, role="status" para informativos.

Manutenção programada

O sistema ficará indisponível no dia 25/03 das 02h às 04h.

<!-- Sucesso -->
<div role="alert"
     class="flex gap-3 p-4
            bg-green-50 border border-green-200">
  <!-- SVG ícone -->
  <div>
    <p class="text-sm font-semibold text-green-800">Título</p>
    <p class="text-xs text-green-700 mt-0.5">Mensagem.</p>
  </div>
</div>

<!-- Mapa de cores:
  sucesso  → green-50 / green-200 / green-800
  warning  → yellow-50 / yellow-200 / yellow-800
  erro     → red-50 / red-200 / red-800
  info     → blue-50 / blue-200 / blue-800
  neutro   → zinc-50 / zinc-200 / zinc-800

  role="alert"  → urgente (lido imediatamente)
  role="status" → informativo (lido quando possível)
-->
M04

Card

Container semântico de agrupamento. Variações de estrutura interna para diferentes densidades de informação.

Card simples

Agrupa informações relacionadas numa unidade visual coesa.

Com cabeçalho

Use o cabeçalho para título e ações contextuais.

Com rodapé

O rodapé abriga as ações primárias relacionadas.

Card clicável

Todo o card é interativo. Ideal para listagens navegáveis.

<!-- Simples -->
<div class="border border-zinc-200 bg-white p-5">
  <p class="text-sm font-semibold text-zinc-900 mb-1">Título</p>
  <p class="text-xs text-zinc-500 leading-relaxed">Conteúdo.</p>
</div>

<!-- Com header + footer -->
<div class="border border-zinc-200 bg-white overflow-hidden">
  <div class="px-5 py-3 border-b border-zinc-100
              flex items-center justify-between">
    <p class="text-sm font-semibold">Título</p>
  </div>
  <div class="p-5"></div>
  <div class="px-5 py-3 border-t border-zinc-100
              bg-zinc-50 flex items-center gap-2">
  </div>
</div>

<!-- Card clicável -->
<a href="#"
   class="block border border-zinc-200 bg-white p-5
          hover:border-zinc-400 hover:bg-zinc-50
          focus:outline-none focus:ring-2
          focus:ring-zinc-900 focus:ring-offset-2
          transition-colors group">
</a>
M05

Stat Card

Label + Valor + Tendência. Para dashboards e resumos de métricas.

Receita total

R$ 48.200

+12,4% vs. mês anterior

Novos usuários

1.284

-3,2% vs. mês anterior

Taxa de conversão

3,6%

Sem variação

Tickets abertos

47

12 urgentes
<div class="border border-zinc-200 bg-white p-5">
  <p class="text-xs font-medium text-zinc-500 mb-2">
    Label
  </p>
  <p class="text-2xl font-bold text-zinc-900 mb-1">
    Valor
  </p>
  <!-- Tendência positiva -->
  <p class="text-xs flex items-center gap-1
           text-green-600">
    <!-- SVG trend-up 14×14 -->
    +12,4%
  </p>
  <!-- Negativa → text-red-600 -->
  <!-- Ícone decorativo (opcional):
    bg-blue-50 + SVG text-blue-600 -->
</div>
M07

Pagination

Prev + Páginas + Next. Use <nav aria-label="Paginação"> e aria-current="page" na página atual.

Padrão

Simples (prev/next)

<nav aria-label="Paginação"
     class="flex items-center gap-1">
  <!-- Página normal -->
  <a href="#"
     class="inline-flex items-center justify-center
            w-9 h-9 text-sm text-zinc-500
            border border-zinc-200
            hover:border-zinc-400 hover:text-zinc-900
            transition-colors">1</a>

  <!-- Página atual -->
  <a aria-current="page"
     class="inline-flex items-center justify-center
            w-9 h-9 text-sm font-semibold
            bg-zinc-900 text-white
            border border-zinc-900">3</a>

  <!-- Elipse -->
  <span aria-hidden="true"
        class="w-9 h-9 inline-flex items-center
               justify-center text-sm text-zinc-400"></span>
</nav>
M09

Tabs

Navegação entre painéis de conteúdo relacionado. Use role="tablist/tab/tabpanel".

Underline

Conteúdo da aba ativa.

Pill / Filled

<!-- Underline -->
<div role="tablist"
     class="flex border-b border-zinc-200">
  <!-- Ativa -->
  <button role="tab" aria-selected="true"
          aria-controls="panel-1"
          class="px-4 py-2.5 text-sm font-medium
                 text-zinc-900
                 border-b-2 border-zinc-900 -mb-px">
    Tab
  </button>
  <!-- Inativa -->
  <button role="tab" aria-selected="false"
          class="... text-zinc-500
                 border-b-2 border-transparent -mb-px
                 hover:text-zinc-700 hover:border-zinc-300
                 transition-colors">
    Tab
  </button>
</div>
<div id="panel-1" role="tabpanel"
     aria-labelledby="tab-1" class="pt-4">
</div>
M10

Empty State

Ícone + Título + Descrição + CTA. Use para listas vazias, resultados nulos e estados de onboarding.

Nenhum projeto ainda

Crie seu primeiro projeto para começar a organizar seu trabalho.

Sem resultados

Nenhum item corresponde à sua busca.

<div class="border border-dashed border-zinc-300
           py-12 px-8 text-center">
  <div class="w-12 h-12 bg-zinc-100
             flex items-center justify-center
             mx-auto mb-4">
    <!-- SVG 24×24 text-zinc-400 -->
  </div>
  <h3 class="text-sm font-semibold text-zinc-900 mb-1">
    Nenhum item ainda
  </h3>
  <p class="text-xs text-zinc-500 mb-5 max-w-xs mx-auto">
    Crie o primeiro para começar.
  </p>
  <button type="button"
          class="inline-flex items-center gap-2
                 px-4 py-2 bg-zinc-900 text-white
                 text-sm font-medium hover:bg-zinc-700
                 focus:outline-none focus:ring-2
                 focus:ring-zinc-900 focus:ring-offset-2
                 transition-colors">
    Criar novo
  </button>
</div>
M11

Accordion

CSS-only via <details>/<summary> nativos. Acessível sem JS.

O que está incluído no plano Pro?

O plano Pro inclui acesso ilimitado, suporte prioritário, exportação em PDF e histórico de 12 meses.

Posso cancelar a qualquer momento?

Sim. Cancele a qualquer momento sem multa. O acesso permanece ativo até o fim do período pago.

Item desabilitado
<div class="border border-zinc-200 divide-y divide-zinc-200">
  <details class="group">
    <summary class="flex items-center justify-between
                     px-5 py-4 cursor-pointer list-none
                     hover:bg-zinc-50 transition-colors
                     select-none
                     focus-visible:outline-none
                     focus-visible:ring-2
                     focus-visible:ring-inset
                     focus-visible:ring-zinc-900">
      <span class="text-sm font-medium text-zinc-900">
        Pergunta
      </span>
      <!-- Chevron: group-open:rotate-180 -->
    </summary>
    <div class="px-5 pb-4 pt-1">
      <p class="text-sm text-zinc-600 leading-relaxed">
        Resposta.
      </p>
    </div>
  </details>
</div>
M12

Collapse

Painel único retrátil. Mesma base <details>, estética diferente do Accordion.

Filtros avançados 3 ativos
Ver detalhes técnicos

POST /api/v2/users
Content-Type: application/json
Authorization: Bearer {token}

<details class="group border border-zinc-200 bg-white"
         open>
  <summary class="flex items-center gap-3
                   px-4 py-3 cursor-pointer list-none
                   hover:bg-zinc-50 transition-colors
                   select-none">
    <!-- Chevron: group-open:rotate-90 -->
    <span class="text-sm font-semibold text-zinc-900">
      Título
    </span>
    <!-- Badge opcional: ml-auto -->
  </summary>
  <div class="border-t border-zinc-200 px-4 py-4">
    <!-- qualquer conteúdo -->
  </div>
</details>

<!-- Accordion = múltiplos itens em lista
  Collapse = painel único retrátil
  Base técnica: idêntica (<details>) -->
M13

List

Lista de itens estruturados. Use <ul>/<li> para listas e <ol> para ordenadas.

Simples com ícone

  • AB

    Ana Barros

    ana@empresa.com

    Ativo
  • CS

    Carlos Silva

    carlos@empresa.com

    Pendente
  • MF

    Maria Ferreira

    maria@empresa.com

    Inativo
<ul class="divide-y divide-zinc-100
          border border-zinc-200">
  <li class="flex items-center gap-3 px-4 py-3
            hover:bg-zinc-50 transition-colors">

    <!-- Avatar/ícone -->
    <div class="w-8 h-8 bg-zinc-800
             flex items-center justify-center
             flex-shrink-0">
      <span class="text-xs font-semibold text-white">AB</span>
    </div>

    <!-- Texto principal -->
    <div class="flex-1 min-w-0">
      <p class="text-sm font-medium
               text-zinc-900 truncate">Nome</p>
      <p class="text-xs text-zinc-500 truncate">
        secundário
      </p>
    </div>

    <!-- Badge/ação (flex-shrink-0) -->
  </li>
</ul>
M14

File Upload

Zona de drop com fallback para botão. CSS-only com peer trick no input.

Drag zone

Arquivo selecionado

relatorio-q3-2025.pdf

2,4 MB

Botão simples

<!-- Drag zone -->
<label for="upload"
       class="flex flex-col items-center
              justify-center gap-3
              border-2 border-dashed border-zinc-300
              bg-zinc-50 p-10 text-center
              cursor-pointer
              hover:border-zinc-500 hover:bg-zinc-100
              focus-within:border-zinc-900
              focus-within:bg-zinc-100
              transition-colors group">

  <!-- Input real, invisível -->
  <input id="upload" type="file"
         accept=".pdf,.png,.jpg"
         multiple
         class="sr-only" />

  <!-- Ícone -->
  <div class="w-10 h-10 bg-white border border-zinc-200
             flex items-center justify-center
             group-hover:border-zinc-400 transition-colors">
    <!-- SVG upload 20×20 -->
  </div>

  <!-- Texto -->
  <div>
    <p class="text-sm font-medium text-zinc-900">
      Arraste arquivos aqui
    </p>
    <p class="text-xs text-zinc-500 mt-0.5">
      ou <span class="text-zinc-900 font-medium
                       underline underline-offset-2">
        clique para selecionar
      </span>
    </p>
  </div>
  <p class="mono text-[10px] text-zinc-400">
    PDF, PNG, JPG · máx. 10 MB
  </p>
</label>

<!-- Arquivo selecionado -->
<div class="border border-zinc-200 bg-white p-3
           flex items-center gap-3">
  <!-- ícone de tipo -->
  <div class="flex-1 min-w-0">
    <p class="text-sm font-medium text-zinc-900 truncate">
      nome-do-arquivo.pdf
    </p>
    <p class="text-xs text-zinc-500">2,4 MB</p>
  </div>
  <!-- Botão remover -->
  <button type="button"
          aria-label="Remover arquivo"
          class="p-1 text-zinc-400
                 hover:text-red-600 hover:bg-red-50
                 transition-colors">
    <!-- SVG X 16×16 -->
  </button>
</div>
M15

Button Group

Botões agrupados com bordas fundidas. Use -mr-px + relative z-0 hover:z-10 para evitar sobreposição de bordas.

Botões agrupados

Input com prefixo

https://

Input + botão inline

Icon toolbar

<!-- Regra: -mr-px + z-0/z-10
  para fundir bordas sem sobreposição -->

<!-- Botões agrupados -->
<div class="flex">
  <button class="... border border-zinc-300
                 -mr-px relative z-0 hover:z-10">A</button>
  <button class="... border border-zinc-900
                 bg-zinc-900 text-white
                 -mr-px relative z-0">B</button>
  <button class="... border border-zinc-300
                 relative z-0 hover:z-10">C</button>
</div>

<!-- Input com prefixo -->
<div class="flex border border-zinc-300
           focus-within:ring-2
           focus-within:ring-zinc-900
           focus-within:border-transparent
           transition-colors">
  <span class="inline-flex items-center px-3
               bg-zinc-100 text-zinc-500 text-sm
               border-r border-zinc-300
               flex-shrink-0 select-none">
    https://
  </span>
  <input class="flex-1 px-3 py-2 text-sm
                bg-white focus:outline-none"/>
</div>

<!-- Toolbar de ícones -->
<div class="flex border border-zinc-200">
  <button type="button"
          aria-label="Ação"
          class="p-2.5 text-zinc-600
                 hover:bg-zinc-100 hover:text-zinc-900
                 border-r border-zinc-200
                 transition-colors">
    <!-- SVG 16×16 -->
  </button>
</div>
M16

Toast / Snackbar

Notificação transiente. Posicione via fixed bottom-4 right-4 na produção. Use role="alert" para urgentes, role="status" para informativos.

Item excluído.

<!-- Toast escuro (padrão) -->
<div role="alert"
     class="flex items-start gap-3 px-4 py-3
            bg-zinc-900 text-white shadow-lg
            fixed bottom-4 right-4 max-w-sm w-full
            z-50">
  <!-- SVG ícone -->
  <div class="flex-1 min-w-0">
    <p class="text-sm font-medium">Título</p>
    <p class="text-xs text-zinc-400 mt-0.5">Detalhe.</p>
  </div>
  <button aria-label="Fechar"
          class="text-zinc-400 hover:text-white
                 transition-colors">
    <!-- SVG X 16×16 -->
  </button>
</div>

<!-- Toast claro (variante erro) -->
<div class="... bg-white border border-red-200"></div>

<!-- Com ação inline -->
<button class="text-xs font-semibold text-zinc-300
                hover:text-white underline
                underline-offset-2">
  Desfazer
</button>

<!-- Posição: fixed bottom-4 right-4 z-50
  Stack múltiplos: flex flex-col gap-2 -->
M17

Combobox / Select Custom

Versão visual enriquecida do Select. Estrutura estática — conecte aria-expanded e toggle via JS da sua stack.

Preview aberto

  • 🇧🇷 Brasil
  • 🇦🇷 Argentina
  • 🇵🇹 Portugal
  • 🇺🇸 Estados Unidos

Com avatar

<!-- Trigger -->
<button type="button"
        aria-haspopup="listbox"
        aria-expanded="false"
        class="w-full flex items-center
               justify-between px-3 py-2 bg-white
               border border-zinc-300
               hover:border-zinc-400 text-sm
               focus:outline-none focus:ring-2
               focus:ring-zinc-900 transition-colors">
  <span class="flex items-center gap-2">
    <!-- ícone/avatar opcional -->
    Opção selecionada
  </span>
  <!-- SVG chevron -->
</button>

<!-- Dropdown -->
<ul role="listbox"
    class="absolute top-full left-0 right-0 mt-1
           bg-white border border-zinc-200
           shadow-lg z-10 py-1
           max-h-48 overflow-y-auto">
  <!-- Selecionado -->
  <li role="option" aria-selected="true"
      class="flex items-center gap-2.5 px-3 py-2
             text-sm text-zinc-900 bg-zinc-100
             cursor-pointer">
    Opção A
    <!-- SVG check ml-auto -->
  </li>
  <!-- Normal: text-zinc-700 hover:bg-zinc-100 -->
</ul>
M18

Rating

Avaliação por estrelas. Versão interativa via radio + peer CSS-only — sem JS.

Exibição (read-only)

4.0 (128 avaliações)

Tamanhos

sm
lg
<!-- Exibição read-only -->
<div class="flex gap-1"
     aria-label="Avaliação: 4 de 5 estrelas">
  <!-- Estrela preenchida -->
  <svg class="w-5 h-5 text-yellow-400"
       fill="currentColor"
       viewBox="0 0 20 20"
       aria-hidden="true">
    <path d="M9.049 2.927..."/>
  </svg>
  <!-- Estrela vazia: text-zinc-200 -->
</div>

<!-- Tamanhos:
  sm → w-3.5 h-3.5 gap-0.5
  md → w-5 h-5 gap-1
  lg → w-6 h-6 gap-1
-->
M19

Command Palette

Paleta de comandos tipo ⌘K. Estrutura estática: search + grupos + itens com atalhos. Conecte abertura/busca via JS.

Esc

Recentes

Navegação

↑↓ navegar selecionar Esc fechar
<div class="border border-zinc-200 bg-white
           shadow-xl overflow-hidden
           fixed top-24 left-1/2 -translate-x-1/2
           w-full max-w-md z-50"
     role="dialog" aria-modal="true"
     aria-label="Paleta de comandos">

  <!-- Search -->
  <div class="flex items-center
              border-b border-zinc-200 px-4">
    <!-- SVG search -->
    <input type="search"
           class="flex-1 px-3 py-3.5 text-sm
                  bg-white focus:outline-none" />
    <kbd class="mono text-[10px]
              border border-zinc-200
              bg-zinc-100 text-zinc-400 px-1.5 py-0.5">Esc</kbd>
  </div>

  <!-- Lista: max-h-72 overflow-y-auto py-2 -->
  <!-- Grupo: p mono text-[10px] uppercase -->
  <!-- Item ativo: bg-zinc-100 -->
  <button class="w-full flex items-center gap-3
                 px-4 py-2.5 text-sm text-zinc-700
                 hover:bg-zinc-100 transition-colors">
    <!-- Ícone 24×24 bg-zinc-100 -->
    <span class="flex-1 text-left">Label</span>
    <kbd class="mono text-[10px]
               border border-zinc-200
               bg-white text-zinc-400 px-1.5 py-0.5">⌘K</kbd>
  </button>

  <!-- Footer hints -->
  <div class="border-t border-zinc-100 px-4 py-2
              flex gap-3 mono text-[10px] text-zinc-400"></div>
</div>
M20

Inline Editable

Campo editável in-place. Texto vira input ao clicar. Use focus-within para controlar exibição dos controles.

Leitura (hover revela edição)

Nome do projeto

Estado de edição

Ghost input (borda no hover/focus)

Borda surge no hover/focus.

<!-- Texto + botão lápis (hover reveal) -->
<div class="group flex items-center gap-2">
  <span class="text-sm font-semibold text-zinc-900">
    Texto editável
  </span>
  <button aria-label="Editar"
          class="opacity-0 group-hover:opacity-100
                 focus:opacity-100 p-1 text-zinc-400
                 hover:text-zinc-700 transition-all">
    <!-- SVG pencil 14×14 -->
  </button>
</div>

<!-- Estado de edição -->
<div class="flex items-center gap-2">
  <input type="text"
         class="flex-1 px-2 py-1 text-sm font-semibold
                border border-zinc-900
                focus:outline-none focus:ring-2
                focus:ring-zinc-900 focus:border-transparent" />
  <!-- btn confirmar: bg-zinc-900 -->
  <!-- btn cancelar: hover:bg-zinc-100 -->
</div>

<!-- Ghost input -->
<div class="border border-transparent
           hover:border-zinc-300
           focus-within:border-zinc-900
           focus-within:ring-2
           focus-within:ring-zinc-900
           px-2 py-1 transition-all">
  <input type="text"
         class="w-full text-sm bg-transparent
                focus:outline-none" />
</div>
M21

Timeline

Sequência cronológica de eventos. Linha vertical + dot + conteúdo. Variantes com ícone e estado.

Deploy concluído

Sucesso

Versão 2.4.1 publicada em produção sem erros.

hoje, 14:32

Code review aprovado

PR #142 aprovado por Carlos Silva.

hoje, 11:15

Em revisão

Pendente

Aguardando aprovação de QA.

ontem, 16:00

Branch criada

feature/payment-refactor criada por Ana Barros.

25 mar 2026

<!-- Wrapper com linha vertical -->
<div class="relative pl-6">
  <div class="absolute left-[7px]
              top-2 bottom-2 w-px bg-zinc-200"></div>
  <div class="space-y-6">
    <div class="relative flex gap-4">
      <!-- Concluído: bg-zinc-900 + check -->
      <div class="absolute -left-6 top-1
               w-3.5 h-3.5 bg-zinc-900
               flex items-center justify-center">
        <!-- SVG check 8×8 text-white -->
      </div>
      <!-- Atual (ativo):
        -left-[23px] bg-white border-2
        border-zinc-900 ring-2 ring-offset-2 -->
      <!-- Futuro: bg-zinc-200 (sem ícone) -->
      <div class="flex-1 min-w-0">
        <p class="text-sm font-semibold text-zinc-900">Evento</p>
        <p class="text-xs text-zinc-500 leading-relaxed">Desc.</p>
        <p class="mono text-[10px] text-zinc-400 mt-1">hoje</p>
      </div>
    </div>
  </div>
</div>