Bootstrap 5, do zero ao nível profissional
Um guia único e navegável para quem já conhece HTML e CSS básicos e quer dominar o framework front-end mais pedido em vagas júnior no Brasil — com exercícios, projetos completos e preparação para entrevistas.
☆ Como usar esta apostila
Esta página foi construída com o próprio Bootstrap 5.3. Isso significa que cada componente que você vai estudar está funcionando de verdade aqui dentro: o menu lateral usa Scrollspy, o menu do celular usa Offcanvas, os exemplos usam Cards, Accordions e tudo o mais. A apostila é, ela mesma, um projeto de referência.
Exemplos ao vivo
Blocos com borda tracejada roxa mostram o resultado renderizado. Clique em </> Ver código para revelar o HTML exato que gerou aquele resultado.
Exercícios com solução
Todo módulo termina com exercícios. Tente resolver antes de abrir a solução escondida no bloco "Ver solução".
Foco em mercado
Blocos verdes "Visão de mercado" conectam a teoria com o que empresas realmente pedem em vagas e entrevistas.
Trilha sugerida de estudo
- Semana 1: módulos 1 a 4 (fundamentos e grid) — a base de tudo.
- Semana 2: módulos 5 a 8 (utilitários, componentes, navbar).
- Semana 3: módulos 9 e 10 (formulários e componentes JS) + projeto 1.
- Semana 4: módulos 11 a 14 (nível avançado) + projetos 2 e 3.
- Semana 5: desafios finais, revisão e módulo 17 (entrevistas).
Para praticar, use o CodePen ou o VS Code com a extensão Live Server. O importante é digitar o código, não copiar e colar.
O que é o Bootstrap (e por que o mercado ainda o usa tanto)
O Bootstrap é um framework front-end: uma coleção pronta de CSS e JavaScript que resolve os problemas mais repetitivos da construção de interfaces — grade de layout responsivo, botões, formulários, menus, janelas modais — para que você não precise escrever tudo do zero em cada projeto.
Ele nasceu em 2011 dentro do Twitter (por isso o nome antigo, Twitter Blitz/Bootstrap), criado por Mark Otto e Jacob Thornton para padronizar as ferramentas internas da empresa. Foi liberado como código aberto e virou, por anos, o repositório mais estrelado do GitHub.
Linha do tempo das versões
| Versão | Ano | O que mudou de importante |
|---|---|---|
| 2.x | 2012 | Primeira versão com layout responsivo opcional. |
| 3.x | 2013 | Mobile first por padrão. Ainda aparece em sistemas legados de empresas. |
| 4.x | 2018 | Grid reescrito com Flexbox; Sass no lugar de Less; muitos sistemas corporativos usam até hoje. |
| 5.x | 2021+ | Removeu a dependência de jQuery, adicionou utilitários novos, RTL, Offcanvas e, na 5.3, dark mode nativo. É a versão desta apostila. |
Por que aprender Bootstrap em vez de ir direto para outra coisa?
- Base instalada gigante: milhões de sistemas internos, painéis administrativos, e-commerces e sites institucionais no Brasil rodam Bootstrap 3, 4 ou 5. Manutenção de legado é uma porta de entrada real para júnior.
- Vagas: Bootstrap aparece com frequência em descrições de vagas de desenvolvedor front-end júnior, full stack (especialmente PHP/Laravel, C#/.NET e Java/Spring) e web designer.
- Velocidade de entrega: agências e software houses cobram por projeto; entregar um site responsivo em dias, não semanas, é diferencial econômico direto.
- Conceitos transferíveis: grid de 12 colunas, mobile first, breakpoints e utilitários existem em quase todos os frameworks. Dominar Bootstrap facilita aprender Tailwind depois (módulo 14).
O que vem "dentro da caixa"
- Reboot: uma normalização de estilos que deixa todos os navegadores parecidos.
- Sistema de grid: 12 colunas flexíveis e responsivas (módulo 4).
- Componentes: mais de 20 blocos prontos — cards, navbar, modal, carousel...
- Utilitários: centenas de classes pequenas para margem, cor, flexbox, etc. (módulo 5).
- Plugins JavaScript: comportamento pronto para dropdown, modal, toast, sem jQuery.
- Ícones (pacote separado): a biblioteca Bootstrap Icons, com 2.000+ ícones SVG.
✍️ Exercícios do módulo 1
- Com suas palavras, explique a diferença entre uma biblioteca de componentes e escrever CSS puro. Cite 2 vantagens e 2 desvantagens de cada abordagem.
- Abra 3 vagas reais de "desenvolvedor front-end júnior" (LinkedIn, Gupy, Programathor) e anote quais tecnologias aparecem junto com Bootstrap. Guarde essa lista: ela vira seu plano de estudos.
Ver comentário da solução
1. Framework: mais velocidade, consistência visual e responsividade pronta; em troca, sites podem "parecer Bootstrap" e o CSS final carrega código não usado. CSS puro: controle e leveza totais; em troca, mais tempo, mais bugs entre navegadores e necessidade de criar seu próprio sistema de grid.
2. Padrões comuns que você vai encontrar: HTML/CSS/JS + Bootstrap + jQuery (legado), PHP/Laravel + Bootstrap + Blade, C#/.NET + Bootstrap + Razor, e WordPress + Bootstrap. Isso mostra que Bootstrap raramente anda sozinho — ele acompanha um back-end.
Instalação e primeiro projeto
Existem três formas principais de adicionar o Bootstrap a um projeto. Para estudar e para a maioria dos sites institucionais, a CDN resolve. Em projetos profissionais com build (Vite, Webpack, Laravel Mix), usa-se npm + Sass.
Opção 1 — CDN (a mais rápida)
Basta colar dois links. O CSS vai no <head> e o JavaScript antes de fechar o
</body>:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<!-- Obrigatória: sem ela, o layout responsivo não funciona no celular -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Meu primeiro projeto Bootstrap</title>
<!-- CSS do Bootstrap 5.3 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>Olá, Bootstrap!</h1>
<button class="btn btn-primary">Meu primeiro botão</button>
<!-- JS do Bootstrap (bundle já inclui o Popper, usado por dropdowns e tooltips) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
- Esquecer a
<meta name="viewport">→ o site abre "encolhido" no celular. - Importar só o CSS e esquecer o JS → dropdown, modal e navbar mobile "não abrem".
- Usar
bootstrap.min.jsem vez debootstrap.bundle.min.jssem incluir o Popper separadamente → tooltips e dropdowns quebram.
Opção 2 — Download local
Baixe o pacote compilado em getbootstrap.com,
copie as pastas css/ e js/ para o projeto e referencie localmente. Útil para
intranets sem acesso à internet — cenário comum em empresas e órgãos públicos.
<link href="assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="assets/bootstrap/js/bootstrap.bundle.min.js"></script>
Opção 3 — npm (projetos profissionais)
# dentro da pasta do projeto
npm install bootstrap@5.3.3
# o Sass é necessário para customizar (módulo 11)
npm install -D sass
E no seu arquivo Sass principal:
// styles.scss — importa o Bootstrap inteiro
@import "bootstrap/scss/bootstrap";
No módulo 11 você aprenderá a importar só as partes que usa e a trocar as variáveis de tema — é isso que separa o uso amador do uso profissional.
Estrutura de pastas recomendada (projeto simples)
meu-projeto/
├── index.html
├── css/
│ └── estilos.css ← seu CSS fica SEMPRE depois do Bootstrap
├── js/
│ └── app.js
└── img/
Testando se funcionou
Se o botão abaixo aparecer azul e com cantos arredondados, o CSS carregou. Se o dropdown abrir ao clicar, o JavaScript carregou:
✍️ Exercícios do módulo 2
- Crie um arquivo
index.htmldo zero com Bootstrap via CDN, um<h1>e três botões: umbtn-primary, umbtn-outline-dangere umbtn-success btn-lg. - Remova a linha do
<script>e tente abrir um dropdown. Observe o que acontece e explique o porquê. - Instale o Bootstrap via npm em uma pasta de teste e localize, dentro de
node_modules/bootstrap/, as pastasdist/escss/. Qual a diferença entre elas?
Ver solução
2. O dropdown não abre: quem escuta o clique e adiciona a classe .show é o JavaScript do Bootstrap. Sem o bundle, só existe o estilo, não o comportamento.
3. dist/ contém os arquivos compilados e minificados, prontos para o navegador. scss/ contém o código-fonte em Sass, que você importa e customiza em projetos com build.
Containers e breakpoints: a base do responsivo
Antes do grid, você precisa entender duas ideias que sustentam todo o Bootstrap: o container (a moldura do conteúdo) e os breakpoints (os pontos de largura de tela onde o layout muda).
Containers
.container— largura fixa por faixa de tela, centralizado, com margens automáticas. O mais usado em sites institucionais..container-fluid— ocupa 100% da largura sempre. Usado em dashboards e sistemas..container-{breakpoint}— fluido até o breakpoint indicado, fixo depois. Ex.:.container-lgé 100% em telas pequenas e fixo a partir de 992px.
Os 6 breakpoints do Bootstrap 5
Decore esta tabela — ela é usada em todas as classes responsivas do framework:
| Sufixo | Nome | Largura mínima | Pense em… |
|---|---|---|---|
| (nenhum) | extra small | 0 | celular em pé |
sm | small | ≥ 576px | celular deitado |
md | medium | ≥ 768px | tablet |
lg | large | ≥ 992px | notebook |
xl | extra large | ≥ 1200px | desktop |
xxl | extra extra large | ≥ 1400px | monitor grande |
Mobile first: o conceito que reprova ou aprova em entrevista
O Bootstrap é mobile first: toda classe sem sufixo vale a partir do celular
para cima, e os sufixos (sm, md…) sobrescrevem a partir daquele
ponto. Ou seja, col-12 col-md-6 significa: "ocupe 12 colunas no celular e
6 colunas do tablet em diante". Você projeta primeiro a versão de celular e vai "abrindo" o layout
conforme a tela cresce.
Por trás dos panos, é uma media query CSS comum:
/* o que o Bootstrap gera, simplificado */
.col-md-6 { /* nada até 767px */ }
@media (min-width: 768px) {
.col-md-6 { width: 50%; }
}
min-width (ou os sufixos do Bootstrap) para ajustar telas maiores."
✍️ Exercícios do módulo 3
- Crie uma página com um
.containere um.container-fluid, cada um com um fundo colorido (bg-primary,bg-success). Redimensione a janela e observe o comportamento. - Sem olhar a tabela, escreva de memória os 6 breakpoints e suas larguras mínimas.
- Explique com suas palavras o que significa
d-none d-lg-block(dica: módulo 5 aprofunda, mas você já consegue deduzir).
Ver solução
3. d-none esconde o elemento a partir de 0px (celular). d-lg-block sobrescreve a partir de 992px, exibindo-o como bloco. Resultado: o elemento só aparece em telas de notebook para cima — exatamente o que o menu lateral desta apostila faz.
Sistema de grid: as 12 colunas que constroem tudo
O grid é o coração do Bootstrap. A regra de ouro tem três camadas, sempre nesta ordem:
.container(ou fluid) — a moldura;.row— a linha, que é um flex container;.col-*— as colunas, que somam 12 por linha.
O básico: dividindo em colunas
Repare na terceira linha: .col sem número divide o espaço igualmente —
o Bootstrap usa Flexbox (flex: 1 0 0) para isso.
Grid responsivo: combinando breakpoints
O padrão profissional mais comum do mundo: 1 coluna no celular, 2 no tablet, 3 no desktop. Redimensione a janela do navegador para ver acontecer:
Gutters: o espaçamento entre colunas
As classes g-0 a g-5 controlam o vão (gutter) entre colunas.
gx-* só horizontal, gy-* só vertical. Em layouts de cards, quase sempre
você usará g-3 ou g-4:
<div class="row g-4"> <!-- vão de 1.5rem nos dois eixos -->
<div class="row gx-5 gy-2"> <!-- 3rem horizontal, 0.5rem vertical -->
<div class="row g-0"> <!-- colunas coladas, sem vão -->
Offset: empurrando colunas
Ordem: reordenando sem tocar no HTML
As classes order-* (0 a 5, order-first, order-last) mudam a
ordem visual. Uso clássico: no celular a imagem vem antes do texto, no desktop vem depois:
(order-md-2: vai para a direita no desktop)
Texto do produto
No celular, este texto aparece depois da imagem (ordem natural do HTML). A partir de md, o order-md-1 o traz para a esquerda.
Alinhamento dentro da row
Como a .row é um flex container, valem os utilitários de Flexbox:
- Vertical:
align-items-start | center | endna row;align-self-*na coluna. - Horizontal:
justify-content-start | center | end | between | around | evenly.
Grid aninhado (nesting)
Dentro de uma coluna, você pode abrir outra .row com novas 12 colunas:
<div class="row">
<div class="col-8">
Conteúdo principal
<div class="row"> <!-- novo grid de 12 dentro da col-8 -->
<div class="col-6">metade da col-8</div>
<div class="col-6">metade da col-8</div>
</div>
</div>
<div class="col-4">Barra lateral</div>
</div>
Row-cols: definindo colunas na linha
Em vez de repetir a classe em cada coluna, defina na row quantos itens cabem por linha — perfeito para listas de produtos vindas de um loop do back-end:
<!-- 1 por linha no celular, 2 no tablet, 4 no desktop -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
<div class="col"><div class="card">Produto</div></div>
<div class="col"><div class="card">Produto</div></div>
<!-- ...gerados pelo loop... -->
</div>
- Colocar
.col-*direto dentro de.containersem.row— quebra as margens negativas do grid. - Somar mais de 12 colunas achando que dá erro — não dá: a coluna excedente quebra para a linha de baixo (às vezes é até desejado).
- Usar grid para tudo. Para alinhar 2 ou 3 itens (ícone + texto),
d-flexcomgap-*é mais simples (módulo 5).
✍️ Exercícios do módulo 4
- Layout clássico de blog: crie uma linha com conteúdo principal (
col-lg-8) e sidebar (col-lg-4), que empilham no celular. - Vitrine: monte 8 caixas que fiquem 1 por linha no celular, 2 no
sm, 4 nolg, usando apenasrow-cols-*. - Hero invertido: texto à esquerda e imagem à direita no desktop; no celular, a imagem deve aparecer primeiro. Use
order-*.
Ver solução
<!-- 1) Blog -->
<div class="container">
<div class="row g-4">
<main class="col-12 col-lg-8">Conteúdo</main>
<aside class="col-12 col-lg-4">Sidebar</aside>
</div>
</div>
<!-- 2) Vitrine -->
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-4 g-3">
<div class="col"><div class="border p-3">1</div></div>
<!-- ...repita 8x... -->
</div>
<!-- 3) Hero invertido -->
<div class="row align-items-center">
<div class="col-md-6 order-2 order-md-1"><h1>Texto</h1></div>
<div class="col-md-6 order-1 order-md-2"><img src="..." class="img-fluid"></div>
</div>
Classes utilitárias: 80% do seu dia a dia
Utilitários são classes minúsculas que fazem uma coisa cada: uma margem, uma cor, um display. Na prática profissional, você passa mais tempo com utilitários do que com componentes. Dominar a nomenclatura permite "adivinhar" classes que você nunca viu.
Espaçamento: a fórmula {propriedade}{lado}-{breakpoint}-{tamanho}
| Parte | Valores | Significado |
|---|---|---|
| propriedade | m / p | margin / padding |
| lado | t b s e x y ou nada | top, bottom, start (esq.), end (dir.), eixo x, eixo y, todos |
| tamanho | 0 1 2 3 4 5 auto | 0, .25rem, .5rem, 1rem, 1.5rem, 3rem, automático |
<div class="mt-4"> margem superior de 1.5rem
<div class="px-3"> padding horizontal de 1rem
<div class="mb-md-5"> margem inferior de 3rem só a partir do tablet
<div class="mx-auto" style="width:200px"> centraliza bloco com largura definida
ms-* = margem esquerda, me-* = margem direita.
Cores: texto, fundo e as variações "subtle"
O Bootstrap tem 8 cores de tema, cada uma com um papel semântico. Isso importa: em
sistemas, danger é excluir, success é confirmar.
.text-primary — texto colorido
.text-muted (ou .text-body-secondary) — texto apagado, ótimo para legendas
text-bg-primary (define fundo e a cor de texto com contraste correto)
em vez de bg-primary text-white. E as variações -subtle/-emphasis
se adaptam sozinhas ao dark mode — detalhe que impressiona em code review.
Display: mostrando e escondendo por tela
d-none esconde
d-block / d-inline / d-inline-block / d-flex / d-grid
d-none d-md-block ← esconde no celular, mostra do tablet pra cima
d-lg-none ← mostra até o notebook, esconde depois
d-print-none ← some na impressão (útil para botões)
Flexbox: o canivete suíço
Provavelmente o grupo de utilitários mais valioso. Padrões que você usará todo dia:
Ícone + texto alinhados, com espaço entre eles (gap):
d-flex align-items-center gap-2
Título à esquerda, botão à direita (padrão de header de card):
Centralização total (o famoso "centralizar div"):
<!-- direção e quebra -->
flex-row flex-column flex-md-row ← coluna no celular, linha no desktop
flex-wrap flex-nowrap
<!-- crescimento -->
flex-grow-1 flex-shrink-0
<!-- espaço automático: empurra o item para a direita -->
ms-auto
Dimensão, borda, sombra e cantos
Também existem h-25/50/75/100, vw-100, vh-100
(altura da janela — clássico em heros de tela cheia) e min-vh-100.
Posição
position-relative / absolute / fixed / sticky
top-0 top-50 top-100 · start-0 · end-0 · bottom-0
translate-middle ← combina com top-50 start-50 para centralizar absoluto
✍️ Exercícios do módulo 5
- Recrie um "item de lista de contatos": avatar redondo (use um div com
rounded-circle), nome em negrito, última mensagem emtext-muted, e o horário empurrado para a direita comms-auto. Tudo comd-flex. - Crie um botão "Carrinho 🛒" com um badge de contagem sobreposto no canto, como no exemplo acima.
- Sem testar no navegador, escreva o que faz:
mt-3 mt-lg-0 px-4 d-none d-md-flex justify-content-end. Depois teste e confira.
Ver solução
<!-- 1) Item de contato -->
<div class="d-flex align-items-center gap-3 p-2 border-bottom">
<div class="rounded-circle bg-secondary" style="width:48px;height:48px;"></div>
<div>
<div class="fw-bold">Ana Souza</div>
<small class="text-muted">Combinado! Te mando amanhã.</small>
</div>
<small class="text-muted ms-auto">14:32</small>
</div>
3. Margem superior de 1rem só no mobile (zera no lg), padding horizontal de 1.5rem, escondido no celular, vira flex a partir do tablet com conteúdo alinhado à direita.
Tipografia, imagens e tabelas
Títulos e classes de texto
.display-5 — título de impacto
.h3 — aparência de h3 em qualquer tag
.lead — parágrafo de destaque, usado em heros e introduções.
.text-muted + small — legendas e observações
fw-bold · fw-semibold · fw-light · fst-italic
text-uppercase
text-center (há também text-md-start, text-end...)
Outros utilitários úteis: fs-1 a fs-6 (tamanho de fonte),
lh-1 / lh-sm / lh-base / lh-lg (altura de linha), text-truncate
(corta com reticências — exige largura definida) e text-break (quebra palavras longas,
salva layouts com URLs gigantes).
Imagens responsivas
<img src="foto.jpg" class="img-fluid" alt="Descrição da foto">
<!-- img-fluid = max-width:100%; height:auto → nunca estoura o container -->
<img src="perfil.jpg" class="rounded-circle" width="64" height="64" alt="Avatar">
<img src="print.png" class="img-thumbnail" alt="Miniatura com borda">
Para cortar imagens sem distorcer (cards de produto com fotos de tamanhos
diferentes), combine altura fixa com object-fit:
<img src="produto.jpg" class="w-100 object-fit-cover" style="height:200px" alt="...">
Proporção fixa: vídeos que não quebram
O helper .ratio mantém a proporção de iframes (YouTube, mapas) em qualquer tela:
<div class="ratio ratio-16x9">
<iframe src="https://www.youtube.com/embed/CODIGO" title="Vídeo" allowfullscreen></iframe>
</div>
<!-- também: ratio-1x1, ratio-4x3, ratio-21x9 -->
Tabelas
| # | Cliente | Plano | Status |
|---|---|---|---|
| 1 | Mariana Lopes | Pro | Ativo |
| 2 | Carlos Andrade | Básico | Pendente |
| 3 | Loja Zenith | Pro | Inadimplente |
Classes principais: table (obrigatória), table-striped
(zebrada), table-hover (destaque ao passar o mouse), table-bordered /
table-borderless, table-sm (compacta), cores contextuais por linha ou célula
(table-danger etc.) e o essencial table-dark para cabeçalhos.
.table-responsive<div class="table-responsive">, que cria rolagem horizontal quando falta espaço.
Em dashboards reais, é a diferença entre "funciona no celular" e chamado de suporte.
✍️ Exercícios do módulo 6
- Monte uma tabela de "produtos em estoque" com cabeçalho escuro, zebra, hover, e uma linha
table-warningpara produto com estoque baixo. Envolva emtable-responsive. - Crie um cabeçalho de artigo: título com
display-4, subtítulo comlead text-mutede uma imagemimg-fluid rounded shadowabaixo. - Incorpore um vídeo do YouTube que se mantenha 16x9 em qualquer largura.
Componentes essenciais
Botões
<button type="button"> para ações e <a class="btn"> apenas para
navegação. Botão dentro de <form> sem type faz submit sem querer —
bug clássico de júnior.
Badges e alerts
Pedidos 4
Entregue Em trânsitoO alert com alert-dismissible fade show + botão com
data-bs-dismiss="alert" pode ser fechado pelo usuário — sem escrever nenhum JavaScript.
Esse padrão data-bs-* se repete em quase todos os componentes interativos (módulo 10).
Cards: o componente mais usado do Bootstrap
Curso de Bootstrap 5
Front-end · 40h
Estrutura clássica: card > card-body > title, subtitle, text e ações.
MatricularCard de mesma altura
O segredo dos cards alinhados: h-100 no card e, para o botão
descer para o rodapé, d-flex flex-column no body + mt-auto no botão.
h-100.row já estica as colunas (Flexbox
align-items: stretch); falta só o card acompanhar com h-100.
List group
Feedback de carregamento: spinners e progress
✍️ Exercícios do módulo 7
- Vitrine de planos: 3 cards de preço lado a lado (empilhados no celular), mesma altura, com o plano do meio destacado com
border-primarye um badge "Mais popular". Botões alinhados no rodapé commt-auto. - Central de avisos: um alert de cada tipo (success, warning, danger, info), todos fecháveis.
- Botão de envio com estado: um botão normal e, ao lado, sua versão "carregando" (disabled + spinner pequeno), simulando o antes/depois de um clique.
Ver solução (exercício 1, estrutura)
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card h-100 text-center">
<div class="card-body d-flex flex-column">
<h5 class="card-title">Básico</h5>
<p class="display-6">R$ 29<small class="fs-6">/mês</small></p>
<ul class="list-unstyled"><li>1 usuário</li></ul>
<button class="btn btn-outline-primary mt-auto">Assinar</button>
</div>
</div>
</div>
<div class="col">
<div class="card h-100 text-center border-primary border-2 shadow">
<div class="card-header text-bg-primary">Mais popular</div>
<div class="card-body d-flex flex-column">
<h5 class="card-title">Pro</h5>
<p class="display-6">R$ 59<small class="fs-6">/mês</small></p>
<button class="btn btn-primary mt-auto">Assinar</button>
</div>
</div>
</div>
<!-- terceiro card análogo -->
</div>
Navegação: navbar, tabs, breadcrumb e paginação
Navbar responsiva — anatomia completa
A navbar é o componente com mais "peças". Entenda cada uma e você nunca mais copiará navbar da documentação sem saber o que está fazendo:
↑ Estreite a janela para menos de 992px e o menu vira o botão "hambúrguer".
Peças-chave:
navbar-expand-lg— define em qual breakpoint o menu deixa de ser hambúrguer e expande. Troquelgpormdousmconforme o design.navbar-toggler+data-bs-toggle="collapse"+data-bs-target="#id"— o botão hambúrguer que abre o bloco.collapse.navbar-collapsecom aquele id. O id precisa bater! (erro nº 1 de navbar "que não abre").me-autonaul— empurra o que vem depois (a busca) para a direita.data-bs-theme="dark"— na v5.3, deixa a navbar escura (substitui o antigonavbar-dark).- Para fixar no topo:
fixed-top(lembre de darpadding-topno body) ousticky-top.
Tabs e pills (com troca de conteúdo)
.tab-pane via data-bs-target.Troque nav-tabs por nav-pills para o visual de "pílulas".
Só o pane com show active aparece inicialmente.
Breadcrumb e paginação
✍️ Exercícios do módulo 8
- Monte uma navbar clara (
bg-light) com logo, 4 links, um dropdown "Minha conta" (Perfil, Configurações, divisor, Sair) e um botão "Entrar" à direita. Ela deve virar hambúrguer abaixo demd. - Crie uma seção de FAQ com
nav-pillsverticais à esquerda (flex-column) trocando o conteúdo à direita — gridcol-md-3/col-md-9. - Quebre de propósito: mude o
data-bs-targetdo toggler para um id errado e descreva o sintoma. Saber diagnosticar isso economiza horas no trabalho.
Ver solução (comentários)
3. Sintoma: o botão hambúrguer aparece, mas clicar não faz nada (ou abre o bloco errado). Diagnóstico profissional: abrir o DevTools, conferir se o data-bs-target corresponde exatamente ao id do .navbar-collapse, e se o bundle JS foi carregado (aba Network/Console).
Formulários e validação
Formulário é onde sistemas reais vivem: login, cadastro, checkout, filtros. É também onde empresas mais avaliam capricho — rótulos, estados de erro e acessibilidade.
Controles básicos
Regras de ouro: form-control em inputs e textareas,
form-select em selects (não form-control!), form-check-input em
checkboxes/radios, e todo campo com <label for> ligado ao id —
acessibilidade básica que recrutadores técnicos verificam.
Input groups e floating labels
Validação: o jeito Bootstrap
O Bootstrap estiliza os estados is-valid / is-invalid e mostra as
mensagens valid-feedback / invalid-feedback. O fluxo profissional usa a
validação nativa do HTML5 + a classe was-validated:
<form class="needs-validation" novalidate>
<input type="email" class="form-control" required>
<div class="invalid-feedback">Informe um e-mail válido.</div>
...
</form>
// JS padrão da documentação — memorize a lógica, não o código
document.querySelectorAll('.needs-validation').forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) { // API nativa do navegador
event.preventDefault(); // impede o envio
event.stopPropagation();
}
form.classList.add('was-validated'); // liga os estilos de erro/sucesso
});
});
novalidate não desliga a validaçãocheckValidity(). E lembre-se
da regra de ouro de mercado: validação de front-end é conveniência; a validação que protege
o sistema é sempre a do back-end. Diga isso em entrevista e ganhe pontos.
✍️ Exercícios do módulo 9
- Tela de login: card centralizado (
min-vh-100+ flex center), floating labels para e-mail e senha, switch "manter conectado", botão de largura total. - Checkout: formulário em grid — nome (6), sobrenome (6), CPF (4), telefone (4), CEP (4) com botão "Buscar" em input-group, endereço (8), número (4).
- Adicione validação Bootstrap completa ao formulário de login, com mensagens de erro em português.
Ver solução (exercício 1)
<div class="min-vh-100 d-flex align-items-center justify-content-center bg-light">
<div class="card shadow p-4" style="max-width:400px; width:100%;">
<h4 class="text-center mb-4">Entrar</h4>
<form class="needs-validation" novalidate>
<div class="form-floating mb-3">
<input type="email" class="form-control" id="email" placeholder="a@a.com" required>
<label for="email">E-mail</label>
<div class="invalid-feedback">Informe um e-mail válido.</div>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="senha" placeholder="senha" required minlength="6">
<label for="senha">Senha</label>
<div class="invalid-feedback">Mínimo de 6 caracteres.</div>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" role="switch" id="lembrar">
<label class="form-check-label" for="lembrar">Manter conectado</label>
</div>
<div class="d-grid"><button class="btn btn-primary">Entrar</button></div>
</form>
</div>
</div>
Componentes interativos (JavaScript)
Todos seguem o mesmo padrão mental: um gatilho com
data-bs-toggle="tipo" apontando (via data-bs-target ou href)
para um alvo com id. Entendeu um, entendeu todos.
Modal
Variações que caem em prova prática: modal-dialog-centered (vertical),
modal-lg / modal-xl / modal-sm, modal-dialog-scrollable e
data-bs-backdrop="static" (não fecha clicando fora — bom para formulários).
Accordion (collapse)
data-bs-parent faz um fechar quando outro abre.Carousel
Toast, Offcanvas, Tooltip e Popover
🛒 Carrinho
offcanvas-start / end / top / bottom.
document.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach(el => new bootstrap.Tooltip(el));
document.querySelectorAll('[data-bs-toggle="popover"]')
.forEach(el => new bootstrap.Popover(el));
✍️ Exercícios do módulo 10
- CRUD visual: uma tabela de usuários com botão "Excluir" em cada linha abrindo um modal de confirmação centralizado com backdrop estático.
- FAQ completo: accordion com 5 perguntas, apenas uma aberta por vez, primeira aberta por padrão.
- Carrinho lateral: botão "🛒 Carrinho (2)" na navbar abrindo um offcanvas pela direita com dois itens (list-group) e total.
- Feedback de ação: um botão "Salvar" que dispara um toast de sucesso (você vai precisar de 3 linhas de JS — espie o módulo 12 se travar).
Customização: Sass, variáveis CSS e dark mode
É aqui que o "site com cara de Bootstrap" vira o site da sua empresa. Existem três níveis de customização, do mais simples ao mais poderoso:
Nível 1 — Sobrescrever com seu próprio CSS
Funciona para ajustes pontuais. Seu arquivo vem depois do Bootstrap:
/* estilos.css — carregado após bootstrap.min.css */
.btn-primary {
--bs-btn-bg: #e91e63; /* melhor: mexer nas variáveis CSS do componente */
--bs-btn-border-color: #e91e63;
--bs-btn-hover-bg: #c2185b;
}
Desde a v5.2, cada componente expõe variáveis CSS (--bs-btn-bg,
--bs-card-border-radius…). Sobrescrevê-las é mais limpo do que brigar com especificidade.
Nível 2 — Variáveis CSS globais
:root {
--bs-body-font-family: 'Inter', system-ui, sans-serif;
--bs-body-bg: #fafafa;
--bs-border-radius: .75rem;
}
Rápido e sem build — mas não recompila tudo que depende das cores (hovers, subtle, gradientes).
Nível 3 — Sass: o jeito profissional
Com npm + Sass você redefine as variáveis antes de importar o Bootstrap, e ele recalcula todo o framework a partir delas — hovers, variações subtle, contrastes:
// styles.scss
// 1) Sobrescreva variáveis ANTES do import (todas têm !default no fonte)
$primary: #e91e63; // sua cor de marca
$secondary: #495057;
$font-family-sans-serif: 'Inter', system-ui, sans-serif;
$border-radius: .75rem;
$enable-shadows: true; // "feature flags" do framework
// 2) Adicione cores novas ao theme map (vira btn-marca, bg-marca, text-marca...)
$custom-colors: (
"marca": #6f42c1,
"whatsapp": #25d366
);
// 3) Importe as partes necessárias
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
$theme-colors: map-merge($theme-colors, $custom-colors);
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/card";
// ...importe SÓ o que o projeto usa = CSS final muito menor
@import "bootstrap/scss/helpers";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api"; // gera as classes utilitárias
# compilar
npx sass styles.scss dist/styles.css --style=compressed
A API de utilitários: criando suas próprias classes
Recurso muito avançado e valorizado: o mapa $utilities gera as classes
utilitárias. Você pode estender:
@import "bootstrap/scss/utilities";
// cria .cursor-pointer, .cursor-grab + versões responsivas (.cursor-md-pointer)
$utilities: map-merge($utilities, (
"cursor": (
property: cursor,
class: cursor,
responsive: true,
values: auto pointer grab
)
));
@import "bootstrap/scss/utilities/api";
Dark mode (v5.3): nativo e simples
O tema é controlado pelo atributo data-bs-theme — no <html> para a
página toda, ou em qualquer elemento para "ilhas" de tema:
<html data-bs-theme="dark"> <!-- página inteira escura -->
<div class="card" data-bs-theme="dark">só este card é escuro</div>
Tema claro
Card normal, herdando o tema da página.
Tema escuro
Mesmo HTML, só com data-bs-theme="dark" no pai.
Botão de alternância (o famoso "toggle de dark mode" de portfólio):
const html = document.documentElement;
document.querySelector('#btnTema').addEventListener('click', () => {
const novo = html.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
html.setAttribute('data-bs-theme', novo);
});
$primary da marca + import parcial demonstra
senioridade. Cite também que cores em Sass são recalculadas com funções de contraste
(color-contrast()) — o Bootstrap decide sozinho se o texto do botão fica preto ou branco.
✍️ Exercícios do módulo 11
- Sem build: usando só variáveis CSS, mude a cor primária dos botões e o raio de borda global de uma página.
- Com npm + Sass: crie um tema com
$primary: #ff6b35, fonte Poppins e uma cor extra "whatsapp" no theme map. Gere um botãobtn-whatsapp. - Implemente o botão de dark mode com o JS acima e salve a preferência (pesquise
prefers-color-schemepara respeitar o sistema do usuário).
JavaScript avançado: controlando componentes por código
Os atributos data-bs-* resolvem 80% dos casos. Os outros 20% — abrir um modal após uma
resposta do servidor, disparar um toast ao salvar, fechar um offcanvas programaticamente — exigem a
API JavaScript do Bootstrap.
Criando e controlando instâncias
// cada componente é uma classe no objeto global `bootstrap`
const meuModal = new bootstrap.Modal('#modalPedido', {
backdrop: 'static', // opções: as mesmas dos data-attributes
keyboard: false
});
meuModal.show();
meuModal.hide();
// pegar uma instância já criada (por data-attributes, por exemplo)
const t = bootstrap.Toast.getOrCreateInstance('#meuToast');
t.show();
O padrão getInstance() / getOrCreateInstance() evita o erro de criar duas
instâncias sobre o mesmo elemento (comportamento duplicado, animações "piscando").
Eventos: reagindo ao ciclo de vida
Todos os componentes emitem eventos no formato {ação}.bs.{componente}. Os quatro de
modal se repetem em quase todos: show (antes de abrir), shown (depois de abrir),
hide, hidden.
const el = document.getElementById('modalPedido');
el.addEventListener('shown.bs.modal', () => {
el.querySelector('input')?.focus(); // foca o 1º campo ao abrir (UX!)
});
el.addEventListener('hidden.bs.modal', () => {
el.querySelector('form')?.reset(); // limpa o form ao fechar
});
// cancelar a abertura condicionalmente
el.addEventListener('show.bs.modal', (e) => {
if (!usuarioLogado) e.preventDefault();
});
Caso real: toast de sucesso após salvar via fetch
async function salvar(dados) {
const btn = document.querySelector('#btnSalvar');
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Salvando…';
try {
const resp = await fetch('/api/clientes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(dados)
});
if (!resp.ok) throw new Error('Falha ao salvar');
bootstrap.Toast.getOrCreateInstance('#toastOk').show();
bootstrap.Modal.getInstance('#modalCliente')?.hide();
} catch (err) {
bootstrap.Toast.getOrCreateInstance('#toastErro').show();
} finally {
btn.disabled = false;
btn.textContent = 'Salvar';
}
}
Esse trecho reúne meia dúzia de padrões que aparecem em qualquer sistema: estado de carregamento no botão, feedback com toast, fechamento programático de modal e tratamento de erro. Vale digitar e entender linha a linha.
Bootstrap com frameworks JS (React, Vue, Angular)
O JS do Bootstrap manipula o DOM diretamente — e React/Vue/Angular também querem ser donos do DOM. Misturar os dois causa bugs sutis. As soluções de mercado:
- React: usar React-Bootstrap ou reactstrap — componentes React que reimplementam o comportamento, usando só o CSS do Bootstrap.
- Angular: ng-bootstrap.
- Vue: BootstrapVueNext.
- Usar só o CSS do Bootstrap (grid + utilitários) é sempre seguro em qualquer framework.
✍️ Exercícios do módulo 12
- Crie um modal que, ao abrir, foca automaticamente o primeiro input (
shown.bs.modal). - Faça um botão "Salvar" que mostra spinner por 1,5s (use
setTimeoutsimulando o servidor) e depois dispara um toast de sucesso. - Desafio: um carousel controlado por código — dois botões externos chamando
.prev()e.next()na instânciabootstrap.Carousel.
Performance, acessibilidade e boas práticas
Performance: entregando menos bytes
- Use sempre os arquivos
.minem produção. - Import parcial via Sass (módulo 11): projetos que usam só grid + utilitários + botões podem cortar o CSS pela metade ou mais.
- PurgeCSS: em builds modernos, remove automaticamente classes não usadas no HTML — o CSS do Bootstrap costuma cair de ~230 KB para 20–40 KB.
- CDN com cache: jsDelivr serve com cache agressivo; usuários que visitaram outros sites com o mesmo arquivo podem já tê-lo.
- Carregue o JS no fim do body (ou com
defer) para não bloquear a renderização. - Imagens são quase sempre o gargalo real: use
loading="lazy", formatos modernos (WebP/AVIF) e dimensões definidas para evitar layout shift.
Acessibilidade (a11y): o que o Bootstrap dá e o que é seu dever
O Bootstrap já traz muita coisa: contraste calculado, foco visível, atributos ARIA nos exemplos. Mas o framework não pode adivinhar seu conteúdo. Checklist mínimo profissional:
altdescritivo em toda imagem informativa (alt=""nas decorativas);<label>ligado a todo campo de formulário (ouaria-label);- Botões só com ícone precisam de texto para leitores de tela:
<span class="visually-hidden">Fechar</span>; - Não transmitir informação só por cor — acompanhe com texto/ícone;
- HTML semântico:
<nav>,<main>,<header>, hierarquia correta de headings; - Testar navegação completa só com Tab e Enter.
A classe visually-hidden é a ferramenta-chave: esconde visualmente, mas leitores de
tela leem. Você a viu em vários exemplos desta apostila (spinners, carousel).
Boas práticas de organização
- Não repita blocos gigantes: se o back-end é PHP/Laravel, use partials/components; se é estático, considere um gerador ou includes.
- Crie classes de componente próprias para padrões repetidos, em vez de 15 utilitários colados em 40 lugares:
.card-produto { /* seu padrão */ } /* melhor ainda, com Sass, reutilizando os mixins do Bootstrap */ - Nunca edite o arquivo do Bootstrap. Sobrescreva por fora ou recompile via Sass. Editar o fonte quebra qualquer atualização futura (e sua reputação no time).
- Versione escolhas no README: versão do Bootstrap, como compilar, onde ficam as variáveis do tema.
✍️ Exercícios do módulo 13
- Rode o Lighthouse em um projeto seu e anote as 3 principais recomendações. Corrija pelo menos uma.
- Navegue por um formulário seu usando apenas o teclado. Consegue chegar, preencher e enviar tudo? Onde o foco "some"?
- Pegue um card que você repetiu 6 vezes e extraia os utilitários repetidos para uma classe própria em seu CSS.
Bootstrap vs Tailwind vs os outros
Você vai ouvir essa comparação em entrevista. A resposta madura não é torcida de futebol: é saber quando cada um brilha.
| Bootstrap 5 | Tailwind CSS | Bulma | Material UI (React) | |
|---|---|---|---|---|
| Filosofia | Componentes prontos + utilitários | Utility-first: só utilitários, você monta os componentes | Componentes prontos, só CSS (sem JS) | Componentes React seguindo o Material Design do Google |
| Curva de aprendizado | Suave — resultados no 1º dia | Média — exige saber bem CSS | Suave | Média/alta — exige React |
| JavaScript incluso | Sim (modal, dropdown…) | Não (usa-se Alpine.js, Headless UI…) | Não | Sim (componentes React) |
| Visual padrão | Reconhecível ("cara de Bootstrap") — customizável via Sass | Nenhum: cada projeto nasce único | Limpo e moderno | Cara de app Google |
| Onde domina no mercado | Sistemas corporativos, painéis admin, PHP/.NET/Java, WordPress, legado | Startups, produtos SaaS, projetos React/Next.js novos | Projetos menores, MVPs | Aplicações React corporativas |
| Tamanho final | ~230 KB cheio; 20–60 KB com purge/Sass parcial | Muito pequeno (gera só o que usa) | ~200 KB | Depende do bundle JS |
O mesmo botão, três dialetos
<!-- Bootstrap: classe de componente -->
<button class="btn btn-primary btn-lg">Comprar</button>
<!-- Tailwind: utilitários puros -->
<button class="bg-blue-600 hover:bg-blue-700 text-white font-semibold
text-lg px-6 py-3 rounded-lg">Comprar</button>
<!-- CSS puro: você escreve a classe -->
<button class="botao-comprar">Comprar</button>
Repare: quem domina os utilitários do Bootstrap (módulo 5) já pensa "meio Tailwind". A transição entre os dois é questão de dias, não meses.
Como responder "Bootstrap ou Tailwind?" em entrevista
"Depende do contexto. Para painéis internos, sistemas com back-end tradicional e times com prazos curtos, Bootstrap entrega mais rápido com componentes e JS prontos. Para produtos com design system próprio e stack React/Next, Tailwind dá mais liberdade sem sobrescrever estilos. Eu me sinto confortável nos dois modelos porque a base — box model, Flexbox, responsividade — é a mesma."
✍️ Exercícios do módulo 14
- Recrie o card de plano do módulo 7 no Tailwind Play. Anote o que foi mais fácil e mais difícil em cada framework.
- Escreva (de verdade, em texto) sua própria resposta para "Bootstrap ou Tailwind?" com um exemplo de projeto para cada.
Projetos completos para o portfólio
Três projetos em ordem de dificuldade. Cada um cobre um tipo de trabalho que empresas realmente contratam. Monte cada um do zero seguindo o roteiro; o código completo de referência fica escondido para consulta.
🛠️ Projeto 1 — Landing page "TechFix Assistência Técnica"
Nível: iniciante+ · Tempo estimado: 3–5h · Simula: freela para comércio local
Requisitos do "cliente":
- Navbar escura fixa no topo, com links âncora para as seções e botão "Orçamento" destacado;
- Hero em tela cheia (
min-vh-100) com título, subtítulo e dois botões; - Seção "Serviços" com 3 cards de mesma altura (celular: empilhados);
- Seção "Depoimentos" com carousel;
- Formulário de contato validado (nome, e-mail, telefone, mensagem);
- Rodapé com 3 colunas (sobre, links, contato) que empilham no celular.
Ver código completo de referência (projeto 1)
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TechFix — Assistência Técnica</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body style="padding-top:56px"><!-- compensa a navbar fixed-top -->
<nav class="navbar navbar-expand-lg fixed-top bg-dark" data-bs-theme="dark">
<div class="container">
<a class="navbar-brand fw-bold" href="#">🔧 TechFix</a>
<button class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#menu">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="menu">
<ul class="navbar-nav ms-auto align-items-lg-center">
<li class="nav-item"><a class="nav-link" href="#servicos">Serviços</a></li>
<li class="nav-item"><a class="nav-link" href="#depoimentos">Depoimentos</a></li>
<li class="nav-item ms-lg-2"><a class="btn btn-warning" href="#contato">Orçamento</a></li>
</ul>
</div>
</div>
</nav>
<header class="min-vh-100 d-flex align-items-center text-white"
style="background:linear-gradient(135deg,#0d1b2a,#1b4965)">
<div class="container text-center">
<h1 class="display-3 fw-bold">Seu computador novo de novo</h1>
<p class="lead col-lg-8 mx-auto">Manutenção de notebooks e PCs com diagnóstico
gratuito e garantia de 90 dias.</p>
<div class="d-flex gap-3 justify-content-center flex-wrap mt-4">
<a href="#contato" class="btn btn-warning btn-lg">Pedir orçamento</a>
<a href="#servicos" class="btn btn-outline-light btn-lg">Ver serviços</a>
</div>
</div>
</header>
<section id="servicos" class="py-5">
<div class="container">
<h2 class="text-center mb-5">Serviços</h2>
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card h-100 text-center shadow-sm">
<div class="card-body d-flex flex-column">
<div class="fs-1">💻</div>
<h5 class="card-title">Formatação</h5>
<p class="card-text">Sistema limpo, drivers atualizados e backup dos seus arquivos.</p>
<a href="#contato" class="btn btn-outline-primary mt-auto">A partir de R$ 120</a>
</div>
</div>
</div>
<div class="col">
<div class="card h-100 text-center shadow-sm">
<div class="card-body d-flex flex-column">
<div class="fs-1">⚡</div>
<h5 class="card-title">Upgrade SSD</h5>
<p class="card-text">Seu notebook até 10x mais rápido com SSD e mais memória.</p>
<a href="#contato" class="btn btn-outline-primary mt-auto">A partir de R$ 250</a>
</div>
</div>
</div>
<div class="col">
<div class="card h-100 text-center shadow-sm">
<div class="card-body d-flex flex-column">
<div class="fs-1">🧹</div>
<h5 class="card-title">Limpeza interna</h5>
<p class="card-text">Troca de pasta térmica e limpeza contra superaquecimento.</p>
<a href="#contato" class="btn btn-outline-primary mt-auto">A partir de R$ 90</a>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="depoimentos" class="py-5 bg-light">
<div class="container" style="max-width:720px">
<h2 class="text-center mb-4">Quem já usou, recomenda</h2>
<div id="carDepo" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner text-center px-5">
<div class="carousel-item active">
<p class="lead">"Notebook voltou voando. Atendimento nota 10!"</p>
<strong>— Juliana M.</strong>
</div>
<div class="carousel-item">
<p class="lead">"Diagnóstico honesto, preço justo. Virei cliente."</p>
<strong>— Roberto S.</strong>
</div>
</div>
<button class="carousel-control-prev" data-bs-target="#carDepo" data-bs-slide="prev">
<span class="carousel-control-prev-icon" style="filter:invert(1)"></span>
</button>
<button class="carousel-control-next" data-bs-target="#carDepo" data-bs-slide="next">
<span class="carousel-control-next-icon" style="filter:invert(1)"></span>
</button>
</div>
</div>
</section>
<section id="contato" class="py-5">
<div class="container" style="max-width:640px">
<h2 class="text-center mb-4">Peça seu orçamento</h2>
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-6">
<label class="form-label" for="nome">Nome</label>
<input class="form-control" id="nome" required>
<div class="invalid-feedback">Informe seu nome.</div>
</div>
<div class="col-md-6">
<label class="form-label" for="tel">Telefone/WhatsApp</label>
<input class="form-control" id="tel" type="tel" required>
<div class="invalid-feedback">Informe um telefone.</div>
</div>
<div class="col-12">
<label class="form-label" for="email">E-mail</label>
<input class="form-control" id="email" type="email" required>
<div class="invalid-feedback">Informe um e-mail válido.</div>
</div>
<div class="col-12">
<label class="form-label" for="msg">Descreva o problema</label>
<textarea class="form-control" id="msg" rows="4" required></textarea>
<div class="invalid-feedback">Conte o que está acontecendo.</div>
</div>
<div class="col-12 d-grid">
<button class="btn btn-primary btn-lg">Enviar</button>
</div>
</form>
</div>
</section>
<footer class="bg-dark text-light py-5" data-bs-theme="dark">
<div class="container">
<div class="row g-4">
<div class="col-md-4">
<h5>🔧 TechFix</h5>
<p class="text-body-secondary">Assistência técnica de confiança desde 2015.</p>
</div>
<div class="col-md-4">
<h6>Links</h6>
<ul class="list-unstyled">
<li><a class="link-light link-underline-opacity-0" href="#servicos">Serviços</a></li>
<li><a class="link-light link-underline-opacity-0" href="#contato">Contato</a></li>
</ul>
</div>
<div class="col-md-4">
<h6>Contato</h6>
<p class="text-body-secondary mb-0">📞 (11) 99999-0000<br>📍 Rua Exemplo, 123 — SP</p>
</div>
</div>
<hr>
<p class="text-center text-body-secondary small mb-0">© 2026 TechFix</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.querySelectorAll('.needs-validation').forEach(form => {
form.addEventListener('submit', e => {
if (!form.checkValidity()) { e.preventDefault(); e.stopPropagation(); }
form.classList.add('was-validated');
});
});
</script>
</body>
</html>
📊 Projeto 2 — Painel administrativo "VendaMais"
Nível: intermediário · Tempo estimado: 5–8h · Simula: sistema interno de empresa (o trabalho mais comum de Bootstrap no mercado)
Requisitos:
- Layout com sidebar fixa no desktop que vira offcanvas no mobile;
- Topbar com busca e dropdown de usuário;
- 4 cards de métricas (
row-cols-1 row-cols-sm-2 row-cols-xl-4) com número grande, rótulo e badge de variação; - Tabela responsiva de pedidos com badges de status e botões de ação abrindo modal;
- Barra de progresso de meta mensal;
- Toast de confirmação ao "excluir" um pedido.
Ver esqueleto de referência (projeto 2 — estrutura principal)
<body>
<div class="d-flex">
<!-- SIDEBAR: visível só no desktop -->
<aside class="bg-dark text-white p-3 d-none d-lg-block vh-100 position-sticky top-0"
style="width:250px" data-bs-theme="dark">
<h5 class="mb-4">📈 VendaMais</h5>
<ul class="nav nav-pills flex-column gap-1">
<li><a class="nav-link active" href="#">Dashboard</a></li>
<li><a class="nav-link text-white" href="#">Pedidos</a></li>
<li><a class="nav-link text-white" href="#">Clientes</a></li>
<li><a class="nav-link text-white" href="#">Relatórios</a></li>
</ul>
</aside>
<!-- mesma sidebar, versão offcanvas para mobile -->
<div class="offcanvas offcanvas-start text-bg-dark" id="menuLateral">
<div class="offcanvas-header">
<h5 class="offcanvas-title">📈 VendaMais</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body"><!-- mesmos links --></div>
</div>
<!-- ÁREA PRINCIPAL -->
<main class="flex-grow-1 bg-light min-vh-100">
<nav class="navbar bg-white border-bottom px-3">
<button class="btn btn-outline-secondary d-lg-none"
data-bs-toggle="offcanvas" data-bs-target="#menuLateral">☰</button>
<form class="d-none d-md-flex ms-3 flex-grow-1" style="max-width:360px">
<input class="form-control" type="search" placeholder="Buscar pedido…">
</form>
<div class="dropdown ms-auto">
<button class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown">👤 Ana</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#">Perfil</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#">Sair</a></li>
</ul>
</div>
</nav>
<div class="container-fluid p-4">
<h4 class="mb-4">Dashboard</h4>
<!-- MÉTRICAS -->
<div class="row row-cols-1 row-cols-sm-2 row-cols-xl-4 g-3 mb-4">
<div class="col"><div class="card shadow-sm"><div class="card-body">
<div class="text-muted small">Vendas hoje</div>
<div class="fs-3 fw-bold">R$ 4.280</div>
<span class="badge text-bg-success">▲ 12%</span>
</div></div></div>
<!-- +3 cards: Pedidos, Novos clientes, Ticket médio -->
</div>
<!-- META -->
<div class="card shadow-sm mb-4"><div class="card-body">
<div class="d-flex justify-content-between">
<strong>Meta do mês</strong><span>R$ 68 mil / R$ 100 mil</span>
</div>
<div class="progress mt-2" style="height:10px">
<div class="progress-bar bg-success" style="width:68%"></div>
</div>
</div></div>
<!-- TABELA -->
<div class="card shadow-sm">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<strong>Pedidos recentes</strong>
<button class="btn btn-sm btn-primary">+ Novo pedido</button>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr><th>#</th><th>Cliente</th><th>Valor</th><th>Status</th><th></th></tr>
</thead>
<tbody>
<tr>
<td>#1042</td><td>Marina P.</td><td>R$ 350,00</td>
<td><span class="badge text-bg-success">Pago</span></td>
<td class="text-end">
<button class="btn btn-sm btn-outline-danger"
data-bs-toggle="modal" data-bs-target="#modalDel">Excluir</button>
</td>
</tr>
<!-- mais linhas: Pendente (warning), Cancelado (danger)… -->
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
<!-- modal de exclusão + toast de sucesso: mesmos padrões dos módulos 10 e 12 -->
</body>
📝 Projeto 3 — Cadastro em etapas com tema próprio
Nível: avançado · Tempo estimado: 6–10h · Simula: teste técnico de vaga júnior/pleno
Requisitos:
- Formulário de cadastro dividido em 3 etapas (Dados pessoais → Endereço → Confirmação) usando panes de tab sem os botões de tab visíveis — a navegação é por botões "Avançar/Voltar" controlados via
bootstrap.Tab(API JS); - Barra de progresso indicando a etapa (33% / 66% / 100%);
- Validação Bootstrap por etapa: só avança se os campos da etapa atual forem válidos;
- Busca de CEP real na API
viacep.com.brpreenchendo os campos de endereço (fetch); - Tema customizado via Sass: cor primária própria, fonte do Google Fonts,
$border-radiusmaior; - Dark mode com botão de alternância;
- Modal de sucesso ao finalizar + código publicado no GitHub com README.
Ver as partes-chave (projeto 3 — navegação por etapas e CEP)
// ETAPAS: panes escondidos controlados por código
const etapas = ['#etapa1', '#etapa2', '#etapa3'];
let atual = 0;
const barra = document.querySelector('#barraProgresso');
function irPara(i) {
atual = i;
bootstrap.Tab.getOrCreateInstance(
document.querySelector(`[data-bs-target="${etapas[i]}"]`)
).show();
barra.style.width = ((i + 1) / etapas.length * 100) + '%';
}
document.querySelectorAll('.btn-avancar').forEach(btn => {
btn.addEventListener('click', () => {
const pane = document.querySelector(etapas[atual]);
// valida SÓ os campos da etapa atual
const invalido = [...pane.querySelectorAll('input,select')].some(c => !c.checkValidity());
pane.classList.add('was-validated');
if (!invalido) irPara(atual + 1);
});
});
// CEP: preenchimento automático com a API pública ViaCEP
document.querySelector('#cep').addEventListener('blur', async (e) => {
const cep = e.target.value.replace(/\D/g, '');
if (cep.length !== 8) return;
const resp = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
const d = await resp.json();
if (d.erro) return;
document.querySelector('#rua').value = d.logradouro;
document.querySelector('#bairro').value = d.bairro;
document.querySelector('#cidade').value = d.localidade;
document.querySelector('#uf').value = d.uf;
});
Os panes usam a mesma estrutura de tab-pane fade do módulo 8, mas com a
ul.nav escondida (d-none) — os "tabs" existem só para a API controlar. Esse projeto
junta tudo: grid, formulários, validação, API JS do Bootstrap, fetch e Sass. Com ele no GitHub (com README
e prints), você tem um portfólio que responde sozinho à pergunta "você sabe Bootstrap?".
- Publique no GitHub Pages, Netlify ou Vercel — link ao vivo vale mais que print.
- README com: o que é, tecnologias, como rodar, prints (desktop e celular) e o que você aprendeu.
- No LinkedIn, publique o antes/depois ou um vídeo curto navegando no projeto.
Desafios finais (sem roteiro)
Agora sem passo a passo — como no trabalho. Receba o "pedido do cliente" e resolva:
position-fixed bottom-0 end-0 m-3). 100% mobile first.
ml-*/mr-* por ms-*/me-*, data-toggle por data-bs-toggle, remova o jQuery, form-group por utilitários de margem, badge-* por text-bg-*. Esse exercício simula manutenção de sistema legado — trabalho real de júnior.
Mercado de trabalho e entrevistas
Onde o Bootstrap te emprega
- Software houses e agências: sites institucionais, landing pages, painéis — volume alto, prazos curtos, Bootstrap em peso.
- Empresas com stack tradicional (PHP/Laravel, C#/.NET, Java/Spring, Python/Django): o front dos sistemas internos costuma ser Bootstrap renderizado pelo servidor.
- WordPress: muitos temas são construídos sobre Bootstrap; freelas abundam.
- Freelancer direto: comércio local, profissionais liberais, cardápios, páginas de captura para tráfego pago.
- Manutenção de legado: migrações BS3→BS5 e correções de responsividade são porta de entrada real.
Palavras para buscar vagas: desenvolvedor front-end júnior, desenvolvedor web, full stack júnior, web designer + filtro "Bootstrap". Combine sempre com HTML, CSS, JavaScript e Git — o Bootstrap nunca é exigido sozinho.
Checklist de empregabilidade
Técnico
- HTML semântico e CSS (box model, Flexbox) sólidos
- Grid e utilitários do Bootstrap de cabeça
- Validação de formulários + API JS básica
- Customização via Sass (diferencial)
- Git/GitHub no fluxo diário
- JavaScript: DOM, eventos, fetch
Portfólio
- 3+ projetos no ar com link e README caprichado
- Pelo menos 1 projeto "de sistema" (painel), não só landing
- Prints mobile + desktop provando responsividade
- Lighthouse alto citado no README
- LinkedIn com os projetos publicados
Perguntas de entrevista — com respostas modelo
min-width: classes sem sufixo valem desde 0px e os sufixos sm/md/lg/xl/xxl sobrescrevem a partir de cada breakpoint. Por isso col-12 col-md-6 é largura total no celular e metade do tablet em diante."
.col sem número divide o espaço igualmente via flex: 1 0 0."
data-toggle virou data-bs-toggle, left/right viraram start/end (ml-*→ms-*) para suporte RTL, breakpoint xxl novo, Offcanvas como componente, form-group removido em favor de utilitários, ícones em pacote próprio e, na 5.3, dark mode nativo com data-bs-theme."
--bs-primary, --bs-btn-bg) num CSS carregado depois. Projeto sério: npm + Sass, redefinindo $primary, $font-family-base etc. antes do import — o framework recompila hovers e variações a partir delas — e importando só os módulos usados para reduzir o bundle. Nunca editar o arquivo do Bootstrap diretamente."
data-bs-target bate com o id do alvo?; (3) é data-bs-toggle ou o antigo data-toggle de BS4?; (4) para tooltip/popover, houve inicialização manual?; (5) há duas versões de Bootstrap conflitando na página? Esse roteiro resolve 95% dos casos."
h-100 no card. Para alinhar os botões no rodapé: d-flex flex-column no card-body e mt-auto no botão."
visually-hidden em botões só de ícone, não depender só de cor para transmitir estado e testar a navegação por teclado. O Bootstrap dá a base (contraste, foco, ARIA nos padrões), mas o conteúdo é responsabilidade minha."
Plano de 30 dias pós-apostila
- Dias 1–10: finalize os 3 projetos, publique com README e link ao vivo.
- Dias 11–15: desafios do módulo 16 + revisão dos módulos que travou.
- Dias 16–20: JavaScript focado (DOM, eventos, fetch) — é o que mais reprova júnior, não o CSS.
- Dias 21–25: LinkedIn e GitHub arrumados; publique cada projeto como post; peça feedback em comunidades (Discord/Reddit br-dev).
- Dias 26–30: aplique para 5+ vagas/dia; trate cada teste técnico recusado como aula grátis — peça feedback sempre.