Skip to content

Design System

Sport Tech Club - Sistema de Design Unificado

Visão Geral

Este documento define o sistema de design do Sport Tech Club, garantindo consistência visual e de experiência em todas as interfaces da plataforma.


1. Fundamentos

1.1 Princípios de Design

yaml
principios:
  esportivo:
    descricao: Energia e dinamismo que refletem a prática esportiva
    aplicacao: Cores vibrantes, formas dinâmicas, movimento

  acessivel:
    descricao: Usável por todos, independente de habilidade
    aplicacao: Contrastes adequados, textos legíveis, áreas de toque generosas

  mobile_first:
    descricao: Projetado primeiro para dispositivos móveis
    aplicacao: Touch-friendly, performance otimizada, offline-capable

  confiavel:
    descricao: Transmite segurança e profissionalismo
    aplicacao: Consistência, feedback claro, estados bem definidos

1.2 Tokens de Design

scss
// Design Tokens - Variáveis CSS/SCSS

// ===========================================
// CORES
// ===========================================

// Cores Primárias
$color-primary-50: #E3F2FD;
$color-primary-100: #BBDEFB;
$color-primary-200: #90CAF9;
$color-primary-300: #64B5F6;
$color-primary-400: #42A5F5;
$color-primary-500: #2196F3; // Principal
$color-primary-600: #1E88E5;
$color-primary-700: #1976D2;
$color-primary-800: #1565C0;
$color-primary-900: #0D47A1;

// Cores Secundárias (Accent)
$color-secondary-50: #FFF3E0;
$color-secondary-100: #FFE0B2;
$color-secondary-200: #FFCC80;
$color-secondary-300: #FFB74D;
$color-secondary-400: #FFA726;
$color-secondary-500: #FF9800; // Principal
$color-secondary-600: #FB8C00;
$color-secondary-700: #F57C00;
$color-secondary-800: #EF6C00;
$color-secondary-900: #E65100;

// Cores de Sucesso
$color-success-50: #E8F5E9;
$color-success-100: #C8E6C9;
$color-success-500: #4CAF50;
$color-success-700: #388E3C;

// Cores de Erro
$color-error-50: #FFEBEE;
$color-error-100: #FFCDD2;
$color-error-500: #F44336;
$color-error-700: #D32F2F;

// Cores de Aviso
$color-warning-50: #FFF8E1;
$color-warning-100: #FFECB3;
$color-warning-500: #FFC107;
$color-warning-700: #FFA000;

// Cores de Informação
$color-info-50: #E1F5FE;
$color-info-100: #B3E5FC;
$color-info-500: #03A9F4;
$color-info-700: #0288D1;

// Cores Neutras
$color-neutral-0: #FFFFFF;
$color-neutral-50: #FAFAFA;
$color-neutral-100: #F5F5F5;
$color-neutral-200: #EEEEEE;
$color-neutral-300: #E0E0E0;
$color-neutral-400: #BDBDBD;
$color-neutral-500: #9E9E9E;
$color-neutral-600: #757575;
$color-neutral-700: #616161;
$color-neutral-800: #424242;
$color-neutral-900: #212121;
$color-neutral-1000: #000000;

// Cores por Esporte
$color-sport-beach-tennis: #FF6B35;
$color-sport-beach-volley: #F7C948;
$color-sport-futevolei: #2EC4B6;
$color-sport-padel: #9B5DE5;

2. Tipografia

2.1 Famílias de Fonte

scss
// Fontes
$font-family-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
$font-family-secondary: 'Poppins', sans-serif;
$font-family-mono: 'JetBrains Mono', 'Fira Code', monospace;

// Import Google Fonts
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@500;600;700&display=swap');

2.2 Escala Tipográfica

scss
// Tamanhos de Fonte
$font-size-xs: 0.75rem;    // 12px
$font-size-sm: 0.875rem;   // 14px
$font-size-base: 1rem;     // 16px
$font-size-lg: 1.125rem;   // 18px
$font-size-xl: 1.25rem;    // 20px
$font-size-2xl: 1.5rem;    // 24px
$font-size-3xl: 1.875rem;  // 30px
$font-size-4xl: 2.25rem;   // 36px
$font-size-5xl: 3rem;      // 48px
$font-size-6xl: 3.75rem;   // 60px

// Pesos de Fonte
$font-weight-light: 300;
$font-weight-regular: 400;
$font-weight-medium: 500;
$font-weight-semibold: 600;
$font-weight-bold: 700;

// Altura de Linha
$line-height-tight: 1.25;
$line-height-snug: 1.375;
$line-height-normal: 1.5;
$line-height-relaxed: 1.625;
$line-height-loose: 2;

// Espaçamento entre Letras
$letter-spacing-tighter: -0.05em;
$letter-spacing-tight: -0.025em;
$letter-spacing-normal: 0;
$letter-spacing-wide: 0.025em;
$letter-spacing-wider: 0.05em;
$letter-spacing-widest: 0.1em;

2.3 Estilos de Texto

scss
// Headings
.text-h1 {
  font-family: $font-family-secondary;
  font-size: $font-size-4xl;
  font-weight: $font-weight-bold;
  line-height: $line-height-tight;
  letter-spacing: $letter-spacing-tight;

  @media (max-width: 768px) {
    font-size: $font-size-3xl;
  }
}

.text-h2 {
  font-family: $font-family-secondary;
  font-size: $font-size-3xl;
  font-weight: $font-weight-semibold;
  line-height: $line-height-tight;
}

.text-h3 {
  font-family: $font-family-secondary;
  font-size: $font-size-2xl;
  font-weight: $font-weight-semibold;
  line-height: $line-height-snug;
}

.text-h4 {
  font-family: $font-family-primary;
  font-size: $font-size-xl;
  font-weight: $font-weight-semibold;
  line-height: $line-height-snug;
}

.text-h5 {
  font-family: $font-family-primary;
  font-size: $font-size-lg;
  font-weight: $font-weight-medium;
  line-height: $line-height-normal;
}

.text-h6 {
  font-family: $font-family-primary;
  font-size: $font-size-base;
  font-weight: $font-weight-medium;
  line-height: $line-height-normal;
}

// Body
.text-body-lg {
  font-size: $font-size-lg;
  line-height: $line-height-relaxed;
}

.text-body {
  font-size: $font-size-base;
  line-height: $line-height-normal;
}

.text-body-sm {
  font-size: $font-size-sm;
  line-height: $line-height-normal;
}

// Caption/Label
.text-caption {
  font-size: $font-size-xs;
  line-height: $line-height-normal;
  letter-spacing: $letter-spacing-wide;
}

.text-overline {
  font-size: $font-size-xs;
  font-weight: $font-weight-semibold;
  line-height: $line-height-normal;
  letter-spacing: $letter-spacing-widest;
  text-transform: uppercase;
}

3. Espaçamento

3.1 Escala de Espaçamento

scss
// Espaçamento (baseado em 4px)
$spacing-0: 0;
$spacing-1: 0.25rem;   // 4px
$spacing-2: 0.5rem;    // 8px
$spacing-3: 0.75rem;   // 12px
$spacing-4: 1rem;      // 16px
$spacing-5: 1.25rem;   // 20px
$spacing-6: 1.5rem;    // 24px
$spacing-8: 2rem;      // 32px
$spacing-10: 2.5rem;   // 40px
$spacing-12: 3rem;     // 48px
$spacing-16: 4rem;     // 64px
$spacing-20: 5rem;     // 80px
$spacing-24: 6rem;     // 96px

// Aliases semânticos
$spacing-xs: $spacing-1;
$spacing-sm: $spacing-2;
$spacing-md: $spacing-4;
$spacing-lg: $spacing-6;
$spacing-xl: $spacing-8;
$spacing-2xl: $spacing-12;

3.2 Layout Grid

scss
// Container
$container-max-width: 1280px;
$container-padding: $spacing-4;

// Grid
$grid-columns: 12;
$grid-gutter: $spacing-4;

// Breakpoints
$breakpoint-xs: 0;
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
$breakpoint-xxl: 1400px;

// Mixin para media queries
@mixin breakpoint($size) {
  @if $size == 'sm' {
    @media (min-width: $breakpoint-sm) { @content; }
  } @else if $size == 'md' {
    @media (min-width: $breakpoint-md) { @content; }
  } @else if $size == 'lg' {
    @media (min-width: $breakpoint-lg) { @content; }
  } @else if $size == 'xl' {
    @media (min-width: $breakpoint-xl) { @content; }
  } @else if $size == 'xxl' {
    @media (min-width: $breakpoint-xxl) { @content; }
  }
}

4. Componentes

4.1 Botões

scss
// Base do Botão
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: $spacing-2;
  padding: $spacing-3 $spacing-6;
  font-family: $font-family-primary;
  font-size: $font-size-base;
  font-weight: $font-weight-medium;
  line-height: 1;
  text-decoration: none;
  border: none;
  border-radius: $radius-md;
  cursor: pointer;
  transition: all 0.2s ease;

  // Touch target mínimo de 44x44px
  min-height: 44px;
  min-width: 44px;

  &:focus-visible {
    outline: 2px solid $color-primary-500;
    outline-offset: 2px;
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
}

// Variantes
.btn-primary {
  @extend .btn;
  background-color: $color-primary-500;
  color: $color-neutral-0;

  &:hover:not(:disabled) {
    background-color: $color-primary-600;
  }

  &:active:not(:disabled) {
    background-color: $color-primary-700;
  }
}

.btn-secondary {
  @extend .btn;
  background-color: transparent;
  color: $color-primary-500;
  border: 2px solid $color-primary-500;

  &:hover:not(:disabled) {
    background-color: $color-primary-50;
  }
}

.btn-ghost {
  @extend .btn;
  background-color: transparent;
  color: $color-neutral-700;

  &:hover:not(:disabled) {
    background-color: $color-neutral-100;
  }
}

.btn-danger {
  @extend .btn;
  background-color: $color-error-500;
  color: $color-neutral-0;

  &:hover:not(:disabled) {
    background-color: $color-error-700;
  }
}

// Tamanhos
.btn-sm {
  padding: $spacing-2 $spacing-4;
  font-size: $font-size-sm;
  min-height: 36px;
}

.btn-lg {
  padding: $spacing-4 $spacing-8;
  font-size: $font-size-lg;
  min-height: 52px;
}

// Com ícone
.btn-icon {
  padding: $spacing-3;

  &.btn-sm { padding: $spacing-2; }
  &.btn-lg { padding: $spacing-4; }
}

4.2 Inputs

scss
// Base do Input
.input {
  display: block;
  width: 100%;
  padding: $spacing-3 $spacing-4;
  font-family: $font-family-primary;
  font-size: $font-size-base;
  line-height: $line-height-normal;
  color: $color-neutral-900;
  background-color: $color-neutral-0;
  border: 1px solid $color-neutral-300;
  border-radius: $radius-md;
  transition: all 0.2s ease;

  // Touch target mínimo
  min-height: 44px;

  &::placeholder {
    color: $color-neutral-500;
  }

  &:hover:not(:disabled) {
    border-color: $color-neutral-400;
  }

  &:focus {
    outline: none;
    border-color: $color-primary-500;
    box-shadow: 0 0 0 3px rgba($color-primary-500, 0.1);
  }

  &:disabled {
    background-color: $color-neutral-100;
    cursor: not-allowed;
  }

  &.input-error {
    border-color: $color-error-500;

    &:focus {
      box-shadow: 0 0 0 3px rgba($color-error-500, 0.1);
    }
  }

  &.input-success {
    border-color: $color-success-500;
  }
}

// Input Group
.input-group {
  display: flex;
  flex-direction: column;
  gap: $spacing-1;
}

.input-label {
  font-size: $font-size-sm;
  font-weight: $font-weight-medium;
  color: $color-neutral-700;
}

.input-helper {
  font-size: $font-size-xs;
  color: $color-neutral-600;
}

.input-error-message {
  font-size: $font-size-xs;
  color: $color-error-500;
}

// Input com ícone
.input-with-icon {
  position: relative;

  .input {
    padding-left: $spacing-10;
  }

  .input-icon {
    position: absolute;
    left: $spacing-3;
    top: 50%;
    transform: translateY(-50%);
    color: $color-neutral-500;
  }
}

4.3 Cards

scss
// Variáveis de bordas arredondadas
$radius-none: 0;
$radius-sm: 0.25rem;   // 4px
$radius-md: 0.5rem;    // 8px
$radius-lg: 0.75rem;   // 12px
$radius-xl: 1rem;      // 16px
$radius-2xl: 1.5rem;   // 24px
$radius-full: 9999px;

// Sombras
$shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
$shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
$shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
$shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
$shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);

// Card Base
.card {
  background-color: $color-neutral-0;
  border-radius: $radius-lg;
  box-shadow: $shadow-sm;
  overflow: hidden;
}

.card-header {
  padding: $spacing-4 $spacing-4 0;
}

.card-body {
  padding: $spacing-4;
}

.card-footer {
  padding: 0 $spacing-4 $spacing-4;
}

// Card Variantes
.card-elevated {
  box-shadow: $shadow-md;

  &:hover {
    box-shadow: $shadow-lg;
  }
}

.card-outlined {
  box-shadow: none;
  border: 1px solid $color-neutral-200;
}

.card-interactive {
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;

  &:hover {
    transform: translateY(-2px);
    box-shadow: $shadow-lg;
  }

  &:active {
    transform: translateY(0);
  }
}

4.4 Court Card (Específico do Domínio)

vue
<template>
  <div
    class="court-card"
    :class="[`court-card--${sport}`, { 'court-card--available': isAvailable }]"
  >
    <div class="court-card__header">
      <span class="court-card__sport-badge">
        <SportIcon :sport="sport" />
        {{ sportLabel }}
      </span>
      <span class="court-card__status" :class="`status--${status}`">
        {{ statusLabel }}
      </span>
    </div>

    <div class="court-card__body">
      <h3 class="court-card__name">{{ name }}</h3>

      <div class="court-card__features">
        <span v-if="covered" class="feature">
          <CoveredIcon /> Coberta
        </span>
        <span v-if="lighting" class="feature">
          <LightingIcon /> Iluminação
        </span>
        <span class="feature">
          <UsersIcon /> {{ maxPlayers }} jogadores
        </span>
      </div>

      <div class="court-card__price">
        <span class="price-value">R$ {{ pricePerHour }}</span>
        <span class="price-unit">/hora</span>
      </div>
    </div>

    <div class="court-card__footer">
      <button
        class="btn btn-primary btn-block"
        :disabled="!isAvailable"
        @click="$emit('book')"
      >
        {{ isAvailable ? 'Reservar' : 'Indisponível' }}
      </button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.court-card {
  @extend .card;
  @extend .card-interactive;

  &__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: $spacing-4;
    border-bottom: 1px solid $color-neutral-100;
  }

  &__sport-badge {
    display: inline-flex;
    align-items: center;
    gap: $spacing-2;
    padding: $spacing-1 $spacing-2;
    font-size: $font-size-sm;
    font-weight: $font-weight-medium;
    border-radius: $radius-full;
  }

  // Cores por esporte
  &--beach-tennis .court-card__sport-badge {
    background-color: rgba($color-sport-beach-tennis, 0.1);
    color: $color-sport-beach-tennis;
  }

  &--beach-volley .court-card__sport-badge {
    background-color: rgba($color-sport-beach-volley, 0.1);
    color: darken($color-sport-beach-volley, 15%);
  }

  &--futevolei .court-card__sport-badge {
    background-color: rgba($color-sport-futevolei, 0.1);
    color: $color-sport-futevolei;
  }

  &--padel .court-card__sport-badge {
    background-color: rgba($color-sport-padel, 0.1);
    color: $color-sport-padel;
  }

  &__status {
    font-size: $font-size-xs;
    font-weight: $font-weight-medium;
    padding: $spacing-1 $spacing-2;
    border-radius: $radius-full;

    &.status--available {
      background-color: $color-success-50;
      color: $color-success-700;
    }

    &.status--occupied {
      background-color: $color-error-50;
      color: $color-error-700;
    }

    &.status--maintenance {
      background-color: $color-warning-50;
      color: $color-warning-700;
    }
  }

  &__body {
    padding: $spacing-4;
  }

  &__name {
    @extend .text-h5;
    margin-bottom: $spacing-2;
  }

  &__features {
    display: flex;
    flex-wrap: wrap;
    gap: $spacing-2;
    margin-bottom: $spacing-4;

    .feature {
      display: inline-flex;
      align-items: center;
      gap: $spacing-1;
      font-size: $font-size-sm;
      color: $color-neutral-600;
    }
  }

  &__price {
    .price-value {
      font-size: $font-size-2xl;
      font-weight: $font-weight-bold;
      color: $color-primary-600;
    }

    .price-unit {
      font-size: $font-size-sm;
      color: $color-neutral-500;
    }
  }

  &__footer {
    padding: $spacing-4;
    padding-top: 0;
  }
}

.btn-block {
  width: 100%;
}
</style>

5. Iconografia

5.1 Biblioteca de Ícones

yaml
icones:
  biblioteca: Lucide Icons
  url: https://lucide.dev

  categorias:
    navegacao:
      - home
      - menu
      - arrow-left
      - arrow-right
      - chevron-down
      - x

    acoes:
      - plus
      - edit
      - trash
      - search
      - filter
      - download
      - share

    esportes:
      - beach_tennis: custom
      - beach_volley: custom
      - futevolei: custom
      - padel: custom

    status:
      - check-circle
      - x-circle
      - alert-circle
      - info
      - clock

    usuario:
      - user
      - users
      - settings
      - log-out
      - bell

    reserva:
      - calendar
      - map-pin
      - credit-card
      - receipt

5.2 Componente de Ícone

vue
<template>
  <component
    :is="iconComponent"
    :size="size"
    :stroke-width="strokeWidth"
    :class="['icon', `icon--${color}`]"
  />
</template>

<script setup>
import { computed } from 'vue';
import * as icons from 'lucide-vue-next';

const props = defineProps({
  name: { type: String, required: true },
  size: { type: [Number, String], default: 24 },
  strokeWidth: { type: [Number, String], default: 2 },
  color: { type: String, default: 'inherit' },
});

const iconComponent = computed(() => {
  const pascalCase = props.name
    .split('-')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join('');
  return icons[pascalCase] || icons.HelpCircle;
});
</script>

<style lang="scss" scoped>
.icon {
  flex-shrink: 0;

  &--inherit { color: inherit; }
  &--primary { color: $color-primary-500; }
  &--secondary { color: $color-secondary-500; }
  &--success { color: $color-success-500; }
  &--error { color: $color-error-500; }
  &--warning { color: $color-warning-500; }
  &--muted { color: $color-neutral-500; }
}
</style>

6. Dark Mode

6.1 Tokens Dark Mode

scss
// Dark Mode Tokens
:root[data-theme="dark"] {
  // Cores de superfície
  --color-surface-0: #{$color-neutral-900};
  --color-surface-1: #{$color-neutral-800};
  --color-surface-2: #{$color-neutral-700};
  --color-surface-3: #{$color-neutral-600};

  // Cores de texto
  --color-text-primary: #{$color-neutral-50};
  --color-text-secondary: #{$color-neutral-300};
  --color-text-muted: #{$color-neutral-500};

  // Cores de borda
  --color-border: #{$color-neutral-700};
  --color-border-hover: #{$color-neutral-600};

  // Primárias (mais claras no dark)
  --color-primary: #{$color-primary-400};
  --color-primary-hover: #{$color-primary-300};

  // Backgrounds
  --color-bg-overlay: rgba(0, 0, 0, 0.8);
  --color-bg-input: #{$color-neutral-800};
}

// Light Mode (default)
:root {
  --color-surface-0: #{$color-neutral-0};
  --color-surface-1: #{$color-neutral-50};
  --color-surface-2: #{$color-neutral-100};
  --color-surface-3: #{$color-neutral-200};

  --color-text-primary: #{$color-neutral-900};
  --color-text-secondary: #{$color-neutral-700};
  --color-text-muted: #{$color-neutral-500};

  --color-border: #{$color-neutral-200};
  --color-border-hover: #{$color-neutral-300};

  --color-primary: #{$color-primary-500};
  --color-primary-hover: #{$color-primary-600};

  --color-bg-overlay: rgba(0, 0, 0, 0.5);
  --color-bg-input: #{$color-neutral-0};
}

6.2 Componente Theme Toggle

vue
<template>
  <button
    class="theme-toggle"
    :aria-label="isDark ? 'Ativar modo claro' : 'Ativar modo escuro'"
    @click="toggleTheme"
  >
    <SunIcon v-if="isDark" />
    <MoonIcon v-else />
  </button>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { SunIcon, MoonIcon } from 'lucide-vue-next';

const isDark = ref(false);

onMounted(() => {
  // Verifica preferência salva ou do sistema
  const saved = localStorage.getItem('theme');
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

  isDark.value = saved ? saved === 'dark' : prefersDark;
  applyTheme();
});

function toggleTheme() {
  isDark.value = !isDark.value;
  localStorage.setItem('theme', isDark.value ? 'dark' : 'light');
  applyTheme();
}

function applyTheme() {
  document.documentElement.dataset.theme = isDark.value ? 'dark' : 'light';
}
</script>

7. Animações e Transições

7.1 Tokens de Animação

scss
// Duração
$duration-instant: 50ms;
$duration-fast: 100ms;
$duration-normal: 200ms;
$duration-slow: 300ms;
$duration-slower: 500ms;

// Easing
$ease-default: cubic-bezier(0.4, 0, 0.2, 1);
$ease-in: cubic-bezier(0.4, 0, 1, 1);
$ease-out: cubic-bezier(0, 0, 0.2, 1);
$ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
$ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);

// Transições pré-definidas
$transition-fast: all $duration-fast $ease-default;
$transition-normal: all $duration-normal $ease-default;
$transition-slow: all $duration-slow $ease-default;

7.2 Classes de Animação

scss
// Fade
.fade-enter-active,
.fade-leave-active {
  transition: opacity $duration-normal $ease-default;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

// Slide Up
.slide-up-enter-active,
.slide-up-leave-active {
  transition: all $duration-normal $ease-out;
}

.slide-up-enter-from,
.slide-up-leave-to {
  opacity: 0;
  transform: translateY(20px);
}

// Scale
.scale-enter-active,
.scale-leave-active {
  transition: all $duration-fast $ease-bounce;
}

.scale-enter-from,
.scale-leave-to {
  opacity: 0;
  transform: scale(0.95);
}

// Skeleton Loading
@keyframes skeleton-loading {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}

.skeleton {
  background: linear-gradient(
    90deg,
    var(--color-surface-1) 25%,
    var(--color-surface-2) 50%,
    var(--color-surface-1) 75%
  );
  background-size: 200% 100%;
  animation: skeleton-loading 1.5s ease-in-out infinite;
  border-radius: $radius-sm;
}

8. Acessibilidade

8.1 Checklist WCAG 2.1 AA

yaml
acessibilidade:
  perceivable:
    contraste:
      texto_normal: 4.5:1 mínimo
      texto_grande: 3:1 mínimo
      elementos_ui: 3:1 mínimo

    texto:
      redimensionamento: até 200% sem perda
      espaçamento: configurável

    imagens:
      alt_text: obrigatório
      decorativas: alt=""

  operable:
    teclado:
      navegacao: Tab, Shift+Tab
      ativacao: Enter, Space
      fechar: Escape
      focus_visible: sempre visível

    touch_targets:
      tamanho_minimo: 44x44px
      espacamento: 8px entre elementos

  understandable:
    linguagem:
      html_lang: pt-BR
      abreviacoes: expandidas

    erros:
      identificacao: clara e próxima
      sugestao: quando possível

  robust:
    semantica:
      headings: hierárquicos
      landmarks: header, main, nav, footer
      aria: apenas quando necessário

8.2 Componente Acessível

vue
<template>
  <div
    class="alert"
    :class="`alert--${type}`"
    role="alert"
    :aria-live="type === 'error' ? 'assertive' : 'polite'"
  >
    <div class="alert__icon" aria-hidden="true">
      <AlertCircleIcon v-if="type === 'error'" />
      <CheckCircleIcon v-else-if="type === 'success'" />
      <InfoIcon v-else />
    </div>

    <div class="alert__content">
      <h4 v-if="title" class="alert__title">{{ title }}</h4>
      <p class="alert__message">{{ message }}</p>
    </div>

    <button
      v-if="dismissible"
      class="alert__close"
      type="button"
      aria-label="Fechar alerta"
      @click="$emit('dismiss')"
    >
      <XIcon aria-hidden="true" />
    </button>
  </div>
</template>

9. Recursos e Ferramentas

9.1 Design Tokens Export

json
{
  "color": {
    "primary": {
      "50": { "value": "#E3F2FD" },
      "500": { "value": "#2196F3" },
      "900": { "value": "#0D47A1" }
    }
  },
  "spacing": {
    "1": { "value": "0.25rem" },
    "2": { "value": "0.5rem" },
    "4": { "value": "1rem" }
  },
  "typography": {
    "fontFamily": {
      "primary": { "value": "Inter, sans-serif" }
    }
  }
}

9.2 Figma/Design Handoff

yaml
figma:
  library: Sport Tech Club Design System
  url: figma.com/file/xxx

  estrutura:
    - Foundations
      - Colors
      - Typography
      - Spacing
      - Icons
    - Components
      - Buttons
      - Inputs
      - Cards
      - Navigation
    - Patterns
      - Forms
      - Lists
      - Modals
    - Templates
      - Login
      - Dashboard
      - Booking

Este Design System serve como fonte única de verdade para todas as decisões de design e implementação de UI no Sport Tech Club.