5  Estilos e Imagens

Nos capítulos anteriores, criamos a estrutura do site da TED System: as páginas com HTML semântico, os componentes de cabeçalho e rodapé, o layout base e o arquivo de dados da empresa. O resultado é funcional, mas ainda não tem estilo visual: as páginas aparecem apenas com a formatação padrão do navegador.

Neste capítulo, vamos aplicar o design do site usando CSS. Você vai aprender a criar um arquivo de estilos globais com variáveis CSS, a estilizar cada componente com estilos isolados (com escopo) e a destacar no menu a página que o visitante está acessando. Também vamos incluir as imagens do projeto e criar um componente de cartão para organizar o conteúdo das páginas.

5.1 Nivelamento de CSS

Antes de aplicarmos os estilos, é importante revisar alguns conceitos que usaremos no projeto:

Seletores e Variáveis Usaremos seletores CSS fundamentais para estruturar o layout. O seletor universal (*) permite aplicar regras a todos os elementos, como a remoção de margens padrão. Os seletores de classe (como .nome-da-classe) serão essenciais para estilizar componentes específicos de forma modular. Além disso, utilizaremos variáveis CSS (custom properties) para armazenar valores reutilizáveis. Definidas com dois traços (como --cor-primaria), elas garantem que, se precisarmos mudar a cor principal do site, a alteração em um único lugar refletirá globalmente. Saiba mais sobre Seletores CSS no MDN.

Unidades No dimensionamento de textos e espaçamentos, utilizaremos a unidade rem, que é relativa ao tamanho da fonte raiz do documento, garantindo maior acessibilidade ao respeitar as preferências do navegador. Para alturas baseadas na tela, utilizaremos vh, onde 1vh equivale a 1% da altura da janela de visualização (viewport). Mais detalhes em Valores e unidades CSS no MDN.

Flexbox O Flexbox nos ajudará a alinhar e distribuir elementos em uma dimensão. Com a propriedade display: flex, transformamos um elemento em um contêiner flexível. A propriedade flex-direction define a direção dos itens (linha ou coluna), e a propriedade flex-wrap: wrap permite a quebra de linha automática quando não há espaço horizontal suficiente. Confira mais sobre Flexbox no MDN. A estrutura CSS equivalente utiliza display: flex;, flex-direction: row; e flex-wrap: wrap;.

CSS Grid O CSS Grid será utilizado para alinhar elementos em duas dimensões (linhas e colunas). Com display: grid, habilitamos o sistema de grade em um contêiner. Usando a propriedade grid-template-columns, definimos como as colunas serão distribuídas. Aproveitaremos a unidade fracionária fr, que representa uma fração do espaço disponível no grid, para distribuir o espaço proporcionalmente sem necessidade de porcentagens precisas. Detalhes em CSS Grid Layout no MDN. A estrutura CSS equivalente utiliza display: grid; e grid-template-columns: 1fr 1fr; (duas colunas com larguras iguais).

5.2 Estilos Globais

O primeiro passo é criar um arquivo CSS centralizado com as cores, os degradês e os estilos que valem para o site inteiro. No Astro, esse arquivo fica em src/styles/ e é importado no layout base.

  1. No Explorador de Arquivos, passe o mouse sobre a pasta src/ e clique no ícone de Nova Pasta (New Folder).
  2. Escreva o nome styles e pressione Enter.
  3. Passe o mouse sobre a nova pasta src/styles/ e clique no ícone de Novo Arquivo (New File).
  4. Escreva global.css, pressione Enter e adicione o seguinte código:
src/styles/global.css
/* src/styles/global.css */
:root {
  /* Paleta de Cores baseada no Logo */
  --cor-primaria: #0d1520; /* Quase preto */
  --cor-secundaria: #0058ec; /* Azul escuro do degradê (links e botões) */
  --cor-destaque: #008ff7; /* Azul claro do degradê (decorativo) */
  --cor-fundo: #ffffff;
  --cor-fundo-alt: #f8fafc;
  --cor-texto: #334155;
  --cor-texto-secundario: #64748b;
  --cor-texto-invertido: #ffffff;
  --cor-texto-invertido-suave: #cbd5e1;
  --cor-borda: #e2e8f0;
  
  /* Variável para o Hero */
  --cor-hero-fim: #00224d;

  /* Degradês Globais */
  --degrade-marca: linear-gradient(
    90deg,
    var(--cor-secundaria),
    var(--cor-destaque)
  );
  --degrade-hero: linear-gradient(
    135deg,
    var(--cor-primaria),
    var(--cor-hero-fim)
  );

  /* Variáveis Estruturais */
  --borda-raio: 8px;
  --sombra-cartao: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
  --sombra-cartao-hover: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --espacamento-secao: 4rem;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    Oxygen, Ubuntu, Cantarell, sans-serif;
  background-color: var(--cor-fundo);
  color: var(--cor-texto);
  line-height: 1.6;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

main {
  flex: 1;
}

h1, h2, h3 {
  color: var(--cor-primaria);
  line-height: 1.2;
  margin-bottom: 1rem;
}

p {
  margin-bottom: 1rem;
}

a {
  color: var(--cor-secundaria);
  text-decoration: none;
  transition: color 0.2s ease;
}

a:hover {
  text-decoration: underline;
}

.container {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 1.5rem;
}

.secao {
  padding: var(--espacamento-secao) 0;
}

.secao-alt {
  background-color: var(--cor-fundo-alt);
}

.btn {
  display: inline-block;
  background-color: var(--cor-secundaria);
  color: var(--cor-texto-invertido);
  padding: 0.75rem 1.5rem;
  border-radius: var(--borda-raio);
  font-weight: 600;
  text-align: center;
  transition: background-color 0.2s ease;
}

.btn:hover {
  background-color: var(--cor-primaria);
  text-decoration: none;
}

Esse arquivo é extenso, mas cada parte tem uma função clara. Vamos entender por partes.

Variáveis CSS em :root — O bloco :root define variáveis CSS (também chamadas de custom properties) que ficam disponíveis em qualquer lugar do site. O nome de cada variável começa com -- e descreve a sua função: --cor-primaria é a cor principal (quase preta), --cor-secundaria é o azul dos links e botões, --cor-destaque é o azul claro usado apenas nos degradês decorativos. As variáveis de texto invertido (--cor-texto-invertido e --cor-texto-invertido-suave) são usadas em áreas com fundo escuro, como o cabeçalho hero e o rodapé. Para usar uma variável, escrevemos var(--nome-da-variavel). A vantagem é que, se você quiser trocar uma cor, basta alterar o valor aqui e a mudança se reflete em todo o site. Para saber mais sobre variáveis CSS, consulte a documentação do MDN: Using CSS custom properties.

Dica

Variáveis CSS facilitam a manutenção As variáveis declaradas no bloco :root ficam acessíveis em qualquer componente. Se for necessário mudar a paleta de cores no futuro, basta alterar o valor no global.css para que o site inteiro seja atualizado.

Reset e estilos base — O seletor * (que seleciona todos os elementos) aplica box-sizing: border-box e zera as margens e os espaçamentos padrão do navegador, criando uma base uniforme. O body define a fonte do sistema, a cor do texto e do fundo, o espaçamento entre linhas (line-height: 1.6) e usa Flexbox na direção vertical (flex-direction: column) com min-height: 100vh para que o rodapé fique sempre no final da página, mesmo quando o conteúdo é curto. O main recebe flex: 1 para ocupar todo o espaço disponível entre o cabeçalho e o rodapé.

Nota

Por que zerar as margens? Cada navegador (Chrome, Firefox, Safari) possui margens e espaçamentos padrão ligeiramente diferentes. A regra * { margin: 0; padding: 0; } limpa essas diferenças e garante que o layout se comporte de maneira idêntica em todos eles.

Tipografia e links — Os títulos h1, h2 e h3 recebem a cor primária e um espaçamento abaixo. Os links ficam com a cor secundária (azul), sem sublinhado, e ganham sublinhado ao passar o mouse.

Classes utilitárias — A classe .container centraliza o conteúdo com largura máxima de 960 pixels e espaçamento lateral. A classe .secao adiciona o espaçamento vertical entre as seções, e .secao-alt aplica um fundo alternativo mais claro. A classe .btn cria o estilo do botão com fundo azul, texto branco, cantos arredondados e uma transição suave ao passar o mouse.

5.2.1 Importando o CSS no Layout

Para que os estilos globais se apliquem a todas as páginas, precisamos importar o arquivo no layout. Abra o arquivo src/layouts/LayoutBase.astro e adicione a linha de importação no frontmatter:

src/layouts/LayoutBase.astro
---
import '../styles/global.css';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import { empresa } from '../data/empresa.js';

const { titulo, descricao } = Astro.props;
---

<html lang="pt-BR">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<meta name="description" content={descricao} />
		<title>{titulo} | {empresa.nome}</title>
	</head>
	<body>
		<Header />
		<main>
			<slot />
		</main>
		<Footer />
	</body>
</html>

A primeira linha do frontmatter (import '../styles/global.css') carrega o arquivo CSS global. O Astro inclui automaticamente os estilos no HTML de todas as páginas que usam esse layout. Essa é a forma recomendada de aplicar estilos que valem para o site inteiro. Para saber mais sobre estilos no Astro, consulte a documentação oficial: Styles & CSS.

5.3 Imagens do Projeto

O site da TED System usa quatro imagens: o logotipo da empresa e as fotos dos três membros da equipe. Essas imagens ficam na pasta public/images/, de onde o Astro as serve diretamente na raiz do site.

Baixe o arquivo ted-system-imagens.zip e extraia, no seu computador, as quatro imagens: logo.png, wanderson.jpg, sheldon.jpg e amy.jpg.

No Explorador de Arquivos do Codespaces, passe o mouse sobre a pasta public/ e clique no ícone de Nova Pasta (New Folder). Escreva images e pressione Enter. Depois, arraste as quatro imagens extraídas para dentro da pasta public/images/.

5.3.1 Referenciando Imagens Públicas

Arquivos na pasta public/ são servidos na raiz do site. Para referenciá-los no código, o caminho sempre começa com uma barra / e nunca inclui a palavra “public”. Por exemplo:

  • Correto: /images/logo.png
  • Incorreto: public/images/logo.png

5.4 Componente Cartão

Antes de estilizar as páginas, vamos criar o componente Cartao.astro. Ele será usado em todas as páginas: no público-alvo da página inicial, na missão, na visão e nos valores da página Sobre, nos membros da equipe e nos canais de contato.

No Explorador de Arquivos, passe o mouse sobre a pasta src/components/ e clique no ícone de Novo Arquivo (New File). Escreva Cartao.astro, pressione Enter e adicione o seguinte código:

src/components/Cartao.astro
---
const { destaque } = Astro.props;
---

<div class:list={['cartao', { 'cartao-destaque': destaque }]}>
	<slot />
</div>

<style>
	.cartao {
		background-color: var(--cor-fundo);
		border: 1px solid var(--cor-borda);
		border-radius: var(--borda-raio);
		padding: 2rem;
		box-shadow: var(--sombra-cartao);
		transition: transform 0.2s ease, box-shadow 0.2s ease;
	}

	.cartao:hover {
		transform: translateY(-4px);
		box-shadow: var(--sombra-cartao-hover);
	}

	.cartao-destaque {
		border-top: 4px solid var(--cor-secundaria);
	}
</style>

O componente recebe uma prop opcional chamada destaque. Quando destaque é verdadeiro, o cartão ganha uma faixa azul no topo (a classe cartao-destaque).

A diretiva class:list do Astro recebe um array e monta a lista de classes CSS dinamicamente. A classe cartao é aplicada sempre; a classe cartao-destaque é adicionada apenas quando a condição destaque é verdadeira. Essa é a forma recomendada pelo Astro para aplicar classes condicionais: class:list.

O bloco <style> dentro do componente cria estilos com escopo (scoped styles). Isso significa que as regras CSS definidas aqui valem apenas para o HTML deste componente, sem afetar o restante do site. Mesmo que outra parte do site use uma classe com o mesmo nome, os estilos não entram em conflito. Repare que os estilos com escopo podem usar as variáveis definidas no global.css (como var(--cor-fundo) e var(--sombra-cartao)), porque as variáveis CSS são globais por natureza.

A tag <slot /> funciona como um espaço reservado: o conteúdo que for passado entre as tags <Cartao> e </Cartao> aparece no lugar do slot. Já aprendemos sobre props e slots no capítulo anterior, quando criamos o LayoutBase.

5.5 Cabeçalho com Menu Ativo

Agora vamos atualizar o Header.astro para incluir o logotipo, o menu de navegação com destaque na página atual e os estilos visuais.

Abra o arquivo src/components/Header.astro e substitua o conteúdo inteiro pelo código abaixo:

src/components/Header.astro
---
import { empresa } from '../data/empresa.js';

const currentPath = Astro.url.pathname;
---

<header class="cabecalho">
	<div class="faixa-topo"></div>
	<div class="container cabecalho-container">
		<a href="/">
			<img 
				src="/images/logo.png" 
				alt={`Logotipo da ${empresa.nome}`} 
				class="logo-img" 
			/>
		</a>

		<nav class="navegacao">
			<ul>
				<li><a href="/" class:list={[{ ativo: currentPath === '/' }]}>Início</a></li>
				<li><a href="/sobre" class:list={[{ ativo: currentPath.startsWith('/sobre') }]}>Sobre</a></li>
				<li><a href="/equipe" class:list={[{ ativo: currentPath.startsWith('/equipe') }]}>Equipe</a></li>
				<li><a href="/contato" class:list={[{ ativo: currentPath.startsWith('/contato') }]}>Contato</a></li>
			</ul>
		</nav>
	</div>
</header>

<style>
	.faixa-topo {
		height: 4px;
		background: var(--degrade-marca);
	}

	.cabecalho {
		background-color: var(--cor-fundo);
		border-bottom: 1px solid var(--cor-borda);
		padding-bottom: 1rem;
	}

	.cabecalho-container {
		display: flex;
		justify-content: space-between;
		align-items: center;
		flex-wrap: wrap;
		gap: 1.5rem;
		padding-top: 1.5rem;
	}

	.logo-img {
		height: 60px; /* Logo maior e legível */
		width: auto;
		display: block;
	}

	.navegacao ul {
		list-style: none;
		display: flex;
		flex-wrap: wrap;
		gap: 1.5rem;
	}

	.navegacao a {
		font-weight: 600;
		color: var(--cor-texto);
		padding: 0.5rem 0;
		border-bottom: 2px solid transparent;
	}

	.navegacao a:hover,
	.navegacao a.ativo {
		color: var(--cor-secundaria);
		border-bottom-color: var(--cor-secundaria);
		text-decoration: none;
	}
</style>

Vamos entender as novidades deste arquivo.

Menu ativo — No frontmatter, a linha const currentPath = Astro.url.pathname obtém o endereço da página atual. O objeto Astro.url é fornecido pelo framework e contém informações sobre a URL da página que está sendo construída. A propriedade pathname retorna apenas o caminho, sem o domínio — por exemplo, /sobre ou /contato.

Para a página inicial, usamos a comparação exata currentPath === '/', porque o endereço é sempre /. Para as outras páginas, usamos currentPath.startsWith('/sobre') em vez de currentPath === '/sobre'. O método startsWith verifica se o endereço começa com o texto informado. Isso é necessário porque o mesmo endereço pode aparecer com ou sem barra no final: durante o desenvolvimento, você acessa /sobre; já no site gerado pelo npm run build, cada página fica numa pasta própria (dist/sobre/index.html), e o endereço pode terminar com barra (/sobre/). Com startsWith, o link fica destacado nos dois casos.

Quando a condição é verdadeira, o Astro adiciona a classe ativo ao link. No CSS, a regra .navegacao a.ativo aplica a cor azul e a borda inferior, criando o destaque visual. Para saber mais, consulte a documentação do Astro.url e do método startsWith.

Estilos com escopo — O <style> do cabeçalho usa Flexbox para posicionar o logo à esquerda e o menu à direita. A propriedade flex-wrap: wrap faz o menu descer para uma nova linha quando a tela é estreita (como em celulares), sem precisar de uma media query separada. A faixa colorida no topo usa o degradê da marca (--degrade-marca) definido no global.css. Para saber mais sobre Flexbox, consulte o guia do MDN: Conceitos básicos do Flexbox.

5.6 Estilos do Rodapé

Abra o arquivo src/components/Footer.astro e substitua o conteúdo inteiro pelo código abaixo:

src/components/Footer.astro
---
import { empresa } from '../data/empresa.js';

const anoAtual = new Date().getFullYear();
---

<footer class="rodape">
	<div class="faixa-topo"></div>
	<div class="container rodape-container">
		<div class="rodape-marca">
			<h2>{empresa.nome}</h2>
			<p>&copy; {anoAtual} {empresa.nome}. Todos os direitos reservados.</p>
		</div>

		<div class="rodape-contato">
			<p>{empresa.contato.endereco.logradouro}</p>
			<p>{empresa.contato.endereco.complemento} - {empresa.contato.endereco.bairro}</p>
			<p>{empresa.contato.endereco.cidadeEstado} - CEP: {empresa.contato.endereco.cep}</p>
			<div class="links-contato">
				<a href={`mailto:${empresa.contato.email}`}>{empresa.contato.email}</a>
				<p>{empresa.contato.telefone}</p>
			</div>
		</div>
	</div>
</footer>

<style>
	.rodape {
		background-color: var(--cor-primaria);
		color: var(--cor-texto-invertido);
		margin-top: auto;
	}

	.faixa-topo {
		height: 4px;
		background: var(--degrade-marca);
	}

	.rodape-container {
		display: flex;
		flex-wrap: wrap;
		justify-content: space-between;
		gap: 2rem;
		padding: 3rem 1.5rem;
	}

	.rodape-marca h2 {
		color: var(--cor-texto-invertido);
		margin-bottom: 0.5rem;
	}

	.rodape-marca p, .rodape-contato p {
		color: var(--cor-texto-invertido-suave);
		font-size: 0.875rem;
		margin-bottom: 0.25rem;
	}

	.links-contato {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
		margin-top: 1rem;
	}

	.links-contato a {
		color: var(--cor-texto-invertido);
		text-decoration: none;
	}

	.links-contato a:hover {
		color: var(--cor-texto-invertido-suave);
		text-decoration: underline;
	}
</style>

O rodapé tem fundo escuro (--cor-primaria) e usa as variáveis de texto invertido para garantir a legibilidade. O Flexbox com flex-wrap: wrap organiza a marca à esquerda e o contato à direita, e adapta o layout automaticamente em telas menores. A faixa colorida no topo segue o mesmo padrão do cabeçalho.

5.7 Páginas Estilizadas

Agora vamos atualizar cada página para usar as classes CSS, o componente Cartao e os estilos com escopo.

5.7.1 Página Inicial

Abra o arquivo src/pages/index.astro e substitua o conteúdo inteiro pelo código abaixo:

src/pages/index.astro
---
import LayoutBase from '../layouts/LayoutBase.astro';
import Cartao from '../components/Cartao.astro';
import { empresa } from '../data/empresa.js';
---

<LayoutBase 
	titulo="Início" 
	descricao={`Bem-vindo à ${empresa.nome}. ${empresa.slogan}`}
>
	<section class="hero">
		<div class="container">
			<h1>{empresa.slogan}</h1>
			<p class="hero-descricao">{empresa.missao}</p>
			<a href="/contato" class="btn">Fale Conosco</a>
		</div>
	</section>

	<section class="publico secao secao-alt">
		<div class="container">
			<h2>Quem Atendemos</h2>
			<Cartao>
				<p class="texto-publico">{empresa.publicoAlvo}</p>
			</Cartao>
		</div>
	</section>
</LayoutBase>

<style>
	.hero {
		background: var(--degrade-hero);
		color: var(--cor-texto-invertido);
		padding: 6rem 0;
	}

	.hero h1 {
		color: var(--cor-texto-invertido);
		font-size: 3rem;
		margin-bottom: 1.5rem;
		line-height: 1.1;
		max-width: 800px;
	}

	.hero-descricao {
		font-size: 1.25rem;
		max-width: 700px;
		margin-bottom: 2rem;
		color: var(--cor-texto-invertido-suave);
	}

	.publico h2 {
		margin-bottom: 2rem;
	}

	.texto-publico {
		margin-bottom: 0;
		font-size: 1.125rem;
		color: var(--cor-texto-secundario);
	}
</style>

A página inicial tem duas seções. A primeira é o hero: uma área de destaque com o degradê escuro (--degrade-hero), o slogan da empresa como título, a missão como texto de apoio e um botão que leva à página de contato. A segunda seção mostra o público-alvo dentro de um Cartao, com fundo alternativo (.secao-alt).

Note que a página importa o componente Cartao no frontmatter e o usa como uma tag HTML. O texto do público-alvo é passado entre <Cartao> e </Cartao>, e aparece no lugar do <slot /> do componente.

5.7.2 Página Sobre

Abra o arquivo src/pages/sobre.astro e substitua o conteúdo inteiro pelo código abaixo:

src/pages/sobre.astro
---
import LayoutBase from '../layouts/LayoutBase.astro';
import Cartao from '../components/Cartao.astro';
import { empresa } from '../data/empresa.js';
---

<LayoutBase 
	titulo="Sobre Nós" 
	descricao={`Conheça a história e os valores da ${empresa.nome}.`}
>
	<section class="secao">
		<div class="container">
			<div class="bloco-texto">
				<h1>Sobre a {empresa.nome}</h1>
				<p class="historia">{empresa.historia}</p>
			</div>

			<div class="grid-missao-visao">
				<Cartao>
					<h2>Missão</h2>
					<p>{empresa.missao}</p>
				</Cartao>
				
				<Cartao>
					<h2>Visão</h2>
					<p>{empresa.visao}</p>
				</Cartao>
			</div>
		</div>
	</section>

	<section class="secao secao-alt">
		<div class="container">
			<h2 class="titulo-valores">Nossos Valores</h2>
			<div class="grid-valores">
				{empresa.valores.map((valor) => (
					<Cartao destaque={true}>
						<h3>{valor.titulo}</h3>
						<p>{valor.descricao}</p>
					</Cartao>
				))}
			</div>
		</div>
	</section>
</LayoutBase>

<style>
	.bloco-texto {
		margin-bottom: 3rem;
		max-width: 800px;
	}

	.historia {
		font-size: 1.125rem;
		color: var(--cor-texto-secundario);
	}

	.grid-missao-visao {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
		gap: 2rem;
	}

	.grid-missao-visao h2 {
		font-size: 1.5rem;
		color: var(--cor-secundaria);
	}

	.titulo-valores {
		margin-bottom: 2rem;
	}

	.grid-valores {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
		gap: 2rem;
	}

	.grid-valores h3 {
		font-size: 1.25rem;
		color: var(--cor-secundaria);
		margin-bottom: 0.5rem;
	}
</style>

A página Sobre usa CSS Grid para organizar os cartões de missão e visão lado a lado, e os cartões de valores em uma grade responsiva. A regra grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) cria colunas que se ajustam automaticamente ao tamanho da tela: cada coluna tem no mínimo 300 pixels (na grade dos valores, 280 pixels), e o Grid distribui o espaço restante igualmente. Em telas menores, os cartões ficam empilhados automaticamente, sem necessidade de uma media query.

Os cartões de valores recebem destaque={true}, o que ativa a classe cartao-destaque e adiciona a faixa azul no topo. Para saber mais sobre CSS Grid, consulte a documentação do MDN: CSS Grid Layout.

Nota

Flexbox ou Grid? A regra de ouro é: use Flexbox para alinhar elementos em uma única direção (como nosso cabeçalho) e Grid quando precisar controlar o layout em duas direções simultaneamente, com linhas e colunas estruturadas.

5.7.3 Página Equipe

Abra o arquivo src/pages/equipe.astro e substitua o conteúdo inteiro pelo código abaixo:

src/pages/equipe.astro
---
import LayoutBase from '../layouts/LayoutBase.astro';
import Cartao from '../components/Cartao.astro';
import { empresa } from '../data/empresa.js';
---

<LayoutBase 
	titulo="Nossa Equipe" 
	descricao={`Conheça os profissionais por trás da ${empresa.nome}.`}
>
	<section class="secao">
		<div class="container">
			<div class="cabecalho-secao">
				<h1>Nossa Equipe Técnica</h1>
				<p>Conheça os profissionais que transformam requisitos de negócio em soluções de software seguras e escaláveis</p>
			</div>

			<div class="grid-equipe">
				{empresa.equipe.map((membro) => (
					<Cartao>
						<div class="card-membro-conteudo">
							<img 
								src={membro.foto} 
								alt={membro.alt} 
								class="foto-perfil" 
								width="140" 
								height="140" 
							/>
							<div class="info-membro">
								<h3>{membro.nome}</h3>
								<span class="cargo">{membro.cargo}</span>
								<p class="descricao-membro">{membro.descricao}</p>
							</div>
						</div>
					</Cartao>
				))}
			</div>
		</div>
	</section>
</LayoutBase>

<style>
	.cabecalho-secao {
		margin-bottom: 3rem;
	}

	.cabecalho-secao p {
		font-size: 1.125rem;
		color: var(--cor-texto-secundario);
		max-width: 600px;
	}

	.grid-equipe {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
		gap: 2rem;
	}

	.card-membro-conteudo {
		text-align: center;
		display: flex;
		flex-direction: column;
		align-items: center;
	}

	.foto-perfil {
		width: 140px;
		height: 140px;
		border-radius: 50%;
		object-fit: cover;
		margin-bottom: 1.25rem;
		border: 4px solid var(--cor-borda);
	}

	.info-membro h3 {
		font-size: 1.25rem;
		margin-bottom: 0.25rem;
	}

	.cargo {
		display: inline-block;
		color: var(--cor-secundaria);
		font-size: 0.875rem;
		font-weight: 700;
		text-transform: uppercase;
		letter-spacing: 0.5px;
		margin-bottom: 1rem;
	}

	.descricao-membro {
		font-size: 0.95rem;
		color: var(--cor-texto-secundario);
		margin-bottom: 0;
	}
</style>

A página da equipe mostra cada membro dentro de um cartão com a foto circular, o nome, o cargo e uma descrição. A foto usa border-radius: 50% para ficar redonda e object-fit: cover para manter as proporções sem distorcer — quando a imagem original não é quadrada, o object-fit: cover recorta o excesso e preenche o espaço circular. Os atributos width e height na tag <img> informam ao navegador o tamanho em que a foto será exibida (140 por 140 pixels), o que ajuda a reservar o espaço antes de carregá-la e evita que o layout “pule” durante o carregamento. Para saber mais, consulte o MDN: object-fit.

5.7.4 Página Contato

Abra o arquivo src/pages/contato.astro e substitua o conteúdo inteiro pelo código abaixo:

src/pages/contato.astro
---
import LayoutBase from '../layouts/LayoutBase.astro';
import Cartao from '../components/Cartao.astro';
import { empresa } from '../data/empresa.js';
---

<LayoutBase 
	titulo="Fale Conosco" 
	descricao={`Entre em contato com a ${empresa.nome}.`}
>
	<section class="secao">
		<div class="container">
			<div class="cabecalho-secao">
				<h1>Fale Conosco</h1>
				<p>Deseja construir uma solução de software sob medida para o seu negócio? Entre em contato com a {empresa.nome}!</p>
			</div>

			<div class="grid-contato">
				<Cartao>
					<h2>Nossa Sede</h2>
					<address>
						{empresa.contato.endereco.logradouro}, {empresa.contato.endereco.complemento}<br/>
						{empresa.contato.endereco.bairro}<br/>
						{empresa.contato.endereco.cidadeEstado}<br/>
						CEP: {empresa.contato.endereco.cep}
					</address>
				</Cartao>

				<Cartao>
					<h2>Canais Digitais</h2>
					<p><strong>E-mail:</strong> <a href={`mailto:${empresa.contato.email}`}>{empresa.contato.email}</a></p>
					<p><strong>Telefone:</strong> {empresa.contato.telefone}</p>
				</Cartao>
			</div>
		</div>
	</section>
</LayoutBase>

<style>
	.cabecalho-secao {
		margin-bottom: 3rem;
	}

	.cabecalho-secao p {
		font-size: 1.125rem;
		color: var(--cor-texto-secundario);
	}

	.grid-contato {
		display: grid;
		grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
		gap: 2rem;
	}

	.grid-contato h2 {
		font-size: 1.5rem;
		margin-bottom: 1rem;
		color: var(--cor-secundaria);
	}

	address {
		font-style: normal;
	}

	.grid-contato p {
		margin-bottom: 0.5rem;
	}

	.grid-contato p:last-child {
		margin-bottom: 0;
	}
</style>

A página de contato organiza o endereço e os canais digitais em dois cartões lado a lado com CSS Grid. O endereço usa a tag semântica <address>, e o e-mail é um link mailto: que abre o aplicativo de e-mail do visitante. Para saber mais, consulte o MDN: <address>.

5.8 Verificando o Resultado

Se o servidor de desenvolvimento não estiver em execução, inicie-o no terminal:

npm run dev

Abra a aba Portas (Ports), passe o mouse sobre o endereço na coluna Endereço Encaminhado (Forwarded Address) e clique no ícone do globo, Abrir no Navegador (Open in Browser). Navegue pelas quatro páginas e confira: o cabeçalho mostra o logotipo e destaca em azul o link da página atual; a página inicial tem o hero com o degradê escuro e o botão “Fale Conosco”; as fotos da equipe aparecem redondas; e o rodapé tem fundo escuro, com a faixa colorida no topo.

Captura de tela da página inicial renderizada no navegador, destacando o hero escuro
Figura 5.1: Página inicial do site mostrando o cabeçalho e o hero com degradê escuro.
Captura de tela da página Sobre, exibindo os cartões de missão, visão e valores
Figura 5.2: Página Sobre da empresa com o título ‘Sobre a TED System’ e os valores.
Captura de tela da página de equipe, com os cartões e fotos dos funcionários
Figura 5.3: Página da Equipe exibindo cartões com as fotos redondas dos membros da equipe.
Captura de tela da página de contato, com os diferentes meios de comunicação
Figura 5.4: Página de Contato exibindo cartões com as informações de contato.

Para encerrar o servidor, volte ao terminal e pressione Ctrl + C. Por fim, execute o processo de construção para garantir que o código não possui erros que impeçam a geração do site estático:

npm run build

Na saída final, observe a mensagem indicando a criação das quatro páginas (4 page(s) built in ...).

Terminal exibindo a mensagem '4 page(s) built in' indicando a finalização da operação
Figura 5.5: Saída do terminal indicando que as quatro páginas foram geradas durante o build.

5.9 Registrando o Progresso no Git

Registre suas melhorias no Git executando a sequência de comandos abaixo no terminal.

5.9.1 Verificando os arquivos modificados com git status

git status

O Git mostrará as novas pastas src/styles e public/images, o novo componente Cartao.astro e os arquivos alterados do layout, dos componentes e das páginas.

5.9.2 Adicionando as mudanças com git add

git add .

5.9.3 Criando o commit descritivo com git commit

git commit -m "feat: adiciona estilos, imagens e componente de cartão"

5.9.4 Sincronizando com o repositório remoto com git push

git push

5.10 Resumo e Próximos Passos

Neste capítulo, você aprendeu:

  • Como criar um arquivo de estilos globais (src/styles/global.css) com variáveis CSS, importado no layout;
  • A usar estilos com escopo (<style> dentro de cada componente), que não afetam o restante do site;
  • A organizar elementos na tela com Flexbox (cabeçalho e rodapé) e CSS Grid (cartões e grades);
  • A destacar no menu a página atual usando Astro.url.pathname, o método startsWith e a diretiva class:list;
  • A usar a propriedade object-fit: cover para manter as proporções das fotos;
  • A incluir imagens estáticas na pasta public/ e referenciá-las no código;
  • A usar o componente Cartao com props e <slot /> para exibir conteúdo de forma reutilizável.

No próximo capítulo, vamos publicar o site na internet usando a Cloudflare Pages.