UC00622 · Criar aplicações em HTML5
Introdução
HTML5 é a base de toda a web moderna. Esta UC (50h) cobre: - HTML5: markup semântico. - CSS3: design e layout (Flexbox, Grid). - JavaScript moderno (ES6+): interactividade. - Responsive design: mobile-first. - Acessibilidade e SEO básico.
Foco: construir sites reais desde o primeiro dia, com práticas profissionais.
1. Web: como funciona
1.1 Arquitectura
Browser (cliente) ──HTTP request──► Servidor
▲ │
│ │
└─HTML/CSS/JS+assets──────────────────┘
- Browser: Chrome, Firefox, Safari, Edge.
- HTTP/HTTPS: protocolo de comunicação.
- Servidor: aloja ficheiros (Apache, Nginx, Node).
- URL: endereço do recurso.
1.2 Tecnologias web
- HTML (HyperText Markup Language): estrutura.
- CSS (Cascading Style Sheets): apresentação.
- JavaScript: comportamento.
Princípio: separação de preocupações.
1.3 Setup
Editor: VSCode + extensões: - Live Server. - Prettier. - Auto Rename Tag.
Browser: Chrome / Firefox com DevTools (F12).
Servidor local:
# Python
python -m http.server 8000
# Node
npx serve
# VSCode: Live Server extension (Alt+L Alt+O)
2. HTML5
2.1 Estrutura base
<!DOCTYPE html>
<html lang="pt-PT">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Título do site</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Conteúdo principal</h1>
<script src="script.js"></script>
</body>
</html>
Sempre incluir:
- <!DOCTYPE html> (modo standards).
- lang attribute.
- charset UTF-8.
- viewport meta (responsive).
2.2 Tags semânticas HTML5
<header> <!-- cabeçalho da página/secção -->
<nav>...</nav>
</header>
<main> <!-- conteúdo principal -->
<article> <!-- conteúdo independente -->
<section>...</section>
</article>
<aside> <!-- lateral / sidebar -->
</aside>
</main>
<footer> <!-- rodapé -->
</footer>
Por que semântica: - Acessibilidade: leitores de ecrã percebem estrutura. - SEO: motores de busca classificam melhor. - Manutenção: código mais legível. - CSS: targetable sem classes extras.
Em vez de:
<div class="header">
<div class="nav">...</div>
</div>
Fazer:
<header>
<nav>...</nav>
</header>
2.3 Tags de conteúdo
Headings:
<h1>Título principal</h1> <!-- 1 por página -->
<h2>Secção</h2>
<h3>Subsecção</h3>
<!-- até h6 -->
Texto:
<p>Parágrafo</p>
<strong>Importante</strong>
<em>Ênfase</em>
<small>Texto pequeno</small>
<mark>Destacado</mark>
<code>código</code>
<pre>texto pré-formatado</pre>
<blockquote>Citação</blockquote>
Listas:
<!-- Não ordenada -->
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<!-- Ordenada -->
<ol>
<li>Primeiro</li>
<li>Segundo</li>
</ol>
<!-- Definições -->
<dl>
<dt>HTML</dt>
<dd>HyperText Markup Language</dd>
</dl>
Links:
<a href="https://example.com">Link externo</a>
<a href="/sobre">Link interno</a>
<a href="#seccao">Âncora</a>
<a href="mailto:x@y.pt">Email</a>
<a href="tel:+351200000000">Telefone</a>
<!-- Abrir em nova aba -->
<a href="..." target="_blank" rel="noopener noreferrer">
Imagens:
<img src="foto.jpg" alt="Descrição da imagem">
<!-- Com srcset para responsive -->
<img srcset="small.jpg 480w,
large.jpg 1200w"
sizes="(max-width: 600px) 480px, 1200px"
src="large.jpg"
alt="Descrição">
<!-- Picture (formats alternativos) -->
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="">
</picture>
alt é obrigatório (acessibilidade + SEO).
2.4 Mídia
Vídeo:
<video src="video.mp4" controls width="600">
<source src="video.mp4" type="video/mp4">
<source src="video.webm" type="video/webm">
O teu browser não suporta vídeo.
</video>
Áudio:
<audio src="musica.mp3" controls>
O teu browser não suporta áudio.
</audio>
Embed (YouTube):
<iframe src="https://www.youtube.com/embed/VIDEOID"
width="560" height="315"
frameborder="0" allowfullscreen></iframe>
2.5 Formulários
<form action="/submit" method="POST">
<!-- Text -->
<label for="nome">Nome:</label>
<input type="text" id="nome" name="nome" required>
<!-- Email -->
<label for="email">Email:</label>
<input type="email" id="email" name="email"
placeholder="exemplo@email.com" required>
<!-- Password -->
<label for="pass">Password:</label>
<input type="password" id="pass" name="pass"
minlength="8" required>
<!-- Number -->
<input type="number" min="0" max="100" step="1">
<!-- Date -->
<input type="date" min="2026-01-01">
<!-- Checkbox -->
<input type="checkbox" id="newsletter" name="newsletter">
<label for="newsletter">Subscrever newsletter</label>
<!-- Radio -->
<input type="radio" id="m" name="genero" value="M">
<label for="m">Masculino</label>
<input type="radio" id="f" name="genero" value="F">
<label for="f">Feminino</label>
<!-- Select -->
<label for="pais">País:</label>
<select id="pais" name="pais">
<option value="">--Selecciona--</option>
<option value="PT">Portugal</option>
<option value="ES">Espanha</option>
</select>
<!-- Textarea -->
<label for="msg">Mensagem:</label>
<textarea id="msg" name="msg" rows="5"></textarea>
<!-- Submit -->
<button type="submit">Enviar</button>
<button type="reset">Limpar</button>
</form>
Input types HTML5: text, email, password, number, range, date, time, datetime-local, color, file, tel, url, search.
Validation atributos:
- required: obrigatório.
- min, max: número/data.
- minlength, maxlength: texto.
- pattern: regex.
- placeholder: dica.
2.6 Tabelas
<table>
<caption>Vendas mensais</caption>
<thead>
<tr>
<th>Mês</th>
<th>Vendas (€)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Janeiro</td>
<td>5,000</td>
</tr>
<tr>
<td>Fevereiro</td>
<td>6,500</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Total</td>
<td>11,500</td>
</tr>
</tfoot>
</table>
Tabelas apenas para dados tabulares, NÃO para layout (use CSS Grid).
3. CSS
3.1 Como aplicar
3 formas:
1. Externo (recomendado):
<link rel="stylesheet" href="style.css">
2. Interno:
<head>
<style>
h1 { color: blue; }
</style>
</head>
3. Inline (evitar):
<h1 style="color: blue;">Título</h1>
3.2 Sintaxe
/* Comentário */
selector {
propriedade: valor;
outra-propriedade: outro-valor;
}
/* Múltiplos selectors */
h1, h2, h3 {
font-family: 'Inter', sans-serif;
}
3.3 Selectors
Básicos:
/* Elemento */
p { color: black; }
/* Classe */
.destaque { background: yellow; }
/* ID */
#header { height: 80px; }
/* Universal */
* { box-sizing: border-box; }
Combinadores:
/* Descendente (qualquer nível) */
nav a { color: white; }
/* Filho directo */
nav > a { color: blue; }
/* Irmão adjacente */
h1 + p { font-size: 18px; }
/* Irmãos gerais */
h1 ~ p { color: gray; }
Pseudo-classes:
a:hover { color: red; }
a:focus { outline: 2px solid blue; }
a:active { color: purple; }
a:visited { color: gray; }
input:disabled { opacity: 0.5; }
input:checked + label { font-weight: bold; }
li:first-child { font-weight: bold; }
li:last-child { border-bottom: none; }
li:nth-child(odd) { background: #f5f5f5; }
li:nth-child(2n+1) { /* ímpares */ }
Pseudo-elementos:
p::first-line { font-weight: bold; }
p::first-letter { font-size: 2em; }
p::before { content: "→ "; }
p::after { content: " ✓"; }
Attribute selectors:
input[type="email"] { border: 1px solid blue; }
a[href^="https://"] { color: green; }
a[href$=".pdf"]::after { content: " 📄"; }
3.4 Especificidade
Pesos: - Inline style: 1000. - ID: 100. - Classe / pseudo-classe / atributo: 10. - Elemento / pseudo-elemento: 1.
Exemplos:
- p = 1.
- .botao = 10.
- #header .botao = 110.
- body p.destaque = 12.
Mais específico vence.
!important força (evitar, usar com extrema cautela):
p { color: red !important; }
3.5 Box model
Cada elemento é uma "caixa":
┌─────────────────────────────┐
│ margin │
│ ┌───────────────────────┐ │
│ │ border │ │
│ │ ┌─────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌───────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └───────────┘ │ │ │
│ │ └─────────────────┘ │ │
│ └───────────────────────┘ │
└─────────────────────────────┘
Propriedades:
.box {
/* Conteúdo */
width: 300px;
height: 200px;
/* Padding (interno) */
padding: 20px;
padding: 10px 20px; /* top/bottom right/left */
padding: 10px 20px 30px 40px; /* top right bottom left */
/* Border */
border: 1px solid black;
border-radius: 8px;
/* Margin (externo) */
margin: 0 auto; /* centrar horizontalmente */
}
/* Recomendado para todos: */
* {
box-sizing: border-box;
}
border-box: width = content + padding + border (mais intuitivo).
3.6 Cores
/* Hexadecimal */
color: #ff5733;
color: #f53; /* short */
/* RGB / RGBA */
color: rgb(255, 87, 51);
color: rgba(255, 87, 51, 0.5); /* 50% transparente */
/* HSL */
color: hsl(11, 100%, 60%);
/* Named */
color: red;
color: rebeccapurple;
/* CSS Variables */
:root {
--primary: #6366f1;
--secondary: #f59e0b;
}
.button {
background: var(--primary);
}
3.7 Tipografia
body {
/* Fonte */
font-family: 'Inter', -apple-system, sans-serif;
/* Tamanho */
font-size: 16px; /* px (fixo) */
font-size: 1rem; /* rem (relativo a root) */
font-size: 1.2em; /* em (relativo ao parent) */
/* Peso */
font-weight: 400; /* normal */
font-weight: 700; /* bold */
/* Estilo */
font-style: italic;
/* Espaçamento */
line-height: 1.6; /* altura das linhas */
letter-spacing: 0.05em; /* entre letras */
word-spacing: 0.1em; /* entre palavras */
/* Alinhamento */
text-align: center;
text-align: justify;
/* Decoração */
text-decoration: underline;
text-transform: uppercase;
}
Web fonts (Google Fonts):
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
body {
font-family: 'Inter', sans-serif;
}
3.8 Background
.hero {
background-color: #f0f0f0;
background-image: url('bg.jpg');
background-size: cover; /* fill area */
background-position: center;
background-repeat: no-repeat;
background-attachment: fixed; /* parallax */
/* Shorthand */
background: url('bg.jpg') no-repeat center/cover;
/* Gradient */
background: linear-gradient(135deg, #667eea, #764ba2);
background: radial-gradient(circle, red, blue);
}
4. CSS avançado
4.1 Flexbox
Layout 1D (linha ou coluna):
.container {
display: flex;
flex-direction: row; /* row, column, row-reverse */
justify-content: center; /* eixo principal */
align-items: center; /* eixo cruzado */
gap: 20px; /* espaço entre items */
flex-wrap: wrap; /* quebrar linha */
}
.item {
flex: 1; /* cresce igualmente */
flex: 0 0 200px; /* fixo 200px */
flex-basis: 300px;
flex-grow: 1;
flex-shrink: 0;
}
Valores justify-content:
- flex-start, flex-end, center.
- space-between, space-around, space-evenly.
Valores align-items:
- stretch (default), flex-start, flex-end, center, baseline.
Casos comuns:
Centrar elemento:
.parent {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Navbar:
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
}
Cards row:
.cards {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.card {
flex: 1 1 250px; /* min 250px, cresce */
}
4.2 Grid
Layout 2D:
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 3 colunas iguais */
grid-template-columns: repeat(3, 1fr); /* shortcut */
grid-template-columns: 200px 1fr 200px; /* sidebar + main + sidebar */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); /* responsive */
grid-template-rows: auto;
gap: 20px; /* entre cells */
gap: 20px 10px; /* row / column */
}
/* Posicionar items */
.item {
grid-column: 1 / 3; /* coluna 1 a 3 */
grid-column: span 2; /* span 2 colunas */
grid-row: 1;
grid-row: 1 / span 2;
}
Areas (mais legível):
.container {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
header { grid-area: header; }
.sidebar { grid-area: sidebar; }
main { grid-area: main; }
footer { grid-area: footer; }
4.3 Transitions e animations
Transitions (mudanças suaves):
.button {
background: blue;
transition: background 0.3s ease;
}
.button:hover {
background: red;
}
/* Multiple */
.button {
transition: background 0.3s, transform 0.2s ease-out;
}
Transform:
.box {
transform: translateX(100px);
transform: translateY(-50%);
transform: scale(1.5);
transform: rotate(45deg);
transform: skew(10deg);
/* Combinadas */
transform: translate(100px, 50px) rotate(45deg) scale(1.2);
}
Animations (keyframes):
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.element {
animation: slideIn 0.5s ease-out;
animation: slideIn 0.5s ease-out forwards; /* fica no estado final */
}
4.4 CSS Variables (Custom Properties)
:root {
--primary: #6366f1;
--secondary: #f59e0b;
--text: #0f172a;
--bg: #ffffff;
--space-1: 0.5rem;
--space-2: 1rem;
--space-3: 2rem;
}
.button {
background: var(--primary);
color: white;
padding: var(--space-1) var(--space-2);
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--text: #ffffff;
--bg: #0f172a;
}
}
4.5 Responsive design
Mobile-first (recomendado):
/* Base (mobile) */
.container {
display: flex;
flex-direction: column;
padding: 1rem;
}
/* Tablet (>= 768px) */
@media (min-width: 768px) {
.container {
flex-direction: row;
padding: 2rem;
}
}
/* Desktop (>= 1024px) */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
}
}
Breakpoints típicos: - Mobile: < 768px. - Tablet: 768-1023px. - Desktop: 1024-1439px. - Large: 1440px+.
Unidades responsivas:
- vw/vh: viewport width/height (1vw = 1% viewport).
- %: % do parent.
- rem: relativo ao root font-size.
- em: relativo ao parent.
- clamp(min, ideal, max): limites.
.title {
font-size: clamp(1.5rem, 5vw, 3rem);
}
5. JavaScript fundamentos
5.1 Hello World
<!-- Inline -->
<script>
console.log("Olá!");
</script>
<!-- Externo (recomendado) -->
<script src="script.js"></script>
<!-- Defer (depois de DOM carregar) -->
<script src="script.js" defer></script>
<!-- Async (paralelo) -->
<script src="script.js" async></script>
<!-- Module (ES6+) -->
<script type="module" src="app.js"></script>
5.2 Variáveis
// const (preferido, não pode reatribuir)
const PI = 3.14;
const nome = "Ana";
// let (pode reatribuir)
let contador = 0;
contador++;
// var (legacy, evitar)
// Tipos primitivos
const numero = 42;
const decimal = 3.14;
const texto = "Olá";
const booleano = true;
const indefinido = undefined;
const nulo = null;
const simbolo = Symbol("id");
const grande = 9007199254740992n; // BigInt
// Objectos
const array = [1, 2, 3];
const objecto = { nome: "Ana", idade: 25 };
const data = new Date();
5.3 Operadores
// Aritméticos
5 + 3 // 8
5 - 3 // 2
5 * 3 // 15
5 / 3 // 1.6666...
5 % 3 // 2 (resto)
5 ** 3 // 125 (potência)
// Comparação
== // igual (com coercion — evitar)
=== // igual estrito (preferir)
!= // diferente
!== // diferente estrito
< > <= >=
// Lógicos
&& || !
// Ternário
const status = idade >= 18 ? "adulto" : "menor";
// Nullish coalescing
const valor = input ?? "default"; // se null/undefined, "default"
// Optional chaining
user?.address?.city // não dá erro se intermediário é null
5.4 Strings
const nome = "Python";
// Métodos
nome.length // 6
nome.toUpperCase() // "PYTHON"
nome.toLowerCase() // "python"
nome.indexOf("th") // 2
nome.includes("th") // true
nome.slice(0, 3) // "Pyt"
nome.split("") // ["P","y","t","h","o","n"]
nome.replace("Py", "Co") // "Cothon"
nome.trim() // remove whitespace
// Template literals (ES6+)
const idade = 25;
const msg = `${nome} tem ${idade} anos`;
// Multi-linha
const html = `
<div>
<h1>${nome}</h1>
</div>
`;
5.5 Arrays
const frutas = ["maçã", "banana", "uva"];
// Acesso
frutas[0] // "maçã"
frutas[frutas.length - 1] // "uva"
// Adicionar / remover
frutas.push("kiwi") // adicionar no fim
frutas.pop() // remover último
frutas.unshift("manga") // adicionar no início
frutas.shift() // remover primeiro
// Métodos modernos (funcionais)
const numeros = [1, 2, 3, 4, 5];
numeros.map(x => x * 2) // [2, 4, 6, 8, 10]
numeros.filter(x => x > 2) // [3, 4, 5]
numeros.reduce((acc, x) => acc + x, 0) // 15
numeros.find(x => x > 3) // 4
numeros.some(x => x > 4) // true
numeros.every(x => x > 0) // true
numeros.forEach(x => console.log(x))
// Sort
numeros.sort((a, b) => a - b) // ascendente
numeros.sort((a, b) => b - a) // descendente
// Spread / rest
const a = [1, 2, 3];
const b = [...a, 4, 5]; // [1, 2, 3, 4, 5]
// Destructuring
const [first, second, ...rest] = numeros;
5.6 Objects
const pessoa = {
nome: "Ana",
idade: 25,
saudar() {
return `Olá, sou ${this.nome}`;
}
};
// Acesso
pessoa.nome
pessoa["nome"]
// Modificar
pessoa.idade = 26;
pessoa.email = "ana@x.pt";
// Destructuring
const { nome, idade } = pessoa;
const { nome: n, idade: i } = pessoa; // rename
// Spread
const novo = { ...pessoa, cidade: "Lisboa" };
// Iterar
Object.keys(pessoa) // ["nome", "idade", "saudar"]
Object.values(pessoa)
Object.entries(pessoa) // [["nome", "Ana"], ...]
for (const [chave, valor] of Object.entries(pessoa)) {
console.log(chave, valor);
}
5.7 Funções
// Function declaration
function saudar(nome) {
return `Olá, ${nome}`;
}
// Function expression
const saudar = function(nome) {
return `Olá, ${nome}`;
};
// Arrow function (preferido em código moderno)
const saudar = (nome) => `Olá, ${nome}`;
const dobro = x => x * 2; // 1 arg, sem parêntesis
const log = () => {
console.log("sem args");
};
// Default params
const greet = (name = "Mundo") => `Hello, ${name}`;
// Rest params
const sum = (...nums) => nums.reduce((a, b) => a + b, 0);
sum(1, 2, 3, 4) // 10
// Destructuring params
const print = ({ name, age }) => console.log(`${name}, ${age}`);
print({ name: "Ana", age: 25 });
5.8 Control flow
// If
if (idade < 18) {
console.log("menor");
} else if (idade < 65) {
console.log("adulto");
} else {
console.log("sénior");
}
// Switch
switch (cor) {
case "vermelho":
console.log("stop");
break;
case "verde":
console.log("go");
break;
default:
console.log("?");
}
// For
for (let i = 0; i < 10; i++) {
console.log(i);
}
// For of (arrays, strings, iterables)
for (const fruta of frutas) {
console.log(fruta);
}
// For in (chaves de objects — cuidado)
for (const chave in pessoa) {
console.log(chave, pessoa[chave]);
}
// While
let i = 0;
while (i < 5) {
console.log(i++);
}
// Try / catch
try {
JSON.parse(invalido);
} catch (error) {
console.error("Erro:", error.message);
} finally {
console.log("sempre corre");
}
6. DOM manipulation
6.1 Seleccionar elementos
// Por ID
const titulo = document.getElementById("titulo");
// Por classe (HTMLCollection)
const items = document.getElementsByClassName("item");
// Por tag (HTMLCollection)
const ps = document.getElementsByTagName("p");
// Query selector (CSS selector, moderno e preferido)
const primeiro = document.querySelector(".item"); // primeiro
const todos = document.querySelectorAll(".item"); // NodeList (forEach OK)
// Selectors complexos
const navLink = document.querySelector("nav > ul > li > a");
6.2 Modificar conteúdo
// Texto
elemento.textContent = "Novo texto"; // só texto, seguro
elemento.innerText = "Texto"; // texto visível
elemento.innerHTML = "<b>HTML</b>"; // parse HTML (cuidado: XSS)
// Atributos
elemento.setAttribute("id", "novo");
elemento.getAttribute("href");
elemento.removeAttribute("disabled");
// Propriedades
elemento.id = "novo";
elemento.className = "destaque";
elemento.href = "https://...";
// Data attributes
elemento.dataset.userId = "123"; // <div data-user-id="123">
console.log(elemento.dataset.userId);
6.3 Classes
// classList API
elemento.classList.add("active");
elemento.classList.remove("hidden");
elemento.classList.toggle("expanded");
elemento.classList.contains("active"); // boolean
elemento.classList.replace("old", "new");
6.4 Estilos
// Direto (inline)
elemento.style.color = "red";
elemento.style.backgroundColor = "yellow"; // camelCase
elemento.style.fontSize = "20px";
// Mais comum: alterar classe
elemento.classList.add("destaque");
/* CSS */
.destaque {
color: red;
background: yellow;
font-size: 20px;
}
6.5 Criar/remover elementos
// Criar
const div = document.createElement("div");
div.textContent = "Novo!";
div.classList.add("box");
// Adicionar a parent
const container = document.getElementById("container");
container.appendChild(div);
// Inserir antes de outro
container.insertBefore(div, outroFilho);
// Métodos modernos (mais flexíveis)
container.append(div, "texto", outroEl); // múltiplos, no fim
container.prepend(div); // no início
container.before(div); // como irmão anterior
container.after(div); // como irmão seguinte
// Remover
elemento.remove();
// Replace
elemento.replaceWith(novo);
6.6 Events
// addEventListener
botao.addEventListener("click", () => {
console.log("Clicado!");
});
// Com evento como parâmetro
input.addEventListener("input", (e) => {
console.log("Novo valor:", e.target.value);
});
// Múltiplos handlers
botao.addEventListener("click", handler1);
botao.addEventListener("click", handler2);
// Remover
botao.removeEventListener("click", handler1);
Eventos comuns: - Mouse: click, dblclick, mouseenter, mouseleave, mousemove. - Keyboard: keydown, keyup, keypress. - Form: submit, change, input, focus, blur. - Window: load, DOMContentLoaded, resize, scroll.
Form example:
const form = document.querySelector("form");
form.addEventListener("submit", (e) => {
e.preventDefault(); // impedir refresh da página
const formData = new FormData(form);
const data = Object.fromEntries(formData);
console.log("Form data:", data);
// Validar
if (!data.email.includes("@")) {
alert("Email inválido!");
return;
}
// Enviar (fetch)
enviarDados(data);
});
6.7 Exemplo prático completo
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<title>Contador</title>
<style>
body {
font-family: sans-serif;
text-align: center;
padding: 2rem;
}
.contador {
font-size: 4rem;
font-weight: bold;
color: #6366f1;
margin: 2rem 0;
}
button {
font-size: 1.2rem;
padding: 0.5rem 1.5rem;
margin: 0 0.5rem;
cursor: pointer;
border: none;
border-radius: 8px;
background: #6366f1;
color: white;
transition: background 0.2s;
}
button:hover {
background: #4f46e5;
}
button.reset {
background: #ef4444;
}
</style>
</head>
<body>
<h1>Contador</h1>
<div id="display" class="contador">0</div>
<button id="dec">−</button>
<button id="inc">+</button>
<button id="reset" class="reset">Reset</button>
<script>
let contador = 0;
const display = document.getElementById("display");
const btnInc = document.getElementById("inc");
const btnDec = document.getElementById("dec");
const btnReset = document.getElementById("reset");
function actualizar() {
display.textContent = contador;
}
btnInc.addEventListener("click", () => {
contador++;
actualizar();
});
btnDec.addEventListener("click", () => {
contador--;
actualizar();
});
btnReset.addEventListener("click", () => {
contador = 0;
actualizar();
});
</script>
</body>
</html>
7. Responsive design
(Coberto em CSS avançado. Recap aqui.)
7.1 Mobile first
/* Base = mobile */
.container {
width: 100%;
padding: 1rem;
}
/* Tablet+ */
@media (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
}
/* Desktop+ */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
padding: 2rem;
}
}
7.2 Viewport meta
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Obrigatório para responsive.
7.3 Imagens responsivas
<!-- Simples -->
<img src="foto.jpg" alt="" style="max-width: 100%; height: auto;">
<!-- srcset (browser escolhe) -->
<img srcset="small.jpg 480w,
medium.jpg 800w,
large.jpg 1200w"
sizes="(max-width: 600px) 480px,
(max-width: 900px) 800px,
1200px"
src="medium.jpg"
alt="">
7.4 Mobile menu
<nav>
<button class="menu-toggle" aria-label="Toggle menu">☰</button>
<ul class="menu">
<li><a href="#">Home</a></li>
<li><a href="#">Sobre</a></li>
<li><a href="#">Contacto</a></li>
</ul>
</nav>
<script>
const toggle = document.querySelector(".menu-toggle");
const menu = document.querySelector(".menu");
toggle.addEventListener("click", () => {
menu.classList.toggle("open");
});
</script>
<style>
/* Mobile */
.menu {
display: none;
}
.menu.open {
display: block;
}
/* Desktop */
@media (min-width: 768px) {
.menu-toggle {
display: none;
}
.menu {
display: flex;
gap: 2rem;
}
}
</style>
8. Acessibilidade e SEO
8.1 Acessibilidade (a11y)
WCAG (Web Content Accessibility Guidelines).
Principais:
Imagens com alt:
<img src="foto.jpg" alt="Descrição da imagem">
<img src="decorativa.jpg" alt=""> <!-- decorativa -->
Labels em inputs:
<label for="nome">Nome:</label>
<input id="nome" type="text">
Headings hierárquicos:
<h1>Título único</h1>
<h2>Secção</h2>
<h3>Subsecção</h3>
<!-- Não saltar (não h1 → h3) -->
Contraste suficiente: WCAG AA = 4.5:1 para texto normal.
Navegação por teclado: Tab deve funcionar logicamente.
ARIA quando necessário:
<button aria-label="Fechar">×</button>
<nav aria-label="Principal">...</nav>
<div role="alert">Mensagem importante</div>
Ferramentas: - Lighthouse (Chrome DevTools): audit a11y. - axe DevTools (extensão). - WAVE.
8.2 SEO básico
Meta tags:
<head>
<title>Título descritivo (50-60 chars)</title>
<meta name="description" content="Descrição (150-160 chars)">
<!-- Open Graph (Facebook, LinkedIn) -->
<meta property="og:title" content="Título">
<meta property="og:description" content="Descrição">
<meta property="og:image" content="https://example.com/og.jpg">
<meta property="og:url" content="https://example.com/page">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<!-- Canonical (versão preferida) -->
<link rel="canonical" href="https://example.com/page">
</head>
Semântica:
- <header>, <nav>, <main>, <article>, <footer>.
- 1 <h1> por página.
- Hierarquia de headings.
URLs limpas:
- /produtos/sapatos (bom).
- /page.php?id=123&cat=4 (mau).
Performance:
- Lighthouse score > 90.
- Core Web Vitals: LCP, FID, CLS.
- Imagens optimizadas (WebP).
- Lazy loading: <img loading="lazy">.
Sitemap.xml e robots.txt:
/sitemap.xml <- lista de páginas
/robots.txt <- instruções para crawlers
9. Boas práticas
9.1 Organização
projecto/
├── index.html
├── css/
│ ├── reset.css
│ ├── style.css
│ └── responsive.css
├── js/
│ ├── main.js
│ └── utils.js
├── images/
└── fonts/
9.2 CSS
- Mobile first.
- CSS variables para temas.
- BEM ou similar para nomes.
- Reset/normalize no início.
- Box-sizing border-box universal.
- Evitar
!important.
9.3 JavaScript
const>let> evitarvar.- Arrow functions em callbacks.
===não==.- Template literals em vez de concatenação.
- Async/await em vez de callbacks aninhados.
- Modular: separar em ficheiros / módulos ES6.
9.4 Performance
- Minificar CSS/JS em produção.
- Comprimir imagens.
- Lazy load imagens abaixo da fold.
- Cache headers apropriados.
- CDN para libraries.
9.5 Cybersegurança
- HTTPS obrigatório.
- Não confiar em input do utilizador (validar tudo).
- Escapar HTML ao mostrar input do utilizador.
- HTTP-only cookies para sessions.
- Content Security Policy (CSP).
Apêndice A · Reset CSS
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
-webkit-text-size-adjust: 100%;
}
body {
line-height: 1.5;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: #333;
background: #fff;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
a {
color: inherit;
text-decoration: none;
}
ul, ol {
list-style: none;
}
Apêndice B · Glossário
- DOM: Document Object Model.
- CSS: Cascading Style Sheets.
- a11y: accessibility.
- SEO: Search Engine Optimization.
- WCAG: Web Content Accessibility Guidelines.
- Responsive: adapta a vários tamanhos.
- Mobile-first: design começa em mobile.
- Flexbox: layout 1D.
- Grid: layout 2D.
- Pseudo-class:
:hover,:focus. - Pseudo-element:
::before,::after.
Apêndice C · Recursos
- MDN Web Docs: developer.mozilla.org — referência definitiva.
- CSS-Tricks: css-tricks.com — tutoriais e snippets.
- W3Schools: w3schools.com — básico.
- freeCodeCamp: cursos gratuitos.
- Frontend Masters: cursos pagos avançados.
- DevTools docs: Chrome / Firefox.