DevTools

Cheatsheet CSS

Folhas de estilo para design web

Voltar às linguagens
CSS
94 cards encontrados
Categorias:
Versões:

Seletores


12 cards
Seletor de elemento
p { color: blue; }
h1 { font-size: 2rem; }

Aplica a todos os elementos daquele tipo. É o seletor mais simples e menos específico.

Descendente
nav a { text-decoration: none; }
.card p { color: gray; }

Seleciona elementos dentro de outro (qualquer nível de profundidade). O espaço é o combinador descendente.

Seletores de atributo
input[type="text"] { border: 1px solid; }
a[href^="https"] { color: green; }
img[src$=".png"] { border: 2px solid; }
a[href*="exemplo"] { font-weight: bold; }

Selecionam por atributo: ^= começa com, $= termina com, *= contém.

Seletor de classe
.destaque { font-weight: bold; }
.btn-primary { background: blue; }

Seleciona elementos com class="destaque". Pode ser reutilizado em vários elementos. É o seletor mais usado.

Filho direto
ul > li { list-style: none; }
.card > h2 { margin: 0; }

O > seleciona apenas filhos diretos, não netos. Mais preciso que o descendente.

Agrupar seletores
h1, h2, h3 { margin: 0; }
.btn, .link { cursor: pointer; }

A vírgula aplica a mesma regra a vários seletores. Evita repetir código.

Seletor de ID
#topo { margin: 0; }
#menu { position: fixed; }

Seleciona o elemento com id="topo". O ID deve ser único na página. Tem alta especificidade — evita usar para estilos.

Pseudo-classes
a:hover { color: red; }
input:focus { border: 2px solid blue; }
li:first-child { font-weight: bold; }
li:nth-child(2n) { background: #f5f5f5; }
li:last-child { border: none; }

As pseudo-classes selecionam por estado (hover, focus) ou posição (first-child, nth-child). Não precisam de classe extra.

Especificidade
/* Ordem de prioridade (maior ganha): */
/* inline (1000) > #id (100) > .classe (10) > elemento (1) */

p { color: blue; }          /* 1 */
.texto { color: green; }    /* 10 */
#titulo { color: red; }     /* 100 */

A especificidade decide qual regra vence em conflito. IDs > classes > elementos. O inline e !important sobrepõem tudo.

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

Aplica a todos os elementos. Útil para resets e box-sizing global.

Pseudo-elementos
p::before { content: "» "; }
p::after { content: " «"; }
p::first-line { font-weight: bold; }
::placeholder { color: #999; }

Os pseudo-elementos (::) criam conteúdo virtual ou estilizam partes. O content é obrigatório no before/after.

Combinador irmão
/* Irmão adjacente (logo a seguir) */
h2 + p { margin-top: 0; }

/* Todos os irmãos seguintes */
h2 ~ p { color: gray; }

O + seleciona o irmão imediatamente a seguir. O ~ seleciona todos os irmãos seguintes do mesmo tipo.

Cores e Fundos


8 cards
Formas de cor
color: red;
color: #ff0000;
color: rgb(255, 0, 0);
color: rgba(255, 0, 0, 0.5);
color: hsl(0, 100%, 50%);
color: hsla(0, 100%, 50%, 0.5);

CSS aceita nomes, hex, RGB(A) e HSL(A). O HSL é mais intuitivo: matiz, saturação e luminosidade.

Gradiente radial
background: radial-gradient(circle, #fff, #000);
background: radial-gradient(ellipse at top, #a8edea, #fed6e3);

O radial-gradient irradia do centro para fora. A forma pode ser circle ou ellipse.

Cor de fundo
background-color: #f0f0f0;
background-color: rgba(0, 0, 0, 0.8);

Define a cor de fundo do elemento. Com rgba, o fundo fica semi-transparente sem afetar o texto.

Opacidade
opacity: 0.5;   /* 0 = transparente, 1 = opaco */
opacity: 0;     /* invisível mas ocupa espaço */

/* Afeta TODO o elemento (texto incluído) */
/* Para só o fundo, usa rgba() */

A opacidade afeta o elemento inteiro (filhos incluídos). Para transparência só no fundo, usa rgba() na cor.

Imagem de fundo
background-image: url("img.jpg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;

O cover preenche todo o espaço (pode cortar). O contain mostra a imagem inteira (pode deixar espaço).

Background shorthand
background: #fff url("x.jpg") no-repeat center / cover;

/* Ordem: cor | imagem | repeat | posição / size */

A shorthand junta todas as propriedades de fundo numa linha. O / separa posição de size.

Gradiente linear
background: linear-gradient(to right, #ff7e5f, #feb47b);
background: linear-gradient(135deg, #667eea, #764ba2);
background: linear-gradient(#fff, #000);

O linear-gradient cria uma transição suave entre cores. A direção pode ser to right, um ângulo, ou por defeito de cima para baixo.

Múltiplos fundos
background:
  url("logo.png") no-repeat top right,
  url("pattern.png") repeat,
  #f0f0f0;

Podes empilhar vários fundos separados por vírgula. O primeiro fica por cima dos seguintes.

Texto e Fontes


11 cards
Família de fonte
font-family: "Segoe UI", Arial, sans-serif;
font-family: monospace;

Lista de fontes com fallback. Se a primeira não existir, tenta a seguinte. Nomes com espaço precisam de aspas.

Decoração
text-decoration: underline;
text-decoration: none;        /* remove sublinhado */
text-decoration: line-through; /* riscado */
text-decoration: overline;

O none remove o sublinhado dos links. O line-through é usado para preços antigos ou tarefas concluídas.

Google Fonts
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");

body {
  font-family: "Roboto", sans-serif;
}

O @import ou a tag <link> carregam fontes do Google. O display=swap mostra texto imediatamente com fallback.

Tamanho da fonte
font-size: 16px;    /* fixo */
font-size: 1.2rem;  /* relativo à raiz */
font-size: 1.5em;   /* relativo ao pai */

O rem é relativo ao html (recomendado). O em é relativo ao pai. O px é fixo e não escala.

Espaçamento
line-height: 1.6;      /* entre linhas */
letter-spacing: 2px;   /* entre letras */
word-spacing: 5px;     /* entre palavras */

O line-height de 1.5-1.8 melhora a legibilidade. O letter-spacing é útil em maiúsculas e títulos.

Truncar com reticências
.truncar {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

As três propriedades juntas cortam o texto com "..." quando excede o contentor. Requer largura definida.

Peso e estilo
font-weight: bold;    /* 700 */
font-weight: 300;     /* light */
font-style: italic;
font-style: normal;

O font-weight aceita nomes (bold, normal) ou números (100-900). O font-style controla o itálico.

Transformação de texto
text-transform: uppercase;
text-transform: lowercase;
text-transform: capitalize;
text-transform: none;

Transforma o texto visualmente sem alterar o HTML. O capitalize põe a primeira letra de cada palavra em maiúscula.

Truncar múltiplas linhas
.truncar-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

O line-clamp limita o texto a N linhas com reticências. Funciona nos browsers modernos (prefixo -webkit-).

Alinhamento
text-align: center;
text-align: left;
text-align: right;
text-align: justify;

Alinha o texto horizontalmente. O justify distribui o texto pelas duas margens (como em jornais).

Sombra de texto
text-shadow: 2px 2px 4px #000;
text-shadow: 0 0 10px rgba(0,0,0,0.5);
text-shadow: 1px 1px 0 #fff, 2px 2px 0 #ccc;

Formato: x y blur cor. Podes empilhar várias sombras com vírgula para efeitos criativos.

Box Model


10 cards
Box model
/* Do exterior para o interior: */
/* margin > border > padding > content */

.caixa {
  margin: 20px;
  border: 1px solid #ccc;
  padding: 15px;
  width: 300px;
}

Cada elemento é uma caixa com 4 camadas: margin (fora), border, padding (dentro) e content.

Bordas
border: 1px solid #ccc;
border-radius: 8px;
border-radius: 50%;     /* círculo */
border-top: 2px dashed red;

A shorthand é espessura estilo cor. O border-radius: 50% transforma qualquer quadrado em círculo.

Display
display: block;         /* ocupa linha inteira */
display: inline;        /* flui com o texto */
display: inline-block;  /* inline + dimensões */
display: none;          /* oculta totalmente */
display: flex;
display: grid;

O display define o tipo de caixa. O none remove do fluxo. O inline-block aceita width/height mas flui inline.

box-sizing
* { box-sizing: border-box; }

/* content-box (padrão): width = só conteúdo */
/* border-box: width = conteúdo + padding + border */

O border-box faz o width incluir padding e border. Torna os cálculos muito mais previsíveis. Usa sempre.

Largura e altura
width: 100%;
max-width: 1200px;
height: 200px;
min-height: 100vh;
aspect-ratio: 16 / 9;

O max-width evita esticar demasiado. O min-height: 100vh garante altura mínima do ecrã. O aspect-ratio mantém a proporção.

Visibility vs Display
visibility: hidden;  /* oculta mas ocupa espaço */
display: none;       /* remove do layout */
opacity: 0;          /* invisível, clicável */

O visibility: hidden oculta mas mantém o espaço. O display: none remove completamente. O opacity: 0 ainda recebe cliques.

Margens
margin: 10px;                  /* todos */
margin: 10px auto;             /* centra horizontalmente */
margin: 5px 10px 15px 20px;   /* topo dir baixo esq */
margin: 10px 20px;            /* vertical | horizontal */

O auto nas laterais centra o bloco. A shorthand segue o sentido horário: topo, direita, baixo, esquerda.

Sombra de caixa
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
box-shadow: 0 0 0 3px blue;  /* "anel" */
box-shadow: inset 0 2px 4px #000;  /* interna */

Formato: x y blur spread cor. O inset faz a sombra para dentro. Sem blur/spread cria um contorno sólido.

Padding
padding: 20px;
padding: 10px 20px;    /* vertical | horizontal */
padding: 0 15px 10px;  /* topo | lados | baixo */

O padding é o espaço interno entre o conteúdo e a borda. Não aceita valores negativos (ao contrário do margin).

Overflow
overflow: hidden;    /* corta */
overflow: auto;      /* scroll se necessário */
overflow: scroll;    /* scroll sempre */
overflow: visible;   /* mostra fora (padrão) */

O overflow controla o que acontece quando o conteúdo excede a caixa. O auto só mostra scroll quando precisa.

Flexbox


10 cards
Ativar flexbox
.container {
  display: flex;
}
/* Os filhos diretos tornam-se itens flex */

O display: flex ativa o layout flexível nos filhos diretos. O container controla o alinhamento e distribuição.

flex-wrap
flex-wrap: nowrap;  /* padrão: tudo numa linha */
flex-wrap: wrap;    /* quebra para a linha seguinte */
flex-wrap: wrap-reverse;

Sem wrap, os itens comprimem-se numa linha. Com wrap, passam para a linha seguinte quando não cabem.

order e align-self
.item { order: 2; }           /* reordena visualmente */
.item { align-self: flex-end; } /* sobrepõe align-items */

O order muda a ordem visual sem alterar o HTML. O align-self sobrepõe o alinhamento num item específico.

Direção
flex-direction: row;            /* → padrão */
flex-direction: column;         /* ↓ */
flex-direction: row-reverse;    /* ← */
flex-direction: column-reverse; /* ↑ */

A direção define o eixo principal. Com column, o justify-content passa a controlar a vertical.

gap
gap: 20px;
row-gap: 10px;
column-gap: 30px;

O gap é o espaçamento moderno entre itens flex/grid. Substitui margins hacky e não adiciona espaço nas bordas.

Layout com flex (exemplo)
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
}
/* Logo à esquerda, menu à direita */

Exemplo prático: navbar com logo à esquerda e menu à direita. O space-between empurra-os para as bordas.

justify-content
justify-content: flex-start;
justify-content: center;
justify-content: flex-end;
justify-content: space-between;
justify-content: space-around;
justify-content: space-evenly;

Distribui os itens no eixo principal. O space-between cola o primeiro e último às bordas. O space-evenly dá espaços iguais.

flex shorthand
flex: 1;           /* cresce igualmente */
flex: 0 0 200px;   /* fixo, não cresce nem encolhe */
flex: 2;           /* cresce o dobro dos outros */

/* flex: grow shrink basis */

O flex combina grow (crescer), shrink (encolher) e basis (tamanho base). O flex: 1 distribui o espaço igualmente.

align-items
align-items: stretch;     /* estica (padrão) */
align-items: center;      /* centra */
align-items: flex-start;  /* topo */
align-items: flex-end;    /* baixo */
align-items: baseline;    /* linha de texto */

Alinha os itens no eixo cruzado (perpendicular). Com row, controla a vertical. O stretch é o padrão.

Centrar perfeitamente
.container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

A combinação justify + align center centra horizontal e verticalmente. O padrão mais usado para centrar qualquer coisa.

Grid


10 cards
Ativar grid
.container {
  display: grid;
}
/* Os filhos tornam-se células de grelha */

O display: grid ativa o layout em grelha. Ideal para layouts bidimensionais (linhas E colunas ao mesmo tempo).

Definir linhas
grid-template-rows: 100px auto 50px;
grid-auto-rows: minmax(100px, auto);

O grid-template-rows define alturas fixas. O grid-auto-rows controla linhas criadas automaticamente.

Alinhar no grid
justify-items: center;   /* horizontal na célula */
align-items: center;     /* vertical na célula */
place-items: center;     /* ambos (shorthand) */

justify-content: center; /* a grelha no container */

O place-items: center centra o conteúdo dentro de cada célula. O justify/align-content posiciona a grelha inteira.

Definir colunas
grid-template-columns: 200px 1fr 1fr;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(4, 250px);

O fr é uma fração do espaço disponível. O repeat(3, 1fr) cria 3 colunas iguais. Podes misturar px e fr.

Ocupar várias colunas
.item {
  grid-column: span 2;
}
.item-full {
  grid-column: 1 / -1;  /* todas */
}

O span 2 faz o item ocupar 2 colunas. O 1 / -1 estende da primeira à última coluna (full width).

Grid vs Flexbox
/* Flexbox: 1 dimensão (linha OU coluna) */
.nav { display: flex; }

/* Grid: 2 dimensões (linhas E colunas) */
.layout { display: grid; }

/* Podem ser combinados! */

Usa Flexbox para alinhar itens numa direção. Usa Grid para layouts com linhas e colunas. Combinam-se bem.

Grelha responsiva automática
grid-template-columns:
  repeat(auto-fit, minmax(250px, 1fr));

O auto-fit + minmax cria colunas que se ajustam sozinhas. Sem media queries! O padrão mais útil do Grid.

Posicionar item
.item {
  grid-column: 1 / 3;  /* colunas 1 e 2 */
  grid-row: 1 / 2;     /* linha 1 */
}

Define início / fim nas linhas da grelha. Os números referem-se às linhas (não às células).

gap
gap: 20px;
row-gap: 10px;
column-gap: 30px;

O gap define o espaço entre células. Substitui margins e é mais limpo que hacks com padding.

Áreas nomeadas
.layout {
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }

As grid-template-areas criam um layout visual com nomes. Muito legível — parece um desenho da página.

Posicionamento


8 cards
Position
position: static;    /* padrão, fluxo normal */
position: relative;  /* desloca do original */
position: absolute;  /* fora do fluxo */
position: fixed;     /* fixo no viewport */
position: sticky;    /* cola ao rolar */

O position controla como o elemento se posiciona. Só static ignora as propriedades top/right/bottom/left.

Fixed
.barra {
  position: fixed;
  bottom: 0;
  width: 100%;
}

O fixed fixa o elemento no viewport — não rola com a página. Ideal para barras de navegação e botões flutuantes.

Coordenadas
top: 10px;
right: 0;
bottom: 0;
left: 20px;
inset: 0;  /* todos a 0 (shorthand) */

As coordenadas funcionam com position (exceto static). O inset: 0 é shorthand para top/right/bottom/left todos iguais.

Sticky
.menu {
  position: sticky;
  top: 0;
}

O sticky comporta-se como relative até atingir o offset, depois "cola" como fixed. Perfeito para headers e menus.

Relative
.caixa {
  position: relative;
  top: 10px;
  left: 20px;
}
/* Desloca mas mantém o espaço original */

O relative desloca o elemento da sua posição original. O espaço que ocupava é preservado. Serve de referência para filhos absolute.

z-index
.modal { z-index: 1000; }
.dropdown { z-index: 100; }
.navbar { z-index: 50; }

O z-index controla a ordem de empilhamento (quem fica por cima). Requer position (não static). Valores maiores ficam na frente.

Absolute
.pai { position: relative; }
.filho {
  position: absolute;
  top: 0;
  right: 0;
}

O absolute posiciona relativo ao ancestral com position (não static) mais próximo. Remove o elemento do fluxo normal.

Centrar com absolute
.centro {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Combina top/left 50% com translate(-50%, -50%) para centrar. Alternativa ao flexbox para overlays e modais.

Responsivo


8 cards
Media query
@media (max-width: 768px) {
  .menu { display: none; }
  .conteudo { padding: 10px; }
}

As media queries aplicam regras conforme a largura do ecrã. O max-width é mobile-first ao contrário.

Imagens responsivas
img {
  max-width: 100%;
  height: auto;
  display: block;
}

O max-width: 100% impede a imagem de exceder o contentor. O height: auto mantém a proporção. O display: block remove espaço extra.

Breakpoints comuns
/* Mobile first (min-width): */
@media (min-width: 576px) { /* tablet */ }
@media (min-width: 768px) { /* desktop */ }
@media (min-width: 992px) { /* grande */ }
@media (min-width: 1200px) { /* xl */ }

Os breakpoints são pontos onde o layout muda. A abordagem mobile-first usa min-width (estilos base = mobile).

clamp() fluido
font-size: clamp(1rem, 2.5vw, 2rem);
width: clamp(300px, 80%, 1200px);
padding: clamp(1rem, 5vw, 3rem);

O clamp(mín, preferido, máx) cria valores fluidos com limites. Substitui media queries para tipografia e espaçamentos.

Unidade rem
html { font-size: 16px; }
h1 { font-size: 2rem; }    /* 32px */
p { font-size: 1rem; }     /* 16px */
.caixa { padding: 1.5rem; } /* 24px */

O rem é relativo ao font-size do html. Se mudares a raiz, tudo escala proporcionalmente. Recomendado para acessibilidade.

Container queries
.card-wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card { display: flex; }
}

As container queries aplicam estilos baseados no tamanho do contentor (não do viewport). O futuro do CSS responsivo.

Unidades viewport
width: 100vw;     /* largura do ecrã */
height: 100vh;    /* altura do ecrã */
font-size: 5vw;   /* escala com a largura */
min-height: 100dvh; /* altura dinâmica (mobile) */

As unidades vw/vh são percentagens do viewport. O dvh (dynamic viewport height) lida com a barra do browser mobile.

Orientação e preferências
@media (orientation: landscape) { ... }

@media (prefers-color-scheme: dark) {
  body { background: #1a1a2e; color: #eee; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none; }
}

Para além da largura, podes detetar orientação, tema escuro e preferência de movimento do utilizador.

Transições e Animações


8 cards
Transição
.btn {
  transition: all 0.3s ease;
}
.btn:hover {
  background: blue;
  transform: scale(1.05);
}

A transition anima mudanças de propriedade suavemente. Formato: propriedade duração timing-function delay.

Propriedades de animação
animation-name: piscar;
animation-duration: 2s;
animation-delay: 0.5s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-fill-mode: forwards;

Controlo fino: delay (espera), iteration-count (repetições), direction (alternate inverte), fill-mode (estado final).

Transição específica
transition: background 0.3s,
  transform 0.5s ease-in-out;

/* Evita "all" em produção (performance) */

Especifica quais propriedades animar em vez de all. Melhor performance e controlo sobre cada animação.

Efeito hover (elevação)
.card {
  transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 6px 12px rgba(0,0,0,0.15);
}

O efeito de elevação combina translateY com box-shadow. Dá a sensação de o elemento "levantar" ao passar o rato.

Transform
transform: scale(1.1);
transform: rotate(45deg);
transform: translateX(20px);
transform: translateY(-10px);
transform: skewX(5deg);

O transform aplica transformações visuais sem afetar o layout. Podes combinar: translateX(10px) scale(1.1).

Timing functions
ease            /* suave (padrão) */
linear          /* velocidade constante */
ease-in         /* começa devagar */
ease-out        /* termina devagar */
ease-in-out     /* ambos */
cubic-bezier(0.4, 0, 0.2, 1)

As timing functions controlam a aceleração. O ease-out é bom para entradas. O cubic-bezier permite curvas personalizadas.

Keyframes
@keyframes piscar {
  0% { opacity: 1; }
  50% { opacity: 0; }
  100% { opacity: 1; }
}

.item {
  animation: piscar 1s infinite;
}

Os @keyframes definem os passos da animação. O animation aplica-a com nome, duração e repetições.

Animação de entrada
@keyframes aparecer {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card {
  animation: aparecer 0.4s ease-out;
}

A animação de entrada (fade + slide) dá polish à interface. O ease-out faz o elemento "aterrar" suavemente.

Dicas e Boas Práticas


9 cards
Variáveis CSS
:root {
  --cor-principal: #3498db;
  --espaco: 1rem;
  --raio: 8px;
}

.btn {
  color: var(--cor-principal);
  padding: var(--espaco);
  border-radius: var(--raio);
}

As variáveis CSS (--nome) centralizam valores. Mudar num sítio atualiza tudo. Podem ser sobrescritas por componente.

Seleção de texto
::selection {
  background: #3498db;
  color: white;
}

O ::selection estiliza o texto quando o utilizador o seleciona. Pequeno detalhe que dá personalidade ao site.

aspect-ratio
.video {
  aspect-ratio: 16 / 9;
  width: 100%;
}

.avatar {
  aspect-ratio: 1;  /* quadrado */
  border-radius: 50%;
}

O aspect-ratio mantém a proporção sem hacks de padding. Substitui o truque do padding-top para vídeos responsivos.

Reset básico
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

O reset remove estilos padrão do browser (margins, paddings). O border-box torna os cálculos previsíveis.

Filtro de imagem
filter: grayscale(100%);
filter: blur(5px);
filter: brightness(0.8) contrast(1.2);
filter: sepia(0.5);

Os filtros aplicam efeitos visuais sem editar a imagem. Podes combinar vários na mesma declaração.

Cursor
cursor: pointer;     /* clicável */
cursor: not-allowed;  /* desativado */
cursor: grab;         /* arrastável */
cursor: text;         /* editável */

O cursor: pointer indica que o elemento é clicável. Usa-o em botões, links e elementos interativos.

Esconder scrollbar
.no-scroll {
  scrollbar-width: none;       /* Firefox */
  -ms-overflow-style: none;    /* IE */
}
.no-scroll::-webkit-scrollbar {
  display: none;               /* Chrome */
}

Oculta a barra de scroll mantendo a funcionalidade. Útil em carrosséis e listas horizontais.

Scroll suave
html {
  scroll-behavior: smooth;
}

/* Respeitar preferência do utilizador: */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

O scroll-behavior: smooth anima a rolagem ao clicar em âncoras. Respeita sempre a preferência de movimento reduzido.

Outline vs Border
/* Outline: não afeta layout, bom para focus */
input:focus {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Nunca removes o outline sem alternativa! */

O outline não ocupa espaço (não muda o layout). É essencial para acessibilidade — nunca o removas sem dar alternativa visual.