Caso de Estudo // 04

Construído de Raiz.

Um website bilingue em Next.js com backoffice CMS próprio, desenvolvido a solo desde a arquitetura até ao lançamento.

Next.jsTypeScriptTailwind CSSSupabaseFramer MotionVercel
FunçãoProgramador Full-Stack
Duração2025–2026
ClienteCalliope
Site Onlinecalliope.pt
Sobre o projeto

Visão Geral

A Calliope é uma agência criativa digital com sede no Algarve, Portugal, especializada em cobertura de eventos, criação de podcasts e gestão de redes sociais. Desenhei e construí o website a solo: um site público completamente bilingue em inglês e português, mais um backoffice protegido para a equipa publicar e editar artigos de blog sem depender de um CMS externo. Sem Contentful, sem Sanity, sem WordPress. O cliente é dono de tudo.

Desafios

O Desafio

SEO bilingue, não apenas um seletor de idioma

Um simples seletor de idioma não era suficiente. Tanto a versão inglesa como a portuguesa do site precisavam de ser completamente indexadas pelos motores de busca, com canonical URLs corretos, alternativas hreflang e metadados Open Graph específicos por locale. Qualquer atalho aqui significaria que a versão portuguesa simplesmente não existia para efeitos de pesquisa.

Um CMS que pertence ao cliente

A agência queria controlo total sobre o conteúdo. Sem licenças externas, sem dependência de plataformas de terceiros. Isso implicou construir um backoffice próprio onde a equipa pudesse redigir, editar e publicar artigos de blog diretamente, dentro da mesma base de código Next.js que o site público.

Animações que justificam a sua presença

A Calliope vende serviços criativos, por isso o site é parte da sua proposta. As animações tinham de parecer pensadas e intencionais, não decorativas. E não podiam prejudicar os scores de performance.

Uma stack completamente autónoma

Sem CMS externo, sem fornecedor de autenticação externo. A stack tinha de ser simples, de propriedade do cliente e operável por uma equipa pequena sem conhecimentos técnicos, sem overhead de manutenção contínua.

A Solução

Construí o site inteiro em Next.js 16 com App Router, usando um sistema de i18n próprio, um CMS com base em Supabase, e uma única função de middleware a gerir tanto a deteção de locale como a autenticação. Cada rota pública é uma página distinta renderizada no servidor com os seus próprios metadados. Cada rota do backoffice é protegida na edge. O formulário de contacto usa Server Actions e honeypot, com envio via Resend. A analytics corre através de uma instância Umami self-hosted incorporada no dashboard do backoffice.

Stack

Stack Tecnológica

Framework da Aplicação

Next.js 16

App Router com Server Components por rota. As Server Actions substituíram uma camada API separada para o formulário de contacto e o CRUD do blog. Cada rota pública gera os seus próprios metadados, canonical URL e hreflang de forma independente.

Segurança de Tipos

TypeScript

Modo strict em todo o projeto. Mais útil na camada de dicionários i18n, onde chaves de tradução em falta são fáceis de ignorar em runtime, mas são apanhadas em build time com a tipagem correta.

Estilização

Tailwind CSS 4

Estilização utility-first com uma biblioteca de componentes própria em components/ui/. Um helper cn() gere a combinação condicional de classes em todo o projeto. Sem estilos inline, sem CSS modules.

Base de Dados e Autenticação

Supabase

PostgreSQL, Auth e gestão de sessão SSR num único serviço. O cliente compatível com SSR mantém o estado da sessão consistente entre servidor e cliente. A renovação da sessão ocorre no middleware antes de qualquer Server Component ler dados do utilizador.

Animações

Framer Motion

Usado para o ecrã de entrada, sequências de hero, indicadores de scroll e microinterações de hover. Todos os elementos animados são Client Components. Os Server Components mantêm-se completamente estáticos.

Deployment

Vercel

Deployment sem configuração para Next.js. O middleware de edge corre o routing i18n e a autenticação numa única passagem na camada CDN, antes de qualquer Server Component ser executado.

Funcionalidades

Funcionalidades em Destaque

01

i18n Bilingue com Persistência via Cookie

Todas as strings visíveis ao utilizador vêm de ficheiros de dicionário TypeScript, sem texto fixo em nenhum componente do site público. Quando o utilizador muda de idioma, é definido um cookie NEXT_LOCALE que o middleware lê em cada pedido seguinte. Os visitantes recorrentes chegam sempre ao idioma que escolheram, sem necessidade de hidratação no cliente.

02

Backoffice CMS Próprio

O backoffice em /backoffice/ é uma aplicação Next.js protegida dentro da mesma base de código. O middleware bloqueia o acesso não autenticado na edge. Dentro do backoffice, a equipa pode criar, editar e publicar artigos armazenados no Supabase, e consultar as analytics do Umami sem sair do dashboard.

03

Ecrã de Entrada com Lógica de Exibição Única

A animação do ecrã de entrada executa uma vez por sessão de browser, usando sessionStorage. Na primeira visita, a sequência completa corre: revelação do logo, fade escalonado, transição de saída. Nas navegações seguintes, é ignorado. A primeira impressão mantém-se sem incomodar quem já conhece o site.

04

Formulário de Contacto com Proteção Honeypot

O formulário de contacto envia via uma Server Action que chama o Resend para entrega de email. Antes de enviar, a ação verifica um campo honeypot oculto, invisível para utilizadores reais mas preenchido pela maioria dos bots. Sem CAPTCHA, sem fricção para utilizadores legítimos. O spam é descartado silenciosamente no servidor.

Decisões

Decisões de Arquitetura

01

i18n próprio em vez de next-intl

Construí a camada i18n de raiz em vez de usar next-intl. O projeto tem dois locales estáticos, sem complexidade de pluralização e sem ICU messages. Um dicionário TypeScript com uma função t() e um contexto LanguageProvider cobriu tudo o que era necessário, com zero overhead em runtime e chaves tipadas que detetam traduções em falta em build time, em vez de falharem silenciosamente em produção.

02

Middleware único para i18n e autenticação

O ficheiro middleware.ts tem dois trabalhos: deteção de locale e reescrita de rotas públicas, e verificação de sessão Supabase para rotas do backoffice. Correr ambos numa única função de middleware significa uma execução na edge por pedido. A lógica ramifica pelo pathname: pedidos para /backoffice/** atingem o guard de autenticação, tudo o resto atinge o resolver de locale.

03

Server Actions em vez de rotas API

O envio do formulário de contacto e todas as operações de artigos de blog (criar, atualizar, publicar, eliminar) são geridos por Server Actions em app/actions/. Isto eliminou a necessidade de uma camada API dedicada. Cada ação verifica a sessão Supabase no servidor. O estado de autenticação no cliente nunca é confiado para operações privilegiadas.

Resultados

Resultados

O site está online em calliope.pt, completamente bilingue com hreflang correto, canonical URLs e metadados Open Graph em todas as rotas públicas.

A equipa consegue publicar conteúdo de blog via backoffice sem envolver o programador.

As analytics estão visíveis dentro do dashboard do backoffice através da integração Umami incorporada.

Os Core Web Vitals são sólidos. Os Server Components e next/image mantêm o LCP baixo, sem layout shifts causados pela camada de animações.

Cronologia

Fase 01: Fundação

Arquitetura e Configuração

Estruturação com App Router, sistema i18n próprio, cliente SSR do Supabase, middleware para deteção de locale e autenticação, e estrutura de routing dinâmico [lang].

Fase 02: Desenvolvimento

Site Público e Backoffice CMS

Implementação completa do site público incluindo animações, formulário de contacto com Resend, conteúdo bilingue, e o backoffice completo com CRUD de blog, autenticação Supabase e analytics Umami.

Fase 03: Lançamento

Polimento e Deployment

Otimização de performance, metadados SEO para todas as rotas, proteção honeypot contra spam, persistência de locale via cookie, e deployment para Vercel com domínio personalizado em calliope.pt.

Tem um projeto? Vamos falar.