Torne o formulário utilizável em telas pequenas

HTMLBeginner
Pratique Agora

Introdução

A Still & Spoke realiza pequenas oficinas de cerâmica aos sábados. A página do estúdio está pronta, mas o formulário precisa de rótulos visíveis, verificações nativas, uma observação condicional de assistência e um layout utilizável pelo teclado em telas estreitas. Você fará uma inscrição local real, além de exibir uma mensagem de sucesso. O Study form independente oferece um exemplo completo antes das alterações.

Você precisa conhecer elementos HTML básicos, seletores e layout CSS, além de objetos, funções, seleção do DOM e eventos JavaScript. Este laboratório apresenta associações de rótulos, restrições do navegador, descrições acessíveis, required condicional, foco visível e as pequenas regras responsivas usadas aqui. No primeiro laboratório, um manipulador de envio e uma API local preparados processam as solicitações; o próximo constrói esse manipulador. Pessoas e endereços são sintéticos; não há envio de email nem pagamento. É uma prática de usabilidade, não uma certificação completa de acessibilidade. Os 50 minutos são uma estimativa do autor que inclui leitura e erros comuns.

Use Workspace para os comandos e Studio para o site. Os arquivos editáveis são /home/labex/project/stillspoke/form.html, validity.js e form.css. Preserve os demais arquivos, a API preparada, as regras de capacidade e os visitantes existentes. Cada laboratório inicia de forma independente. O servidor insere o conteúdo exato de form.html na estrutura visual da página; não completa rótulos ausentes nem gera novamente seu trabalho. Após atualizar, espere a página terminar de carregar antes de usar o formulário. Os rótulos do site continuam em inglês em todos os idiomas da documentação.

Dê um nome visível a cada controle

Nesta etapa, use o exemplo completo pelo teclado e depois conecte cada controle da sua página ao rótulo visível e às instruções.

Em Studio, escolha o link Study form. É uma navegação dentro do site, não uma troca de Tab do LabEx. O exemplo tem API e banco próprios; não conclui seu trabalho nem ocupa suas vagas. Pressione Tab até Your name receber um contorno visível. Digite Example Guest, pressione Tab e digite sample@example.test. Vá com Tab até Session, pressione C para selecionar Clay, slowly / 10:00 e leia o título escolhido antes de continuar. Esse seletor nativo permite digitar o início de uma opção. Pressione Tab até a caixa de assistência, Tab até a observação opcional e Tab até Reserve my place. Pressione Enter uma vez. Aguarde Submission result mostrar Reserved for Example Guest e uma Confirmation SS-… emitida pelo servidor. A mensagem passiva de envio desaparece. A seção de resultado recebe o foco para facilitar sua localização.

A imagem mostra o resultado com título próprio e confirmação verdadeira. Seu número será diferente.

Confirmação real no formulário de exemplo independente

Escolha Studio records na página do exemplo. Encontre o mesmo nome, endereço, horário legível e número. Essa página lê registros aceitos, não o texto do resultado no navegador. Escolha Back to signup e Work form para retornar à sua página. Ela ainda está incompleta e sem nova inscrição de trabalho; o exemplo não conta.

Troque para Workspace. cd muda o diretório atual desse terminal; um caminho absoluto começa com /. pwd imprime esse diretório. Os comandos tornam explícito o destino da edição:

cd /home/labex/project/stillspoke
pwd

Saída esperada:

/home/labex/project/stillspoke

Os valores name do formulário são as chaves da API: name, email, session, support e note. Um id HTML identifica um elemento da página. O for do rótulo aponta para o id da entrada, de modo que clicar no rótulo ou lê-lo com tecnologia assistiva identifica o controle. Aqui Your name usa for="person", mas a chave enviada é name; os atributos têm funções distintas. Colocar a caixa dentro do rótulo cria uma associação implícita. autocomplete="name" e autocomplete="email" identificam o tipo de informação que o navegador pode sugerir, sem enviar dados por si só. O placeholder desaparece durante a digitação e não substitui um rótulo visível.

required rejeita um valor vazio antes da solicitação. type="email" pede ao navegador que rejeite sintaxe inválida; não comprova o proprietário do endereço. minlength="2" exige pelo menos dois caracteres no nome. A primeira opção vazia obriga a escolher uma sessão real. O value de uma opção é sua identidade estável no servidor; o título visível é para pessoas. Não deduza essa relação pela posição.

aria-describedby contém IDs separados por espaços. Associa cada campo à ajuda persistente e a um parágrafo de erro inicialmente vazio. A ajuda explica a regra antes da falha; o erro acrescenta o motivo após uma recusa do servidor. Use palavras claras além de cores. aria-hidden="true" oculta apenas a seta decorativa das tecnologias assistivas. ↗ é uma referência numérica HTML: seu código ASCII vira uma seta no navegador. O parágrafo Sending tem role="status" para anúncios passivos. O aria-labelledby do resultado aponta ao ID do título e dá nome à seção. O atributo inicial hidden esconde o resultado vazio até haver algo a mostrar. A seção separada tem tabindex="-1": scripts podem focá-la sem adicionar uma parada à ordem comum de Tab. Não focamos o anúncio de status.

O comando seguinte substitui apenas form.html. cat escreve o texto seguinte; > sobrescreve o arquivo nomeado. O marcador HTML entre aspas depois de << inicia um here-document: tudo até a linha contendo somente HTML é conteúdo literal. As aspas impedem a interpretação de caracteres HTML pelo shell. Cole o bloco inteiro, inclusive o marcador final. O retorno do prompt significa que a escrita terminou; não há outra tecla para salvar. Não cole os acentos graves do Markdown. Os pequenos arquivos JavaScript e CSS posteriores usam o mesmo método.

cd /home/labex/project/stillspoke
cat > form.html <<'HTML'
<form id="signup">
  <div class="pair">
    <div class="field">
      <label for="person">Your name (required)</label>
      <input id="person" name="name" required minlength="2" autocomplete="name"
             aria-describedby="name-help name-error">
      <small id="name-help">At least two characters.</small>
      <p class="error" id="name-error"></p>
    </div>
    <div class="field">
      <label for="email">Email (required)</label>
      <input id="email" name="email" type="email" required autocomplete="email"
             aria-describedby="email-help email-error">
      <small id="email-help">Practice address: you@example.test</small>
      <p class="error" id="email-error"></p>
    </div>
  </div>
  <div class="field">
    <label for="session">Session (required)</label>
    <select id="session" name="session" required aria-describedby="session-help session-error">
      <option value="">Choose a time</option>
      <option value="clay-morning">Clay, slowly / 10:00</option>
      <option value="clay-afternoon">Clay, slowly / 14:00</option>
    </select>
    <small id="session-help">Choose a listed time. The afternoon session is full.</small>
    <p class="error" id="session-error"></p>
  </div>
  <label class="check"><input type="checkbox" name="support"> I need seating assistance</label>
  <div class="field">
    <label for="note">Seating note (required when assistance is selected)</label>
    <textarea id="note" name="note" rows="2" aria-describedby="note-help note-error"></textarea>
    <small id="note-help">When assistance is selected, describe the help in at least 5 characters.</small>
    <p class="error" id="note-error"></p>
  </div>
  <button type="submit">Reserve my place <span aria-hidden="true">&#8599;</span></button>
  <p id="progress" role="status"></p>
  <section id="result" aria-labelledby="result-heading" tabindex="-1" hidden>
    <h3 id="result-heading">Submission result</h3>
    <p id="message"></p>
  </section>
</form>
HTML

Volte a Studio e atualize sua página. Leia os rótulos mesmo depois de digitar. Clique em Email para ver o foco passar à entrada, depois use Tab para seguir a ordem do documento. Com o formulário vazio, ative Reserve my place: o navegador deve focar Your name e explicar que é obrigatório. Digite um caractere e tente de novo para ver a regra de comprimento. Use um nome como Morgan Vale; digite not-an-email para observar a recusa de formato de email. São mensagens de validação nativa, não respostas HTTP 422: o navegador ainda não enviou uma inscrição. Para corrigir, selecione o texto com Ctrl+A ou Cmd+A no campo focado e substitua-o. Uma digitação incorreta não exige reiniciar a VM.

Por enquanto, a observação continua opcional mesmo com assistência marcada. Esse é o comportamento a acrescentar a seguir. O layout também ainda não está preparado para telas estreitas. Autoverificação: os cinco controles têm rótulos visíveis associados, a ajuda permanece visível e campos obrigatórios vazios não podem ser enviados.

Faça a observação acompanhar a caixa de seleção

Torne a observação obrigatória somente quando houver pedido de assistência de assento, preservando o texto digitado.

A caixa expõe o booleano checked: true quando marcada, false caso contrário. Uma entrada ou área de texto expõe required, correspondente à restrição HTML. STUDIO é um objeto de configuração preparado carregado por scenario.js; seu noteMin vale 5 nesta oficina. Leia o valor em vez de supor que todas as oficinas futuras serão iguais. A configuração fornece dados do cenário, não implementa sua regra condicional.

querySelector devolve o primeiro elemento correspondente a um seletor CSS; [name="support"] e [name="note"] correspondem aos atributos. Selecionamos pelo name da API. A função atualiza duas restrições nativas sempre que a caixa muda. Em condition ? first : second, o operador condicional escolhe first se a condição for verdadeira, second caso contrário. O mínimo é cinco com assistência e zero sem ela. Chamar updateNote() uma vez também inicializa o estado ao carregar. O listener change a executa novamente após a mudança. Alteramos restrições, não .value, portanto desmarcar não apaga o rascunho.

Em Workspace, substitua validity.js. O marcador JS funciona como HTML na etapa anterior:

cd /home/labex/project/stillspoke
cat > validity.js <<'JS'
const support = document.querySelector('[name="support"]');
const note = document.querySelector('[name="note"]');
function updateNote() {
  note.required = support.checked;
  note.minLength = support.checked ? STUDIO.noteMin : 0;
}
support.addEventListener('change', updateNote);
updateNote();
JS

Volte a Studio e atualize. Com Tab, preencha um nome de dois ou mais caracteres, endereço válido como morgan@example.test e Clay, slowly / 10:00. Use Space em I need seating assistance para marcar. Deixe a observação vazia e ative Reserve my place. Agora o navegador deve focar Seating note e solicitar um valor. Digite near, saia com Tab e tente enviar: quatro caracteres são menos que cinco. minlength se aplica ao texto introduzido pelo usuário; o exercício digita normalmente, sem atribuir por script.

Retorne à caixa com Shift+Tab conforme necessário e desmarque com Space. A nota é opcional e near permanece. Marque novamente e troque por A seat near the entrance. Preserve esses dados para a próxima etapa; atualizar descarta entradas não enviadas, mas trocar de Tab LabEx mantém esta página aberta.

Se a caixa parece não fazer nada, volte a Workspace e mostre o arquivo salvo exato:

cd /home/labex/project/stillspoke
cat validity.js

Compare com o bloco curto, inclusive checked e o último updateNote();. Se necessário, substitua pelo bloco inteiro e atualize Studio. Script ausente ou erro JavaScript é diferente da rejeição nativa de uma nota vazia; ausência de inscrição não prova que a restrição funciona. Autoverificação: a caixa altera a obrigatoriedade, notas curtas são rejeitadas apenas quando marcada e o texto existente é preservado.

Mantenha o formulário legível e o foco visível

Conclua o layout, faça uma inscrição real na sua página e leia o registro aceito correspondente.

Um formulário em duas colunas precisa se reorganizar. 1fr 1fr do CSS Grid dá parcelas iguais a nome e email; min-width: 0 permite encolher. Na largura máxima de 800 pixels, a media query muda o par para uma coluna. width: 100% com box-sizing: border-box inclui padding e bordas na largura disponível. input:not([type="checkbox"]) exclui as caixas dessa regra de largura total, mantendo seu quadrado pequeno. O texto fica disponível sem hover. A estrutura preparada já põe a história sobre o formulário em telas pequenas; suas regras fazem o formulário caber nela.

:focus-visible dá contorno visível ao foco do teclado. Cor não é o único sinal: texto de obrigatoriedade, erros escritos e formato do contorno também importam. Bordas ou sombras de foco claras e equivalentes são válidas; eliminar todos os indicadores não é. [aria-invalid="true"] estiliza um campo marcado pelo servidor; .error:empty oculta apenas um parágrafo de erro vazio, não um rótulo ou explicação necessária. overflow-wrap: anywhere permite quebrar confirmações ou endereços sintéticos longos. A preferência por menos movimento evita adicionar deslocamentos suaves a quem pede menos animação; este formulário não precisa de animações.

Em Workspace, substitua form.css:

cd /home/labex/project/stillspoke
cat > form.css <<'CSS'
form { margin-top: 28px; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.field { min-width: 0; margin-bottom: 18px; }
label { display: block; margin-bottom: 7px; font-size: 13px; font-weight: bold; }
input:not([type="checkbox"]), select, textarea {
  width: 100%; box-sizing: border-box; padding: 11px 12px;
  border: 1px solid #89988a; border-radius: 3px;
  font: inherit; font-size: 15px; color: #243d35; background: #fcfaf5;
}
small { display: block; margin-top: 6px; font-size: 11px; color: #56645a; }
.check { display: flex; gap: 9px; align-items: center; margin: 20px 0; }
.check input { width: 18px; height: 18px; accent-color: #244c3d; }
button {
  padding: 14px 20px; border: 0; border-radius: 3px;
  font: inherit; font-size: 14px; color: #fffdf6; background: #254f41; cursor: pointer;
}
button[type="submit"] { width: 100%; display: flex; justify-content: space-between; }
button:disabled { background: #5b6c5e; cursor: wait; }
:focus-visible { outline: 3px solid #ad4d28; outline-offset: 4px; }
[aria-invalid="true"] { border: 2px solid #9d3327; }
.error { color: #963024; font-size: 12px; margin: 5px 0; }
.error:empty { display: none; }
#progress, #message { font-size: 14px; overflow-wrap: anywhere; }
#result { margin-top: 20px; border-top: 1px solid #c9cebf; padding-top: 12px; }
#result h3 { font-size: 15px; margin: 0 0 8px; }
#records article { border-top: 1px solid #c9cebf; overflow-wrap: anywhere; padding: 12px 0; }
@media (max-width: 800px) { .pair { grid-template-columns: 1fr; gap: 0; } }
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; } }
CSS

Volte a Studio, atualize e use o formulário de trabalho. Digite Morgan Vale, morgan@example.test, selecione Clay, slowly / 10:00 com C, marque assistência com Space e digite A seat near the entrance. Vá com Tab ao botão e pressione Enter uma vez. Espere a confirmação real do manipulador preparado. Este primeiro laboratório fornece o manipulador; você implementou a semântica, as restrições e os estilos ao redor. O próximo explica e constrói a lógica de solicitações e mensagens.

Escolha Studio records e encontre o mesmo nome, email, horário e número. Preserve Existing Visitor e Avery Reed. Seus registros são independentes do novo; apagá-los para liberar lugar está fora da tarefa. É o servidor, não um botão desativado, que impõe a capacidade. Depois escolha Back to signup. A inscrição salva persiste neste laboratório mesmo quando atualizar apaga campos não enviados.

Reduza o navegador ou painel Studio a cerca de 390 pixels e inspecione também uma largura de tablet próxima de 768. Não deve haver rolagem horizontal; rótulos e ajuda continuam visíveis e nome/email se empilham. Percorra com Tab: todo controle focado precisa de indicador visível. Restaure uma área larga e confira as duas colunas. Não esconda um campo difícil para fazer o layout caber.

Se ainda aparece o layout antigo, confira que escreveu form.css em /home/labex/project/stillspoke, não base.css, e atualize. Um caminho errado não exige reiniciar a VM. Autoverificação: o visitante lê e usa todo o formulário em telas estreitas e largas, a inscrição real aparece nos registros e os visitantes antigos permanecem. A verificação lê registros reais e executa uma cópia inalterada dos arquivos com dados temporários isolados; nunca envia seu formulário real por você.

Resumo

Você associou rótulos visíveis, restrições nativas e ajuda aos controles, tornou a nota condicional sem perder entradas e criou um layout responsivo com foco de teclado visível. Usou um exemplo independente, fez uma inscrição real e comparou a confirmação aos registros aceitos. O próximo laboratório independente cria o manipulador de solicitações e mensagens.