Documentação

Guia completo para desenvolvedores da plataforma Ameciclo

Visão Geral

O projeto Ameciclo é uma plataforma web full-stack desenvolvida com Remix, TypeScript e Reactque fornece dados abertos sobre mobilidade ativa na região metropolitana do Recife.

A plataforma integra múltiplas fontes de dados (APIs externas, CMS Strapi, dados estáticos) e oferece: visualizações interativas com mapas (Mapbox), gráficos (Highcharts), calendários (FullCalendar), observatórios especializados (Ideciclo, Sinistros Fatais, Vias Inseguras, SAMU, CicloDados), sistema de contagens de ciclistas, documentos públicos, projetos da organização e ferramentas de acessibilidade.

Monitoramento em Tempo Real

A plataforma conta com uma página dedicada ao monitoramento do status de todos os serviços e páginas em tempo real.

Ver Status dos Serviços

Arquitetura

SSR com Remix, 27 rotas, 182+ componentes, deploy Vercel

Stack Principal

Remix 2.16, React 18, TypeScript 5, Vite 5, Tailwind CSS 3

Recursos

Mapas, gráficos, calendários, busca, acessibilidade WCAG

Instalação

Pré-requisitos

  • Node.js ≥ 20.0.0
  • npm ou yarn
  • Git

Passos para instalação

1. Clone o repositório:

git clone https://github.com/Ameciclo/ameciclo.git

2. Instale as dependências:

npm install

3. Inicie o servidor de desenvolvimento:

npm run dev

4. Acesse a aplicação:

http://localhost:5173

Estrutura do Projeto

ameciclo/
├── app/
│   ├── components/          # 182+ componentes React organizados por funcionalidade
│   │   ├── Commom/         # Componentes globais reutilizáveis
│   │   │   ├── Navbar.tsx, Footer.tsx, Banner.tsx, Breadcrumb.tsx
│   │   │   ├── AccessibilityControls.tsx, ChangeThemeButton.tsx
│   │   │   ├── ErrorBoundary.tsx, ApiStatusHandler.tsx
│   │   │   ├── Charts/, Maps/, Table/, Icones/
│   │   │   └── GoogleAnalytics.tsx, SEO.tsx
│   │   ├── Agenda/         # FullCalendar com Google Calendar API
│   │   ├── Biciclopedia/   # FAQ com busca e accordion
│   │   ├── CicloDados/     # Plataforma de dados colaborativos
│   │   ├── Contagens/      # Visualização de contagens de ciclistas
│   │   ├── Dados/          # Hub central de dados
│   │   ├── Documentacao/   # Esta documentação (11 componentes)
│   │   ├── ExecucaoCicloviaria/ # Execução de infraestrutura
│   │   ├── Ideciclo/       # Índice de ciclabilidade
│   │   ├── PaginaInicial/  # Componentes da home
│   │   ├── Projetos/       # Projetos da organização
│   │   ├── QuemSomos/      # Sobre a Ameciclo
│   │   ├── Samu/           # Dados de chamados do SAMU
│   │   ├── SinistrosFatais/ # Análise de sinistros fatais
│   │   └── ViasInseguras/  # Ranking de vias perigosas
│   ├── routes/             # 27 rotas (file-based routing do Remix)
│   │   ├── _index.tsx      # Home com seções dinâmicas
│   │   ├── agenda.tsx, biciclopedia.tsx, contato.tsx
│   │   ├── dados._index.tsx, dados.ciclodados._index.tsx
│   │   ├── dados.contagens.$slug.tsx, dados.via.$slug.tsx
│   │   ├── dados.ideciclo.$id.tsx, dados.sinistros-fatais.tsx
│   │   ├── dados.vias-inseguras.tsx, dados.samu.tsx
│   │   ├── projetos.$projeto.tsx, quemsomos.tsx
│   │   └── documentacao._index.tsx # Esta página
│   ├── loader/             # Loaders para SSR e data fetching
│   │   ├── home.ts, agenda.ts, projetos.ts, quemsomos.ts
│   │   ├── dados.contagens.ts, dados.ideciclo.ts
│   │   ├── dados.sinistros-fatais.ts, dados.samu.ts
│   │   └── compareContagensLoader.ts
│   ├── services/           # Serviços e APIs
│   │   ├── fetchWithTimeout.ts  # Fetch com timeout e fallback
│   │   ├── cmsApi.ts            # Integração com Strapi CMS
│   │   ├── contagens.service.ts # Serviço de contagens
│   │   ├── ciclodados.service.ts, streets.service.ts
│   │   ├── cache.ts, staticFallbacks.ts
│   │   └── SinistrosFatais/
│   ├── contexts/           # React Context API
│   │   └── ApiStatusContext.tsx # Status global de APIs
│   ├── providers/          # Providers globais
│   │   └── QueryProvider.tsx    # TanStack Query
│   ├── hooks/              # Custom hooks
│   │   ├── useApiWithAlert.ts, useFocusTrap.ts
│   ├── utils/              # Utilitários
│   │   ├── mapbox.server.ts, slugify.ts, translations.ts
│   │   └── contagens/
│   ├── types/              # TypeScript types
│   ├── root.tsx            # Root component com layout global
│   ├── entry.client.tsx    # Entry point do cliente
│   ├── entry.server.tsx    # Entry point do servidor
│   └── globals.css         # Estilos globais
├── public/                 # Assets estáticos
│   ├── icons/              # Ícones SVG organizados por seção
│   ├── ideciclo/, images/, pages_covers/
│   ├── data/               # GeoJSON e dados estáticos
│   └── dbs/                # Databases JSON locais
├── docs/                   # Documentação de APIs
├── .env                    # Variáveis de ambiente
├── vite.config.ts          # Configuração Vite + Remix
├── tailwind.config.ts      # Configuração Tailwind CSS
├── tsconfig.json           # Configuração TypeScript
├── vercel.json             # Deploy Vercel
└── package.json            # Dependências e scripts

Detalhamento das Pastas

app/components/Commom/

Pasta de componentes reutilizáveis usados em toda a aplicação:

  • Navbar.tsx - Navegação responsiva com menu mobile
  • Footer.tsx - Rodapé com links e redes sociais
  • Banner.tsx - Banners de páginas com imagens otimizadas
  • Breadcrumb.tsx - Navegação estrutural (SEO)
  • AccessibilityControls.tsx - Controles WCAG (fonte, contraste)
  • ChangeThemeButton.tsx - Toggle dark/light mode
  • ErrorBoundary.tsx - Tratamento de erros React
  • ApiStatusHandler.tsx - Monitoramento de APIs
  • Charts/ - Componentes Highcharts reutilizáveis
  • Maps/ - Componentes Mapbox GL reutilizáveis
  • Table/ - Tabelas com filtros e ordenação
  • GoogleAnalytics.tsx - GA4 tracking

app/routes/

Sistema de roteamento baseado em arquivos do Remix:

  • _index.tsx - Home (banner, carrossel, dados, CTA)
  • dados._index.tsx - Hub de dados com cards
  • dados.ciclodados._index.tsx - Plataforma colaborativa
  • dados.contagens.$slug.tsx - Contagem individual com gráficos
  • dados.contagens.$slug.$compareSlug.tsx - Comparação
  • dados.ideciclo.$id.tsx - Detalhes do Ideciclo
  • dados.sinistros-fatais.tsx - Análise de sinistros
  • dados.vias-inseguras.tsx - Ranking de vias
  • dados.via.$slug.tsx - Detalhes de via individual
  • dados.samu.tsx - Chamados SAMU com mapa coroplético
  • projetos.$projeto.tsx - Projeto individual do CMS
  • biciclopedia.$question.tsx - FAQ individual

app/loader/

Funções para carregamento de dados do servidor:

  • home.ts - Dados da home (projetos, contagens)
  • dados.contagens.ts - Lista de contagens da API
  • compareContagensLoader.ts - Comparação de contagens
  • dados.ideciclo.ts - Dados do índice de ciclabilidade
  • dados.sinistros-fatais.ts - Dados de sinistros
  • dados.vias-inseguras.ts - Ranking de vias
  • dados.samu.ts - Chamados do SAMU
  • projetos.ts - Projetos do CMS Strapi
  • agenda.ts - Google Calendar API
  • quemsomos.ts - Dados da página institucional

Componentes

Os componentes são organizados por funcionalidade e reutilizabilidade:

Organização por Funcionalidade

app/components/ ├── Commom/ # 50+ componentes globais ├── Agenda/ # 2 componentes (EventCalendar, Loading) ├── Biciclopedia/ # 3 componentes (FAQ, Search, Accordion) ├── Charts/ # 4 componentes de gráficos ├── CicloDados/ # 20+ componentes (MapView, Sidebar, Filters) ├── Contagens/ # 9 componentes (Tabelas, Gráficos, Mapas) ├── Dados/ # 3 componentes (Loading, Grid, Boxes) ├── Documentacao/ # 11 componentes desta página ├── Documentos/ # 2 componentes (Lista, Sessão) ├── ExecucaoCicloviaria/ # 3 componentes (Stats, Charts, Loading) ├── Ideciclo/ # 10 componentes (Tabelas, Mapas, Stats) ├── PaginaInicial/ # 7 componentes (Banner, Carousel, Sections) ├── Perfil/ # 1 componente (Dashboard) ├── Projetos/ # 7 componentes (Cards, Search, Language) ├── QuemSomos/ # 3 componentes (Tabs, Modal, Loading) ├── Samu/ # 2 componentes (Map, ClientSide) ├── SinistrosFatais/ # 7 componentes (Matrix, Filters, Cards) └── ViasInseguras/ # 12 componentes (Map, Ranking, Charts)

Componentes Principais

CicloDados (Plataforma Colaborativa)

Sistema completo de visualização de dados com:

  • • MapView.tsx - Mapa interativo Mapbox com layers
  • • LeftSidebar.tsx, RightSidebar.tsx - Painéis laterais
  • • FilterSection.tsx - Filtros avançados
  • • FloatingChat.tsx - Chat integrado
  • • MuralView.tsx - Visualização tipo mural

ViasInseguras (Análise de Vias)

Sistema de ranking e análise temporal:

  • • ViasInsegurasMap.tsx - Mapa com heatmap
  • • ViasRankingTable.tsx - Tabela ordenada
  • • TemporalAnalysis.tsx - Análise temporal
  • • ConcentrationChart.tsx - Gráficos de concentração
  • • ViaSearch.tsx - Busca com autocomplete

Contagens (Visualização de Dados)

Componentes para contagens de ciclistas:

  • • CountsMap.tsx - Mapa de pontos de contagem
  • • HourlyCyclistsChart.tsx - Gráfico por hora
  • • FlowContainer.tsx - Fluxo de ciclistas
  • • CountingComparisionTable.tsx - Comparação

Exemplo de Uso

import Banner from "~/components/Commom/Banner"; import Breadcrumb from "~/components/Commom/Breadcrumb"; import { CountsMap } from "~/components/Contagens/CountsMap"; export default function ContagensPage() { return ( <> <Banner image="/contagens.webp" alt="Contagens" /> <Breadcrumb label="Contagens" slug="/dados/contagens" /> <CountsMap data={counts} /> </> ); }

Rotas

O Remix utiliza roteamento baseado em arquivos. Cada arquivo em app/routes/ representa uma rota. O projeto possui 27 rotas organizadas hierarquicamente:

Rotas Públicas:

/_index.tsx → /
/agenda.tsx → /agenda
/biciclopedia.tsx → /biciclopedia
/biciclopedia_.$question.tsx → /biciclopedia/:question
/contato.tsx → /contato
/participe.tsx → /participe
/quemsomos.tsx → /quemsomos
/documentacao._index.tsx → /documentacao

Rotas de Dados:

/dados._index.tsx → /dados
/dados.ciclodados._index.tsx → /dados/ciclodados
/dados.contagens._index.tsx → /dados/contagens
/dados.contagens.$slug._index.tsx → /dados/contagens/:slug
/dados.contagens.$slug.$compareSlug.tsx → /dados/contagens/:slug/:compareSlug
/dados.documentos.tsx → /dados/documentos
/dados.dom.tsx → /dados/dom
/dados.loa.tsx → /dados/loa
/dados.execucaocicloviaria.tsx → /dados/execucaocicloviaria
/dados.ideciclo._index.tsx → /dados/ideciclo
/dados.ideciclo.$id.tsx → /dados/ideciclo/:id
/dados.perfil.tsx → /dados/perfil
/dados.samu.tsx → /dados/samu
/dados.sinistros-fatais.tsx → /dados/sinistros-fatais
/dados.vias-inseguras.tsx → /dados/vias-inseguras
/dados.viasinseguras.$slug.tsx → /dados/vias-inseguras/:slug

Rotas de Projetos:

/projetos._index.tsx → /projetos
/projetos.$projeto.tsx → /projetos/:projeto

Rota 404:

/$.tsx → Catch-all para páginas não encontradas

Padrões de Nomenclatura:

  • _index.tsx - Rota index (sem segmento na URL)
  • $param.tsx - Parâmetro dinâmico
  • parent.child.tsx - Rota aninhada
  • $.tsx - Catch-all (404)

Exemplo completo com loader e action:

// app/routes/contagens.$slug.tsx import { LoaderFunctionArgs } from "@remix-run/node"; import { fetchWithTimeout } from "~/services/fetchWithTimeout"; export async function loader({ params }: LoaderFunctionArgs) { const { slug } = params; // Usando fetchWithTimeout para evitar timeouts const data = await fetchWithTimeout( "http://api.garfo.ameciclo.org/cyclist-counts", { cache: "no-cache" }, 5000, { counts: [] } ); const contagem = data.counts?.find((c: any) => c.slug === slug); if (!contagem) { throw new Response("Contagem não encontrada", { status: 404 }); } return { contagem }; }

API

A aplicação consome dados de múltiplas APIs externas e internas. Todas as requisições utilizam fetchWithTimeout para garantir resiliência:

APIs Externas

API Garfo - Contagens de Ciclistas
GET http://api.garfo.ameciclo.org/cyclist-counts

Retorna lista de contagens realizadas

GET http://api.garfo.ameciclo.org/cyclist-counts/edition/:id

Detalhes de uma contagem específica

CMS Strapi - Conteúdo
GET http://do.strapi.ameciclo.org/api/projetos

Lista de projetos da organização

GET http://do.strapi.ameciclo.org/api/projetos/:slug

Detalhes de um projeto

Google Calendar API
Integração via FullCalendar

Eventos da agenda da Ameciclo

Mapbox API
Token configurado via variável de ambiente

Mapas interativos em várias páginas

Serviço fetchWithTimeout

// app/services/fetchWithTimeout.ts export async function fetchWithTimeout( url: string, options: RequestInit = {}, timeout: number = 5000, fallbackData: any = null, onApiDown?: (error: string) => void, retries: number = 1 ): Promise<any> { // Implementa timeout, retries e fallback // Garante resiliência quando APIs estão indisponíveis }

Estrutura de Dados - Contagens

{ "counts": [ { "id": 1, "name": "Ponte do Jiquiá", "slug": "ponte-do-jiquia", "date": "2024-03-15", "total_cyclists": 245, "total_women": 89, "total_helmet": 156, "total_juveniles": 23, "location": { "lat": -8.0476, "lng": -34.8770 } } ] }

Testes

O projeto utiliza ferramentas de qualidade de código para garantir a confiabilidade:

Linting e Type Checking

Verificar qualidade do código:

npm run lint

Verificar tipos TypeScript:

npm run typecheck

Configuração do ESLint

// .eslintrc.cjs module.exports = { extends: [ "@remix-run/eslint-config", "@remix-run/eslint-config/node" ], rules: { // Regras personalizadas do projeto } };

Dica: Execute os testes antes de fazer commit para garantir a qualidade do código.

Configuração

Variáveis de Ambiente

Configure as variáveis no arquivo .env:

# APIs Externas
API_GARFO_URL=http://api.garfo.ameciclo.org
CMS_BASE_URL=http://do.strapi.ameciclo.org

# Mapbox
MAPBOX_ACCESS_TOKEN=pk.seu_token_aqui

# Google Calendar
GOOGLE_CALENDAR_API_KEY=sua_chave_aqui

# Analytics
GOOGLE_ANALYTICS_ID=G-PQNS7S7FD3

# Ambiente
NODE_ENV=development

Configurações Principais

Vite + Remix (vite.config.ts)

export default defineConfig({ plugins: [ remix({ presets: [vercelPreset()], future: { v3_singleFetch: false } }) ], build: { chunkSizeWarningLimit: 1200, rollupOptions: { output: { manualChunks: (id) => { if (id.includes('highcharts')) return 'highcharts'; if (id.includes('mapbox')) return 'mapbox'; if (id.includes('@fullcalendar')) return 'calendar'; } } } } });

Tailwind CSS (tailwind.config.ts)

module.exports = { content: ["./app/**/*.{ts,tsx}"], theme: { extend: { colors: { ameciclo: "#008080", ideciclo: "#5050aa" }, height: { cover: "52vh", "no-cover": "25vh" } } } };

TypeScript (tsconfig.json)

{ "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2022"], "jsx": "react-jsx", "module": "ESNext", "target": "ES2022", "strict": true, "baseUrl": ".", "paths": { "~/*": ["./app/*"] } } }

Soluções

Problemas Comuns

Erro: "Module not found" ou problemas com node_modules

Solução:

rm -rf node_modules package-lock.json && npm install

Limpa e reinstala todas as dependências.

Erro: "Port already in use" (porta 5173)

Solução:

lsof -ti:5173 | xargs kill -9

Mata o processo usando a porta.

Erro de API: "Failed to fetch", "504 Gateway Timeout" ou "ECONNREFUSED"

Causa: API externa indisponível ou timeout

Solução: O sistema já usa fetchWithTimeout com fallback automático. Verifique:

  • Status da API em /status (se disponível)
  • Logs do console para detalhes do erro
  • Dados de fallback em app/services/staticFallbacks.ts

Erro: "Mapbox token not found" ou mapa não carrega

Solução:

MAPBOX_ACCESS_TOKEN=pk.seu_token_aqui

Configure o token no .env. Obtenha em mapbox.com

Erro: TypeScript "Type error" ou "Cannot find module"

Solução:

npm run typecheck

Verifica erros de tipo. Use // @ts-ignore apenas em casos extremos.

Erro: Build falha com "Chunk size warning" ou "Out of memory"

Solução: Já configurado code splitting no vite.config.ts

NODE_OPTIONS="--max-old-space-size=4096" npm run build

Aumenta memória do Node se necessário.

Problema: Highcharts não renderiza ou erro "Highcharts is not defined"

Solução: Usar ClientOnly ou lazy loading

import { ClientOnly } from "remix-utils/client-only"; <ClientOnly>{() => <ChartComponent />}</ClientOnly>

Dica: Se o problema persistir, consulte os issues no GitHub ou abra um novo issue com detalhes do erro.

Deploy

Dependências Principais (40+ pacotes)

Framework e Core

  • @remix-run/* (v2.16.5) - Framework full-stack com SSR, file-based routing, loaders
  • @vercel/remix (v2.16.7) - Adapter para deploy na Vercel
  • react (v18.2.0) + react-dom - Biblioteca UI
  • typescript (v5.1.6) - Superset com tipagem estática
  • vite (v5.1.0) - Build tool rápido (HMR, ESM)

UI e Estilização

  • tailwindcss (v3.4.4) - Framework CSS utility-first
  • framer-motion (v11.18.0) - Animações declarativas
  • styled-components (v6.1.14) - CSS-in-JS
  • lucide-react (v0.545.0) - Ícones SVG

Visualização de Dados

  • highcharts (v12.2.0) + highcharts-react-official - Gráficos interativos
  • react-map-gl (v6.1.21) + mapbox-gl (v1.13.0) - Mapas interativos
  • react-google-charts (v5.2.1) - Gráficos Google Charts
  • @turf/* - Manipulação de dados geoespaciais

Funcionalidades Específicas

  • @fullcalendar/* (v6.1.x) - Calendário com Google Calendar
  • @tanstack/react-query (v5.90.7) - Cache e sincronização de dados
  • keen-slider (v6.3.3) - Carrossel/slider
  • swiper (v12.0.3) - Slider touch
  • react-markdown (v10.1.0) - Renderização Markdown
  • fuse.js (v7.1.0) - Busca fuzzy
  • match-sorter (v8.0.0) - Ordenação inteligente

Utilitários

  • react-spinners (v0.17.0) - Loading indicators
  • react-lazyload (v3.2.1) - Lazy loading
  • react-table (v7.8.0) - Tabelas avançadas
  • isbot (v4.1.0) - Detecção de bots

Build para produção

1. Gerar build otimizado:

npm run build

2. Iniciar servidor de produção:

npm start

Deploy: O projeto está configurado para deploy automático na Vercel via GitHub. Build otimizado com code splitting (Highcharts, Mapbox, Calendar em chunks separados).

Contribuição

Contribuições são sempre bem-vindas! Siga este guia para contribuir com o projeto:

Processo de Contribuição

1. Clone o Repositório

git clone https://github.com/Ameciclo/ameciclo.git
cd ameciclo

2. Crie uma branch

git checkout -b feature/nova-funcionalidade

Use nomes descritivos: feature/, fix/, docs/

3. Desenvolva e Teste

npm install
npm run dev
npm run lint
npm run typecheck

4. Commit e Push

git add .
git commit -m "feat: adiciona nova funcionalidade"
git push origin feature/nova-funcionalidade

5. Abra um Pull Request

Descreva claramente as alterações e inclua screenshots se necessário

Padrões de Código

Commits

Use conventional commits: feat:, fix:, docs:

TypeScript

Sempre tipifique variáveis e funções

Componentes

Use nomes descritivos e organize por funcionalidade

Tipos de Contribuição

Correção de Bugs

Identifique e corrija problemas existentes

Novas Funcionalidades

Adicione recursos que melhorem a plataforma

Documentação

Melhore ou adicione documentação

Design/UX

Melhore a interface e experiência do usuário

Dica: Consulte os issues abertos no GitHub para encontrar tarefas que precisam de ajuda.

Boas Práticas

Requisições de Dados

✓ Use Loaders para dados estáticos

Sempre busque dados no servidor usando loaders do Remix:

// app/loader/minha-pagina.ts export async function loader() { const data = await fetchWithTimeout( "http://api.garfo.ameciclo.org/endpoint", { cache: "no-cache" }, 5000, { fallback: [] } ); return { data }; }

✓ Centralize URLs em arquivos server

Mantenha URLs de APIs em arquivos de serviço:

// app/services/contagens.service.ts const API_BASE = "http://api.garfo.ameciclo.org"; export async function getContagens() { return fetchWithTimeout(`${API_BASE}/cyclist-counts`); }

Padrão de Cores Ameciclo

Cores Principais

ameciclo: #008080
text-ameciclo
ideciclo: #5050aa
text-ideciclo

Cores Sugeridas

  • Sucesso: green-500, green-600
  • Alerta: yellow-500, yellow-600
  • Erro: red-500, red-600
  • Info: blue-500, blue-600
  • Neutro: gray-500, gray-600

Padrões de Estilo

Cards

Padrão: bordas arredondadas, sombra sutil

className="rounded-lg shadow-md p-4 border"

Bordas

  • rounded - 4px (padrão)
  • rounded-lg - 8px (cards)
  • rounded-xl - 12px (destaque)

Sombras

  • shadow-sm - Sutil
  • shadow-md - Média (padrão cards)
  • shadow-lg - Destaque

Fontes

  • Família: Open Sans (font-custom)
  • Títulos: text-2xl, text-3xl, font-bold
  • Subtítulos: text-xl, font-semibold
  • Corpo: text-base, text-sm

Estrutura de Arquivos

Criando uma Nova Página

Use _index.tsx para rotas sem segmento adicional:

app/routes/minha-secao._index.tsx → /minha-secao

O _index indica que é a rota raiz daquele segmento.

Criando um Componente

  1. 1. Crie em app/components/[Secao]/
  2. 2. Use PascalCase: MeuComponente.tsx
  3. 3. Export default function
  4. 4. Tipagem com TypeScript

Organização de Pastas

  • components/Commom/ - Componentes globais reutilizáveis
  • components/[Secao]/ - Componentes específicos de seção
  • routes/ - Páginas (file-based routing)
  • loader/ - Funções de carregamento de dados
  • services/ - Lógica de negócio e APIs
  • hooks/ - Custom React hooks
  • contexts/ - React Context providers
  • utils/ - Funções utilitárias

Exemplo Completo

// app/routes/minha-pagina._index.tsx import { useLoaderData } from "@remix-run/react"; import { loader } from "~/loader/minha-pagina"; import Banner from "~/components/Commom/Banner"; export { loader }; export default function MinhaPagina() { const { data } = useLoaderData<typeof loader>(); return ( <> <Banner image="/banner.webp" alt="Minha Página" /> <div className="max-w-7xl mx-auto px-4 py-8"> <div className="rounded-lg shadow-md p-6 bg-white border"> <h1 className="text-3xl font-bold text-ameciclo mb-4"> Título </h1> {/* Conteúdo */} </div> </div> </> ); }