Communitygithub.com

0xDarkMatter/claude-mods

Padrões do framework Astro, arquitetura de ilhas, coleções de conteúdo, estratégias de renderização e implantação. Use para: astro, arquitetura de ilhas, coleções de conteúdo, astro cloudflare, transições de visualização, hidratação parcial, astrojs, SSG, SSR, renderização híbrida, adaptador astro.

O que é claude-mods?

Este skill fornece um guia abrangente e de referência rápida para o desenvolvimento com o framework Astro. Ele cobre os tópicos essenciais como arquitetura de ilhas (islands), coleções de conteúdo, estratégias de renderização (SSG, SSR, Híbrida), transições de visualização (View Transitions) e implantação em várias plataformas (Cloudflare, Vercel, Netlify, Node.js). É extremamente útil para desenvolvedores que desejam construir sites rápidos e modernos com Astro, oferecendo árvores de decisão práticas, exemplos de configuração, snippets de código e uma tabela de 'erros comuns' para depuração rápida. O conteúdo é direto e prático, servindo tanto como material de aprendizado quanto como um manual de consulta durante o desenvolvimento, e é compatível com as versões 4 e 5 do Astro.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0xDarkMatter/claude-mods/tree/HEAD/skills/astro-ops

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Operações Astro

Fatos verificados em 2026-07.

Padrões abrangentes para desenvolvimento com o framework Astro: arquitetura de ilhas, coleções de conteúdo, estratégias de renderização, transições de visualização e implantação em múltiplas plataformas.

Árvore de Decisão de Estratégia de Renderização

Qual estratégia de renderização?
│
├─ O conteúdo é principalmente estático (blog, docs, marketing)?
│  ├─ SIM → Ele muda menos de uma vez por dia?
│  │  ├─ SIM → SSG (output: 'static')
│  │  │        TTFB mais rápido, armazenável em cache CDN, custo de execução zero
│  │  └─ NÃO  → Híbrido (output: 'hybrid')
│  │           Padrão estático + SSR opcional por rota
│  └─ NÃO  → Cada página precisa de personalização?
│     ├─ SIM → SSR (output: 'server')
│     │        Dinâmico por requisição, ciente de autenticação, dados em tempo real
│     └─ NÃO  → Híbrido (output: 'hybrid')
│              Shell estático + ilhas de servidor para partes dinâmicas
│
├─ A aplicação precisa de interatividade em tempo real (dashboard, SPA)?
│  ├─ SIM → É uma SPA completa com roteamento no lado do cliente?
│  │  ├─ SIM → Considere uma SPA React/Vue, ou Astro + client:only
│  │  └─ NÃO  → Híbrido + arquitetura de ilhas
│  │           Ilhas interativas em páginas estáticas
│  └─ NÃO  → SSG (output: 'static')
│
├─ Preocupações com tempo de build (>10k páginas)?
│  ├─ SIM → Híbrido com renderização sob demanda
│  │        Pré-renderizar páginas populares, SSR para a cauda longa
│  └─ NÃO  → SSG lida bem
│
└─ Precisa de computação de borda (baixa latência global)?
   ├─ SIM → SSR + adaptador Cloudflare/Vercel Edge
   └─ NÃO  → SSR + adaptador Node ou SSG

Configuração

// astro.config.mjs
import { defineConfig } from 'astro/config';

// SSG (padrão) - todas as páginas pré-renderizadas no build
export default defineConfig({
  output: 'static',
});

// SSR - todas as páginas renderizadas na requisição
export default defineConfig({
  output: 'server',
  adapter: cloudflare(), // ou vercel(), netlify(), node()
});

// Híbrido - estático por padrão, SSR opt-in por página
export default defineConfig({
  output: 'hybrid',
  adapter: cloudflare(),
});
---
// No modo híbrido, opte POR NÃO pré-renderizar páginas específicas:
export const prerender = false;
// No modo SSR, opte POR pré-renderizar:
export const prerender = true;
---

Referência Rápida da Arquitetura de Ilhas

DiretivaHidrata QuandoJS EnviadoCaso de Uso
client:loadImediatamente ao carregar a páginaPacote completoInteratividade acima da dobra (nav, CTA do hero)
client:idleApós a página ficar ociosa (requestIdleCallback)Pacote completoInteratividade abaixo da dobra (formulário de comentário, chat)
client:visibleQuando rolado para a viewportPacote completoLonge no final da página (widget do rodapé, carrossel)
client:mediaQuando a media query correspondePacote completoNav apenas para mobile, componentes responsivos
client:only="react"Imediatamente, pula SSR completamentePacote completoComponentes que não podem fazer SSR (canvas, WebGL)
(nenhuma)Nunca - apenas HTML estáticoZero JSConteúdo estático, cards, cabeçalhos
---
import NavBar from '../components/NavBar.tsx';
import CommentForm from '../components/CommentForm.tsx';
import ImageCarousel from '../components/ImageCarousel.svelte';
import MobileMenu from '../components/MobileMenu.vue';
import ThreeScene from '../components/ThreeScene.tsx';
---

<!-- Carrega imediatamente - interatividade crítica -->
<NavBar client:load />

<!-- Carrega após a página ficar ociosa - não crítico -->
<CommentForm client:idle />

<!-- Carrega quando rolado para a viewport - lazy -->
<ImageCarousel client:visible />

<!-- Carrega apenas no mobile -->
<MobileMenu client:media="(max-width: 768px)" />

<!-- Apenas no cliente, sem SSR (WebGL não roda no servidor) -->
<ThreeScene client:only="react" />

Guia Rápido de Coleções de Conteúdo

Definir Schema

// src/content.config.ts (Astro 5) ou src/content/config.ts (Astro 4)
import { defineCollection, z, reference } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string().max(160),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: reference('authors'), // Referenciar outra coleção
  }),
});

const authors = defineCollection({
  loader: glob({ pattern: '**/*.json', base: './src/content/authors' }),
  schema: z.object({
    name: z.string(),
    avatar: z.string(),
    bio: z.string(),
    socials: z.object({
      twitter: z.string().optional(),
      github: z.string().optional(),
    }).optional(),
  }),
});

export const collections = { blog, authors };

Consultar Coleções

---
import { getCollection, getEntry } from 'astro:content';

// Obter todos os posts do blog que não são rascunho, ordenados por data
const posts = (await getCollection('blog', ({ data }) => !data.draft))
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());

// Obter uma única entrada
const post = await getEntry('blog', 'my-first-post');

// Resolver uma referência
const author = await getEntry(post.data.author);

// Renderizar conteúdo
const { Content, headings } = await post.render();
---

<Content />

Coleções de Conteúdo vs CMS Externo

CritérioColeções de ConteúdoCMS Externo (Payload, etc.)
Tipo de conteúdoMarkdown/MDX local, docs, blogsModelos de dados relacionais
AutoresDesenvolvedores (versionado)Editores (admin UI, autenticação multiusuário)
ValidaçãoType-safe via Zod no buildSchemas do CMS + contratos de API
Cadência de atualizaçãoImplanta com o siteIndependente de implantações
Necessidades de APINenhuma (consultas em tempo de build)REST/GraphQL para outros consumidores
Fluxo de trabalhoPRs Git, revisão simplesFluxos editoriais, rascunhos, papéis

Regra geral: comece com Coleções de Conteúdo; recorra a um CMS apenas quando não-desenvolvedores precisarem publicar sem uma implantação, ou quando o conteúdo for genuinamente relacional.

Referência de Estrutura do Projeto

project-root/
├── astro.config.mjs          # Configuração do Astro
├── tsconfig.json              # Configuração TypeScript (estende astro/tsconfigs)
├── package.json
├── public/                    # Assets estáticos (copiados como estão)
│   ├── favicon.svg
│   ├── robots.txt
│   └── og-image.png
├── src/
│   ├── pages/                 # Roteamento baseado em arquivos
│   │   ├── index.astro        # → /
│   │   ├── about.astro        # → /about
│   │   ├── blog/
│   │   │   ├── index.astro    # → /blog
│   │   │   └── [slug].astro   # → /blog/:slug (dinâmico)
│   │   ├── api/
│   │   │   └── search.ts      # → /api/search (endpoint de API)
│   │   └── [...slug].astro    # → catch-all/404
│   ├── layouts/
│   │   ├── BaseLayout.astro   # Shell HTML, <head>, estilos globais
│   │   └── BlogPost.astro     # Layout de post do blog
│   ├── components/
│   │   ├── Header.astro       # Componente Astro estático
│   │   ├── Footer.astro
│   │   ├── NavBar.tsx         # Ilha React
│   │   └── Counter.svelte     # Ilha Svelte
│   ├── content/               # Arquivos fonte das coleções de conteúdo
│   │   ├── blog/
│   │   │   ├── post-one.md
│   │   │   └── post-two.mdx
│   │   └── authors/
│   │       └── jane.json
│   ├── content.config.ts      # Schemas das coleções (Astro 5)
│   ├── middleware.ts           # Middleware de requisição/resposta
│   ├── styles/
│   │   └── global.css
│   └── lib/                   # Utilitários compartilhados
│       ├── utils.ts
│       └── constants.ts
└── .env                       # Variáveis de ambiente

Referência Rápida de Transições de Visualização

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

Diretivas de Transição

<!-- Persistir elemento entre páginas (mantém estado, evita re-renderização) -->
<audio transition:persist id="player">
  <source src="/music.mp3" />
</audio>

<!-- Transição nomeada para pareamento de animação -->
<img transition:name="hero" src={post.heroImage} />

<!-- Animação personalizada -->
<div transition:animate="slide">Conteúdo</div>
<div transition:animate="fade">Conteúdo</div>
<div transition:animate="none">Sem animação</div>

<!-- Persistir com nome (para múltiplos elementos persistentes) -->
<video transition:persist="media-player" />

Eventos do Ciclo de Vida

<script>
  document.addEventListener('astro:before-preparation', (e) => {
    // Antes da nova página ser buscada - cancelar navegação, mostrar carregamento
  });

  document.addEventListener('astro:after-preparation', (e) => {
    // Nova página buscada, antes da troca
  });

  document.addEventListener('astro:before-swap', (e) => {
    // Personalizar comportamento da troca de DOM
  });

  document.addEventListener('astro:after-swap', () => {
    // DOM atualizado - reinicializar scripts
  });

  document.addEventListener('astro:page-load', () => {
    // Página totalmente carregada (dispara na inicial e em cada navegação)
    // Use este evento em vez de DOMContentLoaded com View Transitions
  });
</script>

Manipulação de Voltar/Avançar

// astro.config.mjs
export default defineConfig({
  prefetch: {
    prefetchAll: true,         // Pré-buscar todos os links ao passar o mouse
    defaultStrategy: 'hover',  // 'hover' | 'tap' | 'viewport' | 'load'
  },
});
<!-- Controle de pré-busca por link -->
<a href="/about" data-astro-prefetch>Pré-buscar ao passar o mouse (padrão)</a>
<a href="/blog" data-astro-prefetch="viewport">Pré-buscar quando visível</a>
<a href="/contact" data-astro-prefetch="load">Pré-buscar imediatamente</a>
<a href="/external" data-astro-prefetch="false">Sem pré-busca</a>

Árvore de Decisão de Implantação

Onde implantar?
│
├─ Precisa de computação de borda + ecossistema Cloudflare (KV, D1, R2)?
│  └─ Cloudflare Pages/Workers
│     Adaptador: @astrojs/cloudflare
│     Melhor para: Borda global, Workers bindings, custo-benefício
│
├─ Precisa de serverless + ecossistema Vercel (ISR, analytics)?
│  └─ Vercel
│     Adaptador: @astrojs/vercel
│     Melhor para: Migração de Next.js, otimização de imagens, ISR
│
├─ Precisa de serverless + ecossistema Netlify (formulários, identidade)?
│  └─ Netlify
│     Adaptador: @astrojs/netlify
│     Melhor para: JAMstack, formulários integrados, testes A/B
│
├─ Precisa de controle total do servidor (Docker, runtime personalizado)?
│  └─ Node.js (autônomo ou Express/Fastify)
│     Adaptador: @astrojs/node
│     Melhor para: Auto-hospedado, WebSocket, processos de longa duração
│
└─ Site puramente estático (sem necessidade de SSR)?
   └─ Qualquer host estático (GitHub Pages, S3, Cloudflare Pages)
      Nenhum adaptador necessário, output: 'static'
      Melhor para: Blogs, docs, sites de marketing

Instalação do Adaptador

# Cloudflare
npx astro add cloudflare

# Vercel
npx astro add vercel

# Netlify
npx astro add netlify

# Node.js
npx astro add node

Pegadas Comuns

PegadaPor quêCorreção
Erros de incompatibilidade de hidrataçãoHTML do servidor difere da renderização do cliente (datas, IDs aleatórios, APIs do navegador)Use client:only para componentes que dependem do navegador, ou garanta renderização determinística
import.meta.env indefinido no clienteApenas variáveis prefixadas com PUBLIC_ são expostas ao código do lado do clienteRenomeie para PUBLIC_MY_VAR ou passe via props do servidor
Rotas dinâmicas 404 no SSGgetStaticPaths() não retornando todos os parâmetros possíveisGaranta que getStaticPaths() retorne todos os caminhos válidos, ou mude para híbrido/SSR
Imagens não otimizandoUsando <img> em vez do componente <Image /> do AstroImporte de astro:assets: import { Image } from 'astro:assets' e use imports locais para src
SSR falha sem adaptadoroutput: 'server' ou 'hybrid' requer um adaptador de implantaçãoInstale o adaptador: npx astro add cloudflare (ou vercel, netlify, node)
Componentes MDX não renderizandoComponentes personalizados não passados para o conteúdo MDXPasse componentes via <Content components={{ MyComponent }} /> ou use a configuração MDX em astro.config.mjs
Alterações no schema da coleção de conteúdo não refletidasGeração de tipos está em cache, tipos .astro desatualizadosExecute astro sync para regenerar tipos, reinicie o servidor de desenvolvimento
client:* em componentes AstroDiretivas de cliente só funcionam em componentes de framework (React, Vue, Svelte)Componentes Astro são apenas estáticos; extraia partes interativas para um componente de framework
document / window não está definidoCódigo do lado do servidor não pode acessar globais do navegadorProteja com if (typeof window !== 'undefined') ou mova para client:only
Estilos vazando entre componentesUsando CSS global em vez de estilos com escopoUse <style> (com escopo por padrão em .astro) ou <style is:global> intencionalmente
Transições de Visualização quebram scriptsDOMContentLoaded só dispara uma vez com View TransitionsUse o evento astro:page-load, que dispara em cada navegação
Variáveis de ambiente ausentes em produção.env não carregado ou variáveis de ambiente da plataforma não configuradasUse envField no astro.config.mjs para validação; defina variáveis no painel da plataforma

Checklist de Segurança para Produção

Para cada implantação em produção, aborde:

  • Headers CSP - configure uma Content-Security-Policy restritiva (veja padrões de middleware em references/deployment.md)
  • Restrições de imagem remota - imponha listas de permissão explícitas de image.domains / remotePatterns; nunca derive URLs de imagem de entrada do usuário (risco de SSRF)
  • Validação do header Host - verifique se o host da requisição corresponde aos domínios esperados no middleware (apenas SSR/híbrido)
  • Gerenciamento de segredos - no Cloudflare, use Workers Bindings (wrangler secret put), não variáveis de ambiente embutidas no código; em outras plataformas, use armazenamentos de segredos da plataforma
  • Apenas HTTPS - garanta que todos os recursos externos (scripts, imagens, fontes) carreguem sobre HTTPS
  • Validação de entrada - sanitize toda entrada do usuário em contextos SSR (parâmetros de query, corpos de formulário, cookies)

Arquivos de Referência

ArquivoConteúdoLinhas
references/content-collections.mdPadrões de schema, tipos Zod, consultas, MDX, camada de conteúdo, migrações~500
`references/islands-rendering

...

[truncado para tradução]

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Habilidades Relacionadas