Partilhar: WhatsApp
aulify
UC UC00622 · T. Desenv. Software

Ficha 01 · HTML e CSS

Estrutura semântica, layout, design
Versão · Aluno
Tempo · 60 minutos
Cotação · 100 pontos
Aluno(a)
Turma
Data
Objectivos da ficha

Parte I · HTML

Exercício 1 · Estrutura semântica (15 pts)

Escreve estrutura HTML5 semântica para uma página "Sobre nós" com: - Cabeçalho com logo e navegação. - Conteúdo principal com título e parágrafos. - Sidebar com links relacionados. - Rodapé com informação de contacto.

<nav aria-label="Principal">
  <ul>
    <li><a href="/">Início</a></li>
    <li><a href="/sobre" aria-current="page">Sobre</a></li>
    <li><a href="/servicos">Serviços</a></li>
    <li><a href="/contacto">Contacto</a></li>
  </ul>
</nav>

Sobre a Empresa XYZ

A nossa história

A Empresa XYZ foi fundada em 2010 com o objectivo de...

Desde então, ajudámos centenas de clientes a...

Missão e valores

A nossa missão é...

  • Qualidade em primeiro lugar
  • Inovação constante
  • Foco no cliente

A nossa equipa

Somos uma equipa de profissionais apaixonados...

**Características**:

1. **`<header>`** contém logo + nav.
2. **`<main>`** é o conteúdo único e principal.
3. **`<article>`** para conteúdo independente (página inteira sobre nós).
4. **`<section>`** para subdivisões temáticas.
5. **`<aside>`** para conteúdo relacionado mas independente.
6. **`<footer>`** para rodapé.
7. **`<nav>`** com `aria-label` para distinguir nav principal vs footer.
8. **`<address>`** para info de contacto.
9. **`alt`** em imagem.
10. **Hierarquia de headings**: h1  h2  h3.

</div>

### Exercício 2 · Formulário (15 pts)

Cria formulário de contacto com validação HTML5:
- Nome (obrigatório, mín 3 chars).
- Email (obrigatório).
- Telefone (opcional, formato português).
- Assunto (dropdown).
- Mensagem (textarea, mín 10 chars).
- Aceitar política privacidade (checkbox).
- Botão submit.

<div class="resposta"></div>



  <!-- Email -->
  <div class="form-group">
    <label for="email">Email *</label>
    <input 
      type="email" 
      id="email" 
      name="email" 
      required
      placeholder="joana@email.com"
      autocomplete="email">
  </div>

  <!-- Telefone (opcional) -->
  <div class="form-group">
    <label for="telefone">Telefone</label>
    <input 
      type="tel" 
      id="telefone" 
      name="telefone"
      pattern="(\+351)?[0-9]{9}"
      placeholder="912 345 678"
      autocomplete="tel">
    <small>Opcional. Formato: 912345678 ou +351912345678</small>
  </div>

  <!-- Assunto -->
  <div class="form-group">
    <label for="assunto">Assunto *</label>
    <select id="assunto" name="assunto" required>
      <option value="">-- Selecciona --</option>
      <option value="info">Pedido de informação</option>
      <option value="orcamento">Pedido de orçamento</option>
      <option value="suporte">Suporte técnico</option>
      <option value="outro">Outro</option>
    </select>
  </div>

  <!-- Mensagem -->
  <div class="form-group">
    <label for="mensagem">Mensagem *</label>
    <textarea 
      id="mensagem" 
      name="mensagem" 
      required
      minlength="10"
      maxlength="1000"
      rows="6"
      placeholder="Escreve aqui a tua mensagem..."></textarea>
    <small>Mínimo 10 caracteres.</small>
  </div>

  <!-- Newsletter (opcional) -->
  <div class="form-group">
    <input 
      type="checkbox" 
      id="newsletter" 
      name="newsletter">
    <label for="newsletter">
      Quero receber a newsletter mensal
    </label>
  </div>

  <!-- Aceitar política (obrigatório) -->
  <div class="form-group">
    <input 
      type="checkbox" 
      id="privacidade" 
      name="privacidade" 
      required>
    <label for="privacidade">
      Aceito a <a href="/privacidade" target="_blank">política de privacidade</a> *
    </label>
  </div>

  <!-- Submit -->
  <div class="form-actions">
    <button type="submit">Enviar mensagem</button>
    <button type="reset">Limpar</button>
  </div>

  <p class="form-note">* Campos obrigatórios</p>
</form>

Validação HTML5 usada:

Acessibilidade:

CSS sugerido:

.form-contact {
  max-width: 500px;
  margin: 2rem auto;
  padding: 2rem;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.form-group {
  margin-bottom: 1.25rem;
}

.form-group label {
  display: block;
  margin-bottom: 0.25rem;
  font-weight: 600;
}

.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid #ccc;
  border-radius: 4px;
  font: inherit;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: 2px solid #6366f1;
  border-color: #6366f1;
}

/* Checkbox alinhado */
.form-group input[type="checkbox"] {
  width: auto;
  display: inline-block;
  margin-right: 0.5rem;
}

.form-group input[type="checkbox"] + label {
  display: inline;
  font-weight: normal;
}

/* Erro visual (HTML5 validation) */
.form-group input:invalid:not(:focus):not(:placeholder-shown) {
  border-color: #ef4444;
}

button[type="submit"] {
  background: #6366f1;
  color: white;
  padding: 0.75rem 2rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 600;
}

button[type="submit"]:hover {
  background: #4f46e5;
}

JavaScript para tratamento (opcional):

const form = document.querySelector('.form-contact');

form.addEventListener('submit', async (e) => {
  e.preventDefault();

  const formData = new FormData(form);
  const data = Object.fromEntries(formData);

  try {
    const response = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

    if (response.ok) {
      alert('Mensagem enviada! Vamos contactá-lo em breve.');
      form.reset();
    } else {
      alert('Erro ao enviar. Tenta novamente.');
    }
  } catch (error) {
    alert('Erro de rede.');
  }
});

Parte II · CSS

Exercício 3 · Box model (10 pts)

Para este HTML:

<div class="caixa">Hello</div>

Aplica CSS: - Width: 200px, height: 100px. - Padding: 20px. - Border: 5px solid black. - Margin: 10px. - Background azul.

a) Qual é a largura total visível? b) Como mudar para que width=200 seja a largura total?

Exercício 4 · Flexbox (15 pts)

Cria layout com Flexbox: - Container com 3 cards. - Cada card cresce igualmente. - Espaço de 20px entre cards. - Centrados verticalmente. - Em mobile (< 768px), cards empilham verticalmente.

```css
/* Mobile first (default) */
.cards-container {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
}

.card {
  background: white;
  padding: 1.5rem;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}

.card h2 {
  margin: 0 0 0.5rem 0;
  color: #333;
}

.card p {
  margin: 0;
  color: #666;
}

/* Tablet+ */
@media (min-width: 768px) {
  .cards-container {
    flex-direction: row;
    align-items: stretch;  /* mesma altura */
    max-width: 1200px;
    margin: 0 auto;
  }

  .card {
    flex: 1;  /* cada card cresce igualmente */
  }
}

Resultado:

Mobile (< 768px):

┌────────────────┐
│  Card 1        │
└────────────────┘
       ↓ 20px gap
┌────────────────┐
│  Card 2        │
└────────────────┘
       ↓ 20px gap
┌────────────────┐
│  Card 3        │
└────────────────┘

Tablet+ (>= 768px):

┌─────────┐ ←20px→ ┌─────────┐ ←20px→ ┌─────────┐
│ Card 1  │        │ Card 2  │        │ Card 3  │
└─────────┘        └─────────┘        └─────────┘

Explicação:

Mobile first (regras fora de media query): - flex-direction: column: empilha verticalmente. - gap: 20px: espaço entre items. - Cada card ocupa largura total (default).

Tablet+ (media query): - flex-direction: row: horizontal. - flex: 1 em cada card: crescem igualmente. - Container limitado a 1200px e centrado.

Variações úteis:

Cards com tamanhos diferentes mas iguais:

.card {
  flex: 1 1 0;  /* grow shrink basis */
}

Card com largura mínima (não compactar demais):

.card {
  flex: 1 1 250px;  /* min 250px */
}

Centrar tudo:

.cards-container {
  display: flex;
  justify-content: center;  /* eixo principal */
  align-items: center;       /* eixo cruzado */
}

Justify-content options: - flex-start: ao início. - flex-end: ao fim. - center: ao centro. - space-between: espaço entre items. - space-around: espaço à volta. - space-evenly: espaço igual.

Wrap (várias linhas):

.cards-container {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}

.card {
  flex: 1 1 250px;  /* min 250px, wrap se não cabe */
}

Parte III · Layout completo

Exercício 5 · Grid layout (15 pts)

Cria layout "Holy Grail" com CSS Grid: - Header (topo). - Sidebar (esquerda). - Main content (centro). - Aside (direita). - Footer (baixo).

Mobile: tudo empilhado.

```css
/* Mobile first */
.layout {
  display: grid;
  grid-template-areas:
    "header"
    "sidebar"
    "main"
    "aside"
    "footer";
  min-height: 100vh;
  gap: 0;
}

header { grid-area: header; }
.sidebar { grid-area: sidebar; }
main { grid-area: main; }
aside { grid-area: aside; }
footer { grid-area: footer; }

/* Estilos */
header {
  background: #1f2937;
  color: white;
  padding: 1rem;
}

.sidebar {
  background: #f3f4f6;
  padding: 1rem;
}

main {
  background: white;
  padding: 1rem;
}

aside {
  background: #e5e7eb;
  padding: 1rem;
}

footer {
  background: #1f2937;
  color: white;
  padding: 1rem;
  text-align: center;
}

/* Desktop */
@media (min-width: 1024px) {
  .layout {
    grid-template-columns: 200px 1fr 200px;
    grid-template-rows: auto 1fr auto;
    grid-template-areas:
      "header  header  header"
      "sidebar main    aside"
      "footer  footer  footer";
  }
}

Mobile:

┌─────────────┐
│   Header    │
├─────────────┤
│   Sidebar   │
├─────────────┤
│    Main     │
├─────────────┤
│    Aside    │
├─────────────┤
│   Footer    │
└─────────────┘

Desktop:

┌──────────────────────────────────┐
│            Header                 │
├─────────┬───────────────┬────────┤
│ Sidebar │     Main      │ Aside  │
│         │               │        │
│         │               │        │
├─────────┴───────────────┴────────┤
│            Footer                 │
└──────────────────────────────────┘

Vantagens de Grid Areas:

  1. Visual: vê-se imediatamente o layout no CSS.
  2. Manutenção: alterar layout = mudar áreas (não mover HTML).
  3. Responsive: redefine areas em media queries.

Variações:

Sidebar à direita:

grid-template-areas:
  "header  header  header"
  "main    main    sidebar"
  "footer  footer  footer";

Sem aside:

grid-template-columns: 250px 1fr;
grid-template-areas:
  "header  header"
  "sidebar main"
  "footer  footer";

Magazine layout (várias colunas, áreas complexas):

grid-template-columns: repeat(4, 1fr);
grid-template-areas:
  "h h h h"
  "f f f s"
  "a b c s"
  "g g s s";

Combinação Grid + Flexbox: - Grid: layout 2D (página inteira). - Flexbox: layout 1D (dentro de componentes).

/* Grid para página */
.page { display: grid; ... }

/* Flexbox para cards dentro de main */
main {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

Exercício 6 · Responsividade (15 pts)

Página com cards de produtos. Comportamento responsivo: - Mobile (< 600px): 1 coluna. - Tablet (600-1023px): 2 colunas. - Desktop (>= 1024px): 4 colunas.

Sem media queries explícitas, usar Grid auto-fit.

```css
.produto {
  background: white;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
  transition: transform 0.2s, box-shadow 0.2s;
  display: flex;
  flex-direction: column;
}

.produto:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}

.produto img {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.produto-info {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.produto-info h3 {
  margin: 0 0 0.5rem 0;
  font-size: 1.1rem;
}

.produto-desc {
  color: #666;
  flex: 1;
  margin-bottom: 1rem;
}

.produto-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.preco {
  font-size: 1.25rem;
  font-weight: 700;
  color: #6366f1;
}

.btn-comprar {
  background: #6366f1;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
  font-weight: 600;
}

.btn-comprar:hover {
  background: #4f46e5;
}

Resultado: cards de produto profissionais, responsivos, com hover effects.

Parte IV · Casos práticos

Exercício 7 · Sticky header (10 pts)

Como fazer header que fica fixo no topo ao scroll?

Exercício 8 · Mobile menu (15 pts)

Cria menu hamburger para mobile que abre/fecha com JavaScript.