Pular para o conteúdo

4. Design System

Este documento define o sistema de design para soluções web desenvolvidas com Astro 5, estabelecendo padrões visuais consistentes e componentes reutilizáveis.

Tokens de Design

Os tokens de design são valores fundamentais que definem a aparência visual da aplicação. Eles são implementados através da configuração do Tailwind CSS.

Cores

// tailwind.config.js (trecho)
module.exports = {
theme: {
extend: {
colors: {
// Cores primárias
primary: {
50: '#f0f7ff',
100: '#e0effe',
200: '#bae0fd',
300: '#90cafc',
400: '#5eaef8',
500: '#3b91f2',
600: '#2574e7',
700: '#1d5cd4',
800: '#1e4bab',
900: '#1e4285',
950: '#162a54',
},
// Cores secundárias
secondary: {
50: '#f5f7fa',
100: '#ebeef3',
200: '#d2dae5',
300: '#acbcce',
400: '#8098b2',
500: '#5f7b98',
600: '#4a637e',
700: '#3d5267',
800: '#354557',
900: '#2f3c4a',
950: '#1e2631',
},
// Cores de feedback
success: {
50: '#f0fdf4',
500: '#22c55e',
700: '#15803d',
},
warning: {
50: '#fffbeb',
500: '#f59e0b',
700: '#b45309',
},
error: {
50: '#fef2f2',
500: '#ef4444',
700: '#b91c1c',
},
info: {
50: '#f0f9ff',
500: '#0ea5e9',
700: '#0369a1',
},
// Tons neutros
neutral: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
400: '#9ca3af',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
950: '#030712',
},
},
},
},
};

Uso de Cores

  • Cores primárias: Usadas para elementos principais, CTA e branding
  • Cores secundárias: Usadas para elementos complementares e acentos
  • Cores de feedback: Usadas para mensagens de sucesso, erro, alerta e informação
  • Tons neutros: Usados para texto, fundos e elementos estruturais

Tipografia

// tailwind.config.js (trecho)
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
display: ['Lexend', ...defaultTheme.fontFamily.sans],
mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
},
fontSize: {
xs: ['0.75rem', { lineHeight: '1rem' }],
sm: ['0.875rem', { lineHeight: '1.25rem' }],
base: ['1rem', { lineHeight: '1.5rem' }],
lg: ['1.125rem', { lineHeight: '1.75rem' }],
xl: ['1.25rem', { lineHeight: '1.75rem' }],
'2xl': ['1.5rem', { lineHeight: '2rem' }],
'3xl': ['1.875rem', { lineHeight: '2.25rem' }],
'4xl': ['2.25rem', { lineHeight: '2.5rem' }],
'5xl': ['3rem', { lineHeight: '1' }],
'6xl': ['3.75rem', { lineHeight: '1' }],
},
fontWeight: {
thin: '100',
extralight: '200',
light: '300',
normal: '400',
medium: '500',
semibold: '600',
bold: '700',
extrabold: '800',
black: '900',
},
},
},
};

Hierarquia Tipográfica

ElementoClasseUso
Heading 1text-4xl font-bold text-neutral-900Títulos principais de página
Heading 2text-3xl font-bold text-neutral-900Seções principais
Heading 3text-2xl font-semibold text-neutral-800Subseções
Heading 4text-xl font-semibold text-neutral-800Grupos de conteúdo
Heading 5text-lg font-medium text-neutral-800Títulos menores
Heading 6text-base font-medium text-neutral-800Subtítulos menores
Bodytext-base text-neutral-700Texto principal
Smalltext-sm text-neutral-600Texto secundário, legendas
XSmalltext-xs text-neutral-500Metadados, notas de rodapé

Espaçamento

O sistema de espaçamento segue uma escala consistente, implementada através das classes de espaçamento do Tailwind:

// tailwind.config.js (trecho)
module.exports = {
theme: {
extend: {
spacing: {
// Valores personalizados adicionais, se necessário
'4.5': '1.125rem', // 18px
'13': '3.25rem', // 52px
'15': '3.75rem', // 60px
},
},
},
};

Escala de Espaçamento

TamanhoValorUso
00pxSem espaçamento
px1pxBordas finas
0.50.125rem (2px)Espaçamento mínimo
10.25rem (4px)Espaçamento muito pequeno
20.5rem (8px)Espaçamento pequeno
30.75rem (12px)Espaçamento médio-pequeno
41rem (16px)Espaçamento base
61.5rem (24px)Espaçamento médio
82rem (32px)Espaçamento grande
123rem (48px)Espaçamento muito grande
164rem (64px)Espaçamento extra grande
205rem (80px)Espaçamento 2x extra grande
246rem (96px)Espaçamento 3x extra grande

Componentes Base

Botões

Os botões seguem uma hierarquia visual clara para indicar importância e função:

components/ui/Button.astro
---
const {
variant = 'primary',
size = 'md',
class: className,
disabled = false,
type = 'button',
...props
} = Astro.props;
const variantClasses = {
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
secondary: 'bg-secondary-100 text-secondary-700 hover:bg-secondary-200 focus:ring-secondary-500',
outline: 'bg-white border border-neutral-300 text-neutral-700 hover:bg-neutral-50 focus:ring-primary-500',
ghost: 'text-neutral-700 hover:bg-neutral-100 focus:ring-neutral-500',
danger: 'bg-error-600 text-white hover:bg-error-700 focus:ring-error-500',
};
const sizeClasses = {
sm: 'px-2.5 py-1.5 text-xs',
md: 'px-4 py-2 text-sm',
lg: 'px-5 py-2.5 text-base',
xl: 'px-6 py-3 text-base',
};
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed';
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className || ''}`;
---
<button
type={type}
disabled={disabled}
class={classes}
{...props}
>
<slot />
</button>

Uso dos Botões

<Button>Botão Padrão</Button>
<Button variant="secondary">Botão Secundário</Button>
<Button variant="outline">Botão Outline</Button>
<Button variant="ghost">Botão Ghost</Button>
<Button variant="danger">Botão de Perigo</Button>
<Button size="sm">Pequeno</Button>
<Button size="md">Médio</Button>
<Button size="lg">Grande</Button>
<Button size="xl">Extra Grande</Button>
<Button disabled>Botão Desabilitado</Button>
<Button>
<Icon name="plus" class="w-4 h-4 mr-2" />
Com Ícone
</Button>

Cards

Cards são contêineres versáteis para agrupar conteúdo relacionado:

components/ui/Card.astro
---
const {
padding = 'normal',
shadow = 'md',
border = false,
hover = false,
class: className,
...props
} = Astro.props;
const paddingClasses = {
none: '',
small: 'p-3',
normal: 'p-4',
large: 'p-6',
};
const shadowClasses = {
none: '',
sm: 'shadow-sm',
md: 'shadow',
lg: 'shadow-lg',
};
const borderClass = border ? 'border border-neutral-200' : '';
const hoverClass = hover ? 'transition-all hover:shadow-lg' : '';
const classes = `bg-white rounded-lg ${paddingClasses[padding]} ${shadowClasses[shadow]} ${borderClass} ${hoverClass} ${className || ''}`;
---
<div class={classes} {...props}>
<slot />
</div>

Uso dos Cards

<Card>
<h3 class="text-xl font-semibold mb-2">Título do Card</h3>
<p>Conteúdo do card com texto informativo.</p>
</Card>
<Card padding="large" shadow="lg" border={true} hover={true}>
<h3 class="text-xl font-semibold mb-2">Card com Opções</h3>
<p>Card com padding grande, sombra grande, borda e efeito hover.</p>
</Card>
<Card padding="none" class="overflow-hidden">
<img src="/images/example.jpg" alt="Exemplo" class="w-full h-48 object-cover" />
<div class="p-4">
<h3 class="text-xl font-semibold mb-2">Card com Imagem</h3>
<p>Card com imagem e conteúdo.</p>
</div>
</Card>

Formulários

Componentes de formulário com estilo consistente e acessível:

components/ui/Input.astro
---
const {
id,
label,
type = 'text',
placeholder,
required = false,
disabled = false,
error,
helpText,
class: className,
...props
} = Astro.props;
const baseClasses = 'block w-full rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm';
const stateClasses = error
? 'border-error-300 text-error-900 placeholder-error-300'
: 'border-neutral-300 placeholder-neutral-400';
const disabledClasses = disabled ? 'bg-neutral-100 cursor-not-allowed' : '';
const classes = `${baseClasses} ${stateClasses} ${disabledClasses} ${className || ''}`;
---
<div class="mb-4">
{label && (
<label for={id} class="block text-sm font-medium text-neutral-700 mb-1">
{label}
{required && <span class="text-error-500">*</span>}
</label>
)}
<input
id={id}
type={type}
placeholder={placeholder}
required={required}
disabled={disabled}
class={classes}
aria-invalid={error ? 'true' : 'false'}
aria-describedby={error ? `${id}-error` : helpText ? `${id}-description` : undefined}
{...props}
/>
{error && (
<p id={`${id}-error`} class="mt-1 text-sm text-error-600">
{error}
</p>
)}
{helpText && !error && (
<p id={`${id}-description`} class="mt-1 text-sm text-neutral-500">
{helpText}
</p>
)}
</div>

Outros Componentes de Formulário

Seguindo o mesmo padrão, crie componentes para:

  • Select
  • Checkbox
  • Radio
  • Textarea
  • Toggle
  • DatePicker
  • FileUpload

Sistema de Grid e Layout

Container

components/layout/Container.astro
---
const {
size = 'default',
class: className,
...props
} = Astro.props;
const sizeClasses = {
sm: 'max-w-3xl',
default: 'max-w-5xl',
lg: 'max-w-7xl',
xl: 'max-w-[90rem]',
full: 'max-w-none',
};
const classes = `mx-auto px-4 sm:px-6 lg:px-8 ${sizeClasses[size]} ${className || ''}`;
---
<div class={classes} {...props}>
<slot />
</div>

Grid

Utilize as classes de grid do Tailwind:

<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>Item 1</Card>
<Card>Item 2</Card>
<Card>Item 3</Card>
<!-- ... -->
</div>

Seções

components/layout/Section.astro
---
const {
spacing = 'default',
background = 'white',
class: className,
...props
} = Astro.props;
const spacingClasses = {
small: 'py-4 md:py-6',
default: 'py-8 md:py-12',
large: 'py-12 md:py-16',
xl: 'py-16 md:py-24',
};
const backgroundClasses = {
white: 'bg-white',
light: 'bg-neutral-50',
dark: 'bg-neutral-900 text-white',
primary: 'bg-primary-50',
secondary: 'bg-secondary-50',
};
const classes = `${spacingClasses[spacing]} ${backgroundClasses[background]} ${className || ''}`;
---
<section class={classes} {...props}>
<slot />
</section>

Responsividade e Acessibilidade

Breakpoints

Seguimos os breakpoints padrão do Tailwind:

BreakpointTamanhoDescrição
sm640pxDispositivos pequenos (smartphones em paisagem)
md768pxTablets
lg1024pxLaptops/Desktops pequenos
xl1280pxDesktops
2xl1536pxTelas grandes

Diretrizes de Acessibilidade

  1. Contraste de Cores

    • Texto: Relação de contraste mínima de 4.5:1
    • Elementos grandes e cabeçalhos: Relação de contraste mínima de 3:1
  2. Foco Visível

    • Todos os elementos interativos devem ter um estado de foco visível
    • Use focus:ring-2 focus:ring-offset-2 focus:ring-primary-500
  3. Semântica HTML

    • Use tags HTML semânticas (<button>, <nav>, <main>, etc.)
    • Estruture o conteúdo com hierarquia adequada de cabeçalhos
  4. Atributos ARIA

    • Use atributos ARIA apenas quando necessário
    • Exemplos: aria-label, aria-expanded, aria-controls
  5. Tamanho de Toque

    • Elementos interativos devem ter pelo menos 44x44px em dispositivos móveis

Implementação no Tailwind

Configure o arquivo tailwind.config.js para implementar o design system:

tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme');
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
// Cores do design system (conforme definido acima)
},
fontFamily: {
// Famílias de fontes (conforme definido acima)
},
fontSize: {
// Tamanhos de fonte (conforme definido acima)
},
spacing: {
// Espaçamentos personalizados (conforme definido acima)
},
borderRadius: {
'sm': '0.125rem',
DEFAULT: '0.25rem',
'md': '0.375rem',
'lg': '0.5rem',
'xl': '0.75rem',
'2xl': '1rem',
'3xl': '1.5rem',
'full': '9999px',
},
boxShadow: {
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
'2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
none: 'none',
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
};

Conclusão

Este design system fornece uma base sólida para criar interfaces consistentes, acessíveis e visualmente atraentes. Ele deve evoluir com o projeto, mantendo a documentação atualizada conforme novos componentes e padrões são adicionados.

Os próximos documentos detalharão outros aspectos do desenvolvimento:


Última atualização: 21 de março de 2025