Request Request Design System

Guia para LLMs

Regras que agentes de código (Claude, Cursor, Copilot, Windsurf) seguem ao escrever código em projectos Request. Copia o ficheiro para a raiz do teu projecto ou aponta o agente para a URL pública.

Como usar

  1. Opção 1 — copiar para o projecto.
    curl -o DESIGN.md https://design.request.pt/llms.txt
    Commita. O Claude Code e Cursor lêem automaticamente ficheiros na raiz.
  2. Opção 2 — referenciar no CLAUDE.md / .cursorrules:
    # Design system
    Segue sempre as regras em https://design.request.pt/llms.txt
  3. Opção 3 — MCP / rule import. Ferramentas com suporte a fetch de regras remotas podem indexar directamente https://design.request.pt/llms.txt.

DESIGN.md

Download
# Request Design System — Guia para LLMs

Regras vinculativas para qualquer agente (Claude, Cursor, Copilot, etc.) que escreva código em projectos Request. Copia este ficheiro para a raiz do teu projecto, ou referencia via URL: https://design.request.pt/llms.txt

## Princípios

1. **Light mode only.** Fundos slate-50/white, texto slate-700/800/900. Nunca dark mode.
2. **Rose é acção, nunca decoração.** Só usa `rose-*` em botões primários, links, estados activos. Nunca para fundos de página ou secções inteiras.
3. **Slate para tudo o resto.** Neutros slate-50/100/200/600/700/800/900. Sem violet, amber, cyan, pink, sky, indigo.
4. **Nunca hardcode valores.** Tudo passa pelos tokens do `@request-labs/tokens`. Zero `bg-[#...]`, zero `px-[15px]`, zero `style="color: #..."`.
5. **Hanken Grotesk em h1-h6. Inter em tudo o resto.** `font-display` para títulos, `font-sans` (default) para corpo.

## Setup obrigatório

Qualquer projecto Request novo instala e configura:

```bash
pnpm add @request-labs/tokens @request-labs/brand
```

**tailwind.config.js:**
```js
const tokens = require('@request-labs/tokens/tailwind');

module.exports = {
  content: ['./src/**/*.{html,js,ts,jsx,tsx,vue,blade.php,astro}'],
  theme: {
    extend: {
      colors: tokens.colors,
      spacing: tokens.spacing,
      fontFamily: tokens.fontFamily,
      fontSize: tokens.fontSize,
      fontWeight: tokens.fontWeight,
      lineHeight: tokens.lineHeight,
      borderRadius: tokens.borderRadius,
      boxShadow: tokens.boxShadow,
      transitionDuration: tokens.transitionDuration,
      transitionTimingFunction: tokens.transitionTimingFunction,
      zIndex: tokens.zIndex,
    },
  },
};
```

**CSS global (uma vez):**
```css
@import "@request-labs/brand/fonts.css";  /* Hanken Grotesk self-hosted */
@import "@request-labs/tokens/css";       /* custom properties */

body { font-family: theme('fontFamily.sans'); }
h1, h2, h3, h4, h5, h6 { font-family: theme('fontFamily.display'); }
```

**Head (favicons do brand):**
```html
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#1d1d1b" />
```

## Cores — o que usar quando

| Papel | Classe | Quando |
|---|---|---|
| Fundo de página | `bg-slate-50` | Sempre |
| Fundo de card / painel | `bg-white` + `border-slate-200` | Sempre |
| Header / footer de marca | `bg-brand-dark` (#1d1d1b) + `text-white` | Só a shell da app |
| Acção primária | `bg-rose-600 hover:bg-rose-700 text-white` | Botões submit, CTAs, links principais |
| Acção secundária | `border-slate-300 bg-white hover:bg-slate-50 text-slate-700` | Cancel, voltar |
| Acção destructiva | `bg-red-600 hover:bg-red-700 text-white` | Delete, logout |
| Texto principal | `text-slate-800` ou `text-slate-900` | Default |
| Texto secundário | `text-slate-600` | Descrições, meta |
| Texto terciário / placeholder | `text-slate-400` ou `text-slate-500` | Hints, empty states |
| Sucesso | `bg-emerald-50 text-emerald-800 border-emerald-200` | Alerts, badges de status "ready/done" |
| Erro | `bg-red-50 text-red-800 border-red-200` | Alerts, validation |
| Aviso | `bg-amber-50 text-amber-800 border-amber-200` | Apenas em alerts. Não noutros sítios. |
| Info | `bg-sky-50 text-sky-800 border-sky-200` | Apenas em alerts. Não noutros sítios. |

**NÃO FAZER:**
- `bg-gradient-to-r from-violet-500 to-pink-500` — nunca gradientes coloridos.
- `bg-blue-500` para primária — a primária é **rose**.
- Mais do que 2 cores não-neutras no mesmo ecrã.

## Tipografia

```html
<h1 class="text-4xl font-bold text-slate-900">...</h1>   <!-- Hanken Grotesk -->
<h2 class="text-2xl font-semibold text-slate-900">...</h2>
<p class="text-base text-slate-700">...</p>               <!-- Inter -->
<small class="text-sm text-slate-600">...</small>
```

- **Tamanho mínimo de input:** `text-base` (16px). Nunca `text-sm` em inputs (mobile zoom).
- **Line-height:** default `leading-normal` (1.5). Títulos podem ir `leading-tight`.
- **Pesos:** 400 (regular), 500 (medium), 600 (semibold), 700 (bold). Nada de 300 ou 900.

## Spacing (escala 4px)

Sempre da escala. `1` = 4px, `2` = 8px, `3` = 12px, `4` = 16px, `6` = 24px, `8` = 32px, `12` = 48px, `16` = 64px.

Nunca `p-[13px]`, nunca `gap-[7px]`. Se precisares de algo que não existe, usa o valor mais próximo ou adiciona aos tokens.

## Border radius

- `rounded` (4px) ou `rounded-md` (6px) — default para inputs, cards pequenos
- `rounded-lg` (8px) — cards, painéis
- `rounded-xl` (12px) — hero blocks
- `rounded-full` — avatars, badges circulares
- **Sem `rounded-none` e sem `rounded-3xl`+**

## Shadows

- `shadow-sm` — cards neutros
- `shadow-md` — hover state, dropdowns
- `shadow-lg` — modals, popovers
- Sem sombras coloridas, sem sombras dramáticas.

## Motion

| Token | Quando |
|---|---|
| `duration-instant` (75ms) | Hover states, focus rings |
| `duration-fast` (150ms) | Micro-interacções, botões, toggles |
| `duration-base` (250ms) | Default. Dropdowns, modais a abrir |
| `duration-slow` (400ms) | Transições de página, grandes layouts |

Easing default: `ease-out` para entrada, `ease-in` para saída, `ease-in-out` quando ambos.

```html
<button class="transition-colors duration-fast ease-out ...">
```

## Z-index

Usa sempre os tokens, nunca números arbitrários:

```
z-dropdown  (1000) — menus, autocompletes
z-sticky    (1100) — headers sticky
z-overlay   (1200) — backdrop de modais
z-modal     (1300) — modais
z-popover   (1400) — popovers
z-toast     (1500) — notifications
z-tooltip   (1600) — sempre no topo
```

## Ícones

Sempre **Lucide** (`lucide-react`, `lucide-vue-next`, `lucide-static`, `blade-lucide-icons`).

- Tamanhos: `w-4 h-4` (small), `w-5 h-5` (default), `w-6 h-6` (large)
- Stroke 2 (default do Lucide). Não mudar.
- `aria-hidden="true"` quando decorativo ao lado de texto; `aria-label="..."` quando sozinho num botão.
- Cor via `text-*` (o SVG é `currentColor`).

Nunca misturar bibliotecas (Lucide + Heroicons + FontAwesome no mesmo projecto).

## Componentes

Shadcn-style: **copy/paste**, não lib. Vai a https://design.request.pt/components, copia o código, adapta à tua stack. Cada projecto é dono do próprio código.

**Forma canónica de um botão (referência):**
```html
<button class="inline-flex items-center gap-2 px-4 py-2 bg-rose-600 text-white font-medium rounded-md hover:bg-rose-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-500 focus-visible:ring-offset-2 transition-colors duration-fast disabled:opacity-50 disabled:cursor-not-allowed">
  Label
</button>
```

**Forma canónica de um input:**
```html
<input class="w-full px-3 py-2 text-base border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-rose-500 focus:border-rose-500 disabled:bg-slate-50 disabled:cursor-not-allowed" />
```

## Acessibilidade (não negociável)

- **Contraste AA** mínimo em todo o texto. `text-slate-400` só em fundos brancos.
- **Focus visível** em tudo que é interactivo (`focus-visible:ring-2 focus-visible:ring-rose-500 focus-visible:ring-offset-2`).
- **Touch target** mínimo 44x44px em mobile. Ícones sozinhos precisam de padding.
- **Labels em inputs** via `<label>` ou `aria-label`. Placeholder não substitui label.
- **Skip link** em layouts com muita nav.
- **Idioma** declarado no `<html lang="pt-PT">`.

## Content / Copy (PT-PT)

- Tratamento: **tu** (informal, chill). Nunca "você".
- Botões: verbo no imperativo (Guardar, Cancelar, Enviar). Nunca "Click aqui".
- Erros: explicar o que correu mal + o que fazer (não "Ocorreu um erro").
- Datas: `18 de Abril de 2026` (longo) ou `18/04/2026` (curto). ISO só em APIs.
- Números: separador milhares `.`, decimal `,`. `€1.234,56`.

## Checklist antes de commitar

- [ ] Zero classes com valores arbitrários (`[...]`)
- [ ] Zero `style="..."` inline para cores/spacing
- [ ] Nenhuma cor fora do palette (rose, slate, emerald/red/amber/sky para semantic)
- [ ] Títulos em `font-display`, corpo em default (`font-sans`)
- [ ] Focus visible em tudo interactivo
- [ ] Inputs com `text-base`
- [ ] Ícones da Lucide, com `aria-*` correcto
- [ ] Touch targets >= 44x44 em mobile
- [ ] `lang="pt-PT"` no HTML

## Recursos

- Docs completas: https://design.request.pt
- Tokens: https://design.request.pt/tokens
- Componentes (copy/paste): https://design.request.pt/components
- Brand (logos, fontes): https://design.request.pt/brand
- Ícones: https://design.request.pt/icons
- npm: `@request-labs/tokens`, `@request-labs/brand`