- Setup NestJS with TypeScript, ConfigModule, JWT authentication - Implemented Auth Module with signup, login, logout endpoints - Created DTOs with validation (SignupDto, LoginDto) - JWT Strategy with Passport integration for token validation - JwtAuthGuard for route protection with Bearer tokens - CurrentUser decorator for dependency injection - Supabase integration for user management and auth - Complete API documentation (API.md) with all endpoints - Design System for Web (Next.js + Tailwind) and Mobile (Flutter) - Comprehensive project documentation and roadmap - Environment configuration with Joi schema validation - Ready for Tasks Module and RLS implementation
19 KiB
19 KiB
🎨 TASK MANAGER - Design System & Guia de Interface
🌈 Paleta de Cores
Cores Principais
- Primary (Azul):
#2563EB- Ações principais, links, seleção - Primary Dark:
#1D4ED8- Estados hover/ativo - Primary Light:
#DBEAFE- Backgrounds, estados desabilitados - White:
#FFFFFF- Backgrounds principal - Black:
#000000- Texto principal, contrastes fortes - Gray 50:
#F9FAFB- Backgrounds secundários - Gray 100:
#F3F4F6- Borders leves - Gray 200:
#E5E7EB- Borders padrão - Gray 400:
#9CA3AF- Texto secundário, placeholders - Gray 600:
#4B5563- Texto terciário
Cores de Status
- Success:
#10B981- Tarefas concluídas - Warning:
#F59E0B- Alertas - Error:
#EF4444- Erros, deletar - Info:
#06B6D4- Informações
🔤 Tipografia (Google Fonts)
Fonte Principal: Inter
- Link: https://fonts.google.com/specimen/Inter
- Uso: Corpo de texto, UI, labels
Fonte Secundária: Poppins
- Link: https://fonts.google.com/specimen/Poppins
- Uso: Títulos, headings, destaque
Escala Tipográfica
| Nome | Tamanho | Weight | Line Height | Letra-espaçamento | Uso |
|---|---|---|---|---|---|
| H1 | 32px | 700 (Bold) | 40px | -0.5px | Títulos principais |
| H2 | 24px | 700 (Bold) | 32px | -0.3px | Subtítulos |
| H3 | 18px | 600 (SemiBold) | 28px | -0.2px | Seções |
| Body Large | 16px | 400 (Regular) | 24px | 0px | Texto principal |
| Body Normal | 14px | 400 (Regular) | 22px | 0px | Descrições |
| Body Small | 12px | 400 (Regular) | 18px | 0.2px | Ajuda, captions |
| Label | 12px | 600 (SemiBold) | 18px | 0.5px | Labels de campos |
| Button | 14px | 600 (SemiBold) | 20px | 0.5px | Texto de botões |
Importação no CSS/Next.js
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Poppins:wght@600;700&display=swap');
:root {
--font-inter: 'Inter', sans-serif;
--font-poppins: 'Poppins', sans-serif;
}
🎯 Espaçamento (8px Base)
Seguindo o padrão Material Design + Tailwind, usando múltiplos de 8px:
| Token | Valor | Uso |
|---|---|---|
| xs | 4px | Micro-espaçamentos |
| sm | 8px | Padding pequeno |
| md | 12px | Padding padrão |
| lg | 16px | Espaçamento interno |
| xl | 24px | Separação entre elementos |
| 2xl | 32px | Separação entre seções |
| 3xl | 48px | Grandes divisores |
| 4xl | 64px | Muito grandes |
Exemplos de Aplicação
- Padding de Botão: 12px (vertical) × 16px (horizontal)
- Padding de Card: 24px
- Gap entre inputs: 16px
- Margin entre seções: 32px
🎨 Componentes - Design System
1️⃣ BOTÕES
Botão Primary (Padrão)
Tamanho: 48px altura
Padding: 12px vertical × 16px horizontal
Background: #2563EB
Text: White, 14px Bold
Border Radius: 8px
Font Weight: 600
Estados:
- Default: Background #2563EB
- Hover: Background #1D4ED8, Sombra leve
- Active: Background #1D4ED8, sem sombra
- Disabled: Background #E5E7EB, Text #9CA3AF, sem cursor
Botão Secondary
Background: #F3F4F6
Text: #000000, 14px Bold
Border: 1px #E5E7EB
Border Radius: 8px
Botão Tertiary (Text only)
Background: transparent
Text: #2563EB, 14px Bold
Padding: 12px 16px
Botão Danger
Background: #EF4444
Text: White, 14px Bold
Sombra: 0px 4px 12px rgba(239, 68, 68, 0.2)
Botão com Ícone
Spacing entre ícone e texto: 8px
Ícone tamanho: 20px × 20px
2️⃣ CAMPOS DE ENTRADA (Inputs)
Input Padrão
Altura: 44px
Padding: 12px 16px
Border: 1px #E5E7EB
Border Radius: 8px
Font: Inter 14px
Background: #FFFFFF
Estados:
- Default: Border #E5E7EB, Background #FFFFFF
- Focus: Border #2563EB (2px), Background #FFFFFF, Box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1)
- Filled: Border #2563EB
- Error: Border #EF4444, Background #FEF2F2
- Disabled: Border #E5E7EB, Background #F9FAFB, Text #9CA3AF
Placeholder
Color: #9CA3AF
Font Style: Regular
Label
Font: Poppins 12px SemiBold
Color: #000000
Margin Bottom: 8px
Required indicator (*): Color #EF4444
Help Text
Font: Inter 12px Regular
Color: #6B7280
Margin Top: 4px
Error Message
Font: Inter 12px Regular
Color: #EF4444
Icon: 16px × 16px (⚠️)
Margin Top: 4px
3️⃣ CARDS
Background: #FFFFFF
Border: 1px #E5E7EB
Border Radius: 12px
Padding: 24px
Box Shadow: 0px 1px 3px rgba(0, 0, 0, 0.1)
Hover (Interactive):
Box Shadow: 0px 4px 12px rgba(0, 0, 0, 0.08)
Transition: 150ms
Task Card
Padding: 16px
Border Radius: 12px
Background: #FFFFFF
Border: 1px #E5E7EB
Estrutura:
├── Checkbox (20px × 20px)
├── Conteúdo (Flex: 1)
│ ├── Título: 14px Bold, #000000
│ └── Descrição: 13px Regular, #6B7280
└── Menu/Delete: 24px × 24px (Right)
Spacing: 12px entre elementos
4️⃣ CHECKBOX & RADIO
Checkbox
Tamanho: 20px × 20px
Border: 2px #E5E7EB
Border Radius: 4px
Background: #FFFFFF
Padding: 2px (interno)
Estados:
- Unchecked: Border #E5E7EB, Background #FFFFFF
- Checked: Border #2563EB, Background #2563EB, Ícone ✓ branco
- Focus: Box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1)
- Disabled: Border #E5E7EB, Background #F3F4F6
5️⃣ TABS
Height: 48px
Font: Inter 14px SemiBold
Color: #6B7280 (inactive), #2563EB (active)
Border Bottom: 2px #2563EB (active)
Padding: 16px horizontal
6️⃣ MODALS/DIALOGS
Background: #FFFFFF
Border Radius: 16px
Padding: 32px
Width: 90% (mobile), 520px (desktop)
Max Height: 90vh
Box Shadow: 0px 20px 25px rgba(0, 0, 0, 0.15)
Overlay: rgba(0, 0, 0, 0.5)
Estrutura:
├── Header
│ ├── Título: H2
│ └── Close button: 24px (top right)
├── Content
│ └── Padding: 24px 0
└── Footer (Actions)
├── Button Secondary (Cancel): Left
└── Button Primary (Confirm): Right
└── Spacing: 12px entre botões
7️⃣ ÍCONES (Google Icons)
Link: https://fonts.google.com/icons
Tamanhos Padrão:
- Small: 16px - Lado de inputs, help text
- Normal: 20px - Ícones em botões, menus
- Large: 24px - Ícones principais, headers
- XL: 32px - Ícones de estado vazio, sucesso
Cor Principal: #2563EB Cor Secundária: #6B7280 Cor Erro: #EF4444
Ícones Recomendados:
- check_circle (Concluído)
- radio_button_unchecked (Não concluído)
- delete (Deletar)
- edit (Editar)
- close (Fechar)
- menu (Menu)
- search (Buscar)
- add (Adicionar)
- logout (Sair)
- login (Entrar)
- person (Perfil)
- error (Erro)
- warning (Aviso)
- info (Info)
📱 NEXT.JS - Estrutura de Componentes
Diretório
frontend-next/
├── src/
│ ├── components/
│ │ ├── ui/
│ │ │ ├── Button.tsx
│ │ │ ├── Input.tsx
│ │ │ ├── Card.tsx
│ │ │ ├── Checkbox.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── Toast.tsx
│ │ │ └── Spinner.tsx
│ │ ├── forms/
│ │ │ ├── TaskForm.tsx
│ │ │ ├── LoginForm.tsx
│ │ │ └── SignupForm.tsx
│ │ ├── tasks/
│ │ │ ├── TaskItem.tsx
│ │ │ ├── TaskList.tsx
│ │ │ └── TaskFilters.tsx
│ │ ├── layout/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── Footer.tsx
│ │ └── common/
│ │ ├── EmptyState.tsx
│ │ └── LoadingState.tsx
│ ├── lib/
│ │ ├── styles/
│ │ │ ├── colors.ts
│ │ │ ├── spacing.ts
│ │ │ ├── typography.ts
│ │ │ └── shadows.ts
│ │ └── utils.ts
│ ├── app/
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── (auth)/
│ │ │ ├── login/page.tsx
│ │ │ └── signup/page.tsx
│ │ └── (dashboard)/
│ │ └── tasks/page.tsx
│ └── styles/
│ └── tailwind.config.ts
Configuração Tailwind (tailwind.config.ts)
import type { Config } from 'tailwindcss'
export default {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#2563EB',
dark: '#1D4ED8',
light: '#DBEAFE',
},
gray: {
50: '#F9FAFB',
100: '#F3F4F6',
200: '#E5E7EB',
400: '#9CA3AF',
600: '#4B5563',
},
status: {
success: '#10B981',
warning: '#F59E0B',
error: '#EF4444',
info: '#06B6D4',
},
},
fontFamily: {
inter: ['var(--font-inter)', 'sans-serif'],
poppins: ['var(--font-poppins)', 'sans-serif'],
},
fontSize: {
'h1': ['32px', { lineHeight: '40px', fontWeight: '700', letterSpacing: '-0.5px' }],
'h2': ['24px', { lineHeight: '32px', fontWeight: '700', letterSpacing: '-0.3px' }],
'h3': ['18px', { lineHeight: '28px', fontWeight: '600', letterSpacing: '-0.2px' }],
'body-lg': ['16px', { lineHeight: '24px', fontWeight: '400' }],
'body': ['14px', { lineHeight: '22px', fontWeight: '400' }],
'body-sm': ['12px', { lineHeight: '18px', fontWeight: '400', letterSpacing: '0.2px' }],
'label': ['12px', { lineHeight: '18px', fontWeight: '600', letterSpacing: '0.5px' }],
'btn': ['14px', { lineHeight: '20px', fontWeight: '600', letterSpacing: '0.5px' }],
},
spacing: {
'xs': '4px',
'sm': '8px',
'md': '12px',
'lg': '16px',
'xl': '24px',
'2xl': '32px',
'3xl': '48px',
'4xl': '64px',
},
borderRadius: {
'sm': '4px',
'md': '8px',
'lg': '12px',
'xl': '16px',
},
boxShadow: {
'sm': '0px 1px 3px rgba(0, 0, 0, 0.1)',
'md': '0px 4px 12px rgba(0, 0, 0, 0.08)',
'lg': '0px 20px 25px rgba(0, 0, 0, 0.15)',
'focus': '0 0 0 3px rgba(37, 99, 235, 0.1)',
},
},
},
plugins: [],
} satisfies Config
🚀 FLUTTER - Design System
Diretório
mobile/
├── lib/
│ ├── config/
│ │ ├── theme.dart
│ │ ├── colors.dart
│ │ ├── typography.dart
│ │ └── spacing.dart
│ ├── widgets/
│ │ ├── buttons/
│ │ │ ├── primary_button.dart
│ │ │ ├── secondary_button.dart
│ │ │ └── tertiary_button.dart
│ │ ├── inputs/
│ │ │ ├── text_input.dart
│ │ │ ├── checkbox_input.dart
│ │ │ └── form_field.dart
│ │ ├── cards/
│ │ │ ├── task_card.dart
│ │ │ └── card.dart
│ │ ├── common/
│ │ │ ├── modal.dart
│ │ │ ├── empty_state.dart
│ │ │ └── loading_state.dart
│ │ └── app/
│ │ ├── app_bar.dart
│ │ └── bottom_nav.dart
│ ├── screens/
│ │ ├── auth/
│ │ │ ├── login_screen.dart
│ │ │ └── signup_screen.dart
│ │ ├── tasks/
│ │ │ ├── tasks_screen.dart
│ │ │ ├── task_detail_screen.dart
│ │ │ └── create_task_screen.dart
│ │ └── profile/
│ │ └── profile_screen.dart
│ └── main.dart
Colors (colors.dart)
abstract class AppColors {
// Primary
static const Color primary = Color(0xFF2563EB);
static const Color primaryDark = Color(0xFF1D4ED8);
static const Color primaryLight = Color(0xFFDBEAFE);
// Base
static const Color white = Color(0xFFFFFFFF);
static const Color black = Color(0xFF000000);
// Gray
static const Color gray50 = Color(0xFFF9FAFB);
static const Color gray100 = Color(0xFFF3F4F6);
static const Color gray200 = Color(0xFFE5E7EB);
static const Color gray400 = Color(0xFF9CA3AF);
static const Color gray600 = Color(0xFF4B5563);
// Status
static const Color success = Color(0xFF10B981);
static const Color warning = Color(0xFFF59E0B);
static const Color error = Color(0xFFEF4444);
static const Color info = Color(0xFF06B6D4);
}
Theme (theme.dart)
import 'package:flutter/material.dart';
import 'colors.dart';
final ThemeData appTheme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme(
brightness: Brightness.light,
primary: AppColors.primary,
onPrimary: AppColors.white,
secondary: AppColors.gray600,
onSecondary: AppColors.white,
error: AppColors.error,
onError: AppColors.white,
background: AppColors.white,
onBackground: AppColors.black,
surface: AppColors.white,
onSurface: AppColors.black,
),
fontFamily: 'Inter',
textTheme: TextTheme(
displayLarge: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
fontFamily: 'Poppins',
),
displayMedium: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w700,
letterSpacing: -0.3,
fontFamily: 'Poppins',
),
headlineSmall: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
fontFamily: 'Poppins',
),
bodyLarge: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.5,
),
bodyMedium: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.57,
),
bodySmall: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
letterSpacing: 0.2,
height: 1.5,
),
labelMedium: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
),
),
);
Spacing (spacing.dart)
abstract class AppSpacing {
static const double xs = 4.0;
static const double sm = 8.0;
static const double md = 12.0;
static const double lg = 16.0;
static const double xl = 24.0;
static const double xxl = 32.0;
static const double xxxl = 48.0;
static const double xxxxl = 64.0;
}
Exemplo de Componente - Primary Button
class PrimaryButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
final bool isLoading;
final bool isEnabled;
final IconData? icon;
const PrimaryButton({
required this.label,
required this.onPressed,
this.isLoading = false,
this.isEnabled = true,
this.icon,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isEnabled && !isLoading ? onPressed : null,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
disabledBackgroundColor: AppColors.gray200,
padding: EdgeInsets.symmetric(
vertical: AppSpacing.md,
horizontal: AppSpacing.lg,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
elevation: 0,
),
child: isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
isEnabled ? AppColors.white : AppColors.gray400,
),
),
)
: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null) ...[
Icon(icon, size: 20),
SizedBox(width: AppSpacing.sm),
],
Text(label),
],
),
);
}
}
🎯 PADRÕES DE LAYOUT
Responsividade Next.js
Mobile: < 640px (default)
Tablet: 640px - 1024px
Desktop: > 1024px
Breakpoints (Tailwind)
sm: '640px'
md: '768px'
lg: '1024px'
xl: '1280px'
2xl: '1536px'
Safe Area (Flutter)
Scaffold(
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.all(AppSpacing.lg),
child: YourContent(),
),
),
)
📐 ANIMAÇÕES & TRANSIÇÕES
Next.js (Tailwind)
Duration padrão: 150ms
Easing: cubic-bezier(0.4, 0, 0.2, 1)
Propriedades: transform, opacity, color, box-shadow
Exemplo:
.button {
@apply transition-all duration-150 ease-in-out;
}
.button:hover {
@apply scale-105 shadow-md;
}
Flutter
Duration animationDuration = Duration(milliseconds: 150);
Curve animationCurve = Curves.easeInOut;
✅ ACESSIBILIDADE
Contraste de Cores (WCAG AA)
- Ratio mínimo: 4.5:1 para textos normais
- Ratio mínimo: 3:1 para textos grandes
Next.js
- Usar
<label htmlFor="">para inputs - Alt text em imagens
- Semantic HTML (button, form, etc)
Flutter
- Semantics widget para leitura de tela
- Sufficient touch targets (mínimo 48x48 dp)
📋 CHECKLIST DE DESIGN
- Cores aplicadas conforme paleta
- Tipografia usando Inter e Poppins
- Espaçamento em múltiplos de 8px
- Componentes com 4 estados (default, hover, active, disabled)
- Ícones do Google Icons (20px padrão)
- Botões com altura mínima de 48px
- Inputs com altura de 44px
- Cards com border-radius 12px
- Box shadows consistentes
- Responsive design funcional
- Acessibilidade testada
- Loading states em todas as ações
- Error states claros
- Empty states amigáveis
🔗 REFERÊNCIAS & RECURSOS
Ferramentas
- Figma: Para prototipar (opcional)
- Google Fonts: https://fonts.google.com
- Google Icons: https://fonts.google.com/icons
- Color Checker: https://webaim.org/resources/contrastchecker/
Documentação
- Tailwind CSS: https://tailwindcss.com/docs
- Next.js: https://nextjs.org/docs
- Flutter: https://flutter.dev/docs
- Material Design 3: https://m3.material.io/
💾 IMPLEMENTAÇÃO IMEDIATA
Next.js - Setup Inicial
- Configurar Tailwind config com tokens
- Criar componentes base (Button, Input, Card)
- Importar Google Fonts em globals.css
- Testar componentes em storybook (opcional)
Flutter - Setup Inicial
- Adicionar Google Fonts package
- Criar arquivos de theme
- Implementar componentes customizados
- Testar em multiple devices
Mantém Fidelidade ao Design em TODAS as screens! 🎯