Request Request Design System

Componentes

Exemplos shadcn-style. Copia o código, cola no teu projecto, adapta. Os componentes vivem em cada projecto, não numa lib partilhada.

Button

<button class="inline-flex items-center justify-center gap-2 px-4 py-2 rounded-md
  bg-rose-600 text-white font-medium text-sm
  hover:bg-rose-700 transition-colors duration-fast
  focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 focus-visible:ring-offset-2
  disabled:opacity-50 disabled:cursor-not-allowed">
  Acção primária
</button>

Input

<input type="text" placeholder="Escreve aqui"
  class="w-full px-3 py-2 rounded-md border border-slate-300
  bg-white text-slate-800 text-base
  placeholder:text-slate-400
  focus:outline-none focus:ring-2 focus:ring-rose-500 focus:border-rose-500
  disabled:bg-slate-50 disabled:cursor-not-allowed" />

Card

Título do card

Descrição curta a explicar o conteúdo. Slate-600 para secundário.

<div class="bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden">
  <div class="p-6">
    <h3 class="text-lg font-semibold text-slate-900 mb-1">Título do card</h3>
    <p class="text-sm text-slate-600">Descrição curta. Slate-600 para secundário.</p>
  </div>
  <div class="px-6 py-4 bg-slate-50 border-t border-slate-200 flex justify-end gap-2">
    <button class="...">Acção</button>
  </div>
</div>

Badge

Neutral Primary Sucesso Erro Aviso Info
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-emerald-100 text-emerald-800">
  Activo
</span>

Alert

Guardado
As alterações foram gravadas.
Erro
Não foi possível gravar. Tenta novamente.
Atenção
Esta acção demora alguns minutos.
Nota
Podes editar em qualquer altura.
<div class="flex gap-3 p-4 rounded-md bg-rose-50 border border-rose-200 text-rose-900">
  <svg class="w-5 h-5 flex-shrink-0" ...><!-- lucide alert-circle --></svg>
  <div>
    <div class="font-semibold text-sm">Algo correu mal</div>
    <div class="text-sm text-rose-800">Explica o que aconteceu e o que fazer a seguir.</div>
  </div>
</div>

Checkbox

<label class="flex items-start gap-2 cursor-pointer">
  <input type="checkbox" class="mt-0.5 w-4 h-4 rounded border-slate-300 text-rose-600 focus:ring-rose-500" />
  <span class="text-sm text-slate-700">Aceito os termos</span>
</label>

Radio

<label class="flex items-center gap-2 cursor-pointer">
  <input type="radio" name="opcao" class="w-4 h-4 border-slate-300 text-rose-600 focus:ring-rose-500" />
  <span class="text-sm text-slate-700">Opção A</span>
</label>

Select

<select class="w-full px-3 py-2 pr-8 rounded-md border border-slate-300
  bg-white text-slate-800 text-base
  focus:outline-none focus:ring-2 focus:ring-rose-500 focus:border-rose-500">
  <option>Escolhe uma opção</option>
  <option>Opção A</option>
</select>

Tabs

Conteúdo do separador activo.
<div class="border-b border-slate-200">
  <nav class="flex gap-6 -mb-px">
    <button class="px-1 py-3 text-sm font-medium border-b-2 border-rose-600 text-rose-600">Activo</button>
    <button class="px-1 py-3 text-sm font-medium border-b-2 border-transparent text-slate-600 hover:text-slate-900 hover:border-slate-300">Inactivo</button>
  </nav>
</div>

Tooltip

Apenas CSS. Para tooltips com trigger em click / keyboard, usa Radix / Headless UI.

Texto do tooltip
<div class="relative group inline-block">
  <button class="text-slate-600 hover:text-slate-900">?</button>
  <div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 rounded
       bg-slate-800 text-white text-xs whitespace-nowrap opacity-0 group-hover:opacity-100
       pointer-events-none transition-opacity duration-fast z-tooltip">
    Texto do tooltip
  </div>
</div>

Form pattern

Composição canónica: label em cima, asterisco para required, helper em baixo, erro em vermelho com aria-describedby.

Como aparece no recibo.

Email inválido

<form class="space-y-5 max-w-md">
  <div>
    <label for="name" class="block text-sm font-medium text-slate-700 mb-1">
      Nome <span class="text-rose-600">*</span>
    </label>
    <input id="name" required
      class="w-full px-3 py-2 rounded-md border border-slate-300 text-base
             focus:outline-none focus:ring-2 focus:ring-rose-500 focus:border-rose-500" />
    <p class="mt-1 text-xs text-slate-500">Como aparece no recibo.</p>
  </div>

  <div>
    <label for="email" class="block text-sm font-medium text-slate-700 mb-1">Email</label>
    <input id="email" type="email" aria-invalid="true" aria-describedby="email-err"
      class="w-full px-3 py-2 rounded-md border border-red-300 text-base
             focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-red-500" />
    <p id="email-err" class="mt-1 text-xs text-red-600">Email inválido</p>
  </div>

  <button type="submit" class="px-4 py-2 bg-rose-600 text-white rounded-md hover:bg-rose-700">
    Submeter
  </button>
</form>

Empty state

Para listas/tabelas vazias, 404 de conteúdo, ou search sem resultados. Sempre com CTA primária + descrição curta.

Sem facturas ainda

Quando criares a tua primeira factura, aparece aqui. Leva menos de um minuto.

<div class="max-w-md mx-auto text-center py-8">
  <div class="w-14 h-14 mx-auto mb-4 rounded-full bg-rose-50 text-rose-600 flex items-center justify-center"><!-- icon --></div>
  <h3 class="text-lg font-semibold text-slate-900 mb-1">Título</h3>
  <p class="text-sm text-slate-600 mb-5">Descrição curta a orientar o próximo passo.</p>
  <button class="inline-flex items-center gap-2 px-4 py-2 bg-rose-600 text-white font-medium text-sm rounded-md hover:bg-rose-700">CTA primária</button>
</div>

Semantic colors

Para estados (success/warning/error/info). Cada um tem 3 tons: subtle (bg suave), base (sólido/ícone), text (texto sobre subtle).

Success
bg-success-subtle / text-success-text
Warning
bg-warning-subtle / text-warning-text
Error
bg-error-subtle / text-error-text
Info
bg-info-subtle / text-info-text
<span class="bg-success-subtle text-success-text px-2 py-0.5 rounded-full">Pago</span>
<span class="bg-warning-subtle text-warning-text px-2 py-0.5 rounded-full">A expirar</span>
<span class="bg-error-subtle text-error-text px-2 py-0.5 rounded-full">Em atraso</span>
<span class="bg-info-subtle text-info-text px-2 py-0.5 rounded-full">Rascunho</span>