Introduction
Still & Spoke runs small Saturday pottery workshops. Its studio page and ordinary work form are ready to inspect. Name and email provide working label examples, but the remaining controls need their own associations, the seating note does not follow the checkbox, and keyboard focus is not yet clear. You will complete those relationships and a responsive layout, then make a real local booking. A separate Study form shows the complete experience before you edit your work.
You need basic HTML elements, CSS selectors and layout, and JavaScript objects, functions, DOM selection and events. This lab introduces label associations, browser form constraints, accessible descriptions, conditional required, visible focus and the small responsive rules used here. A prepared submission handler and local API handle requests in this first lab; the next lab builds that handler. All people and addresses are synthetic; no email or payment is sent. This is a practical usability exercise, not a full accessibility certification. The 50-minute duration is an author estimate including reading and ordinary mistakes.
Use WebIDE to read and edit /home/labex/project/stillspoke/form.html, validity.js and form.css; use Workspace for the shown read-only terminal commands and Studio for the website. Preserve the other files, the prepared API, capacity rules and existing visitors. Each lab starts independently. The server inserts your actual form.html into the branded page shell; it does not supply missing labels or regenerate your work. Saving code does not update an already loaded form: refresh Studio after a completed edit and wait for it to finish loading. A refresh clears unsent fields, while switching LabEx Tabs leaves that form page open. Website labels remain English in every document language.
Give Each Control a Visible Name
In this step, use the complete example with the keyboard, read two working label relationships, and connect the remaining controls in your own page.
In Studio, choose the page link Study form. This navigates within the website; it does not switch a LabEx Tab. The example has its own API and database, so it cannot complete your work or take your work-page seats. Press Tab until Your name receives a visible outline. Type Example Guest, press Tab, and type sample@example.test. Press Tab to Session, press C to select Clay, slowly / 10:00, and read the selected title before continuing. This native select supports typing the beginning of an option. Press Tab to the assistance checkbox, Tab to the optional note, and Tab to Reserve my place. Press Enter once. Wait for Submission result to show Reserved for Example Guest and a server-issued Confirmation SS-…. The passive sending message disappears. The separately headed result receives focus so you can find the completed result.
The example below shows that real confirmation. Your issued number will differ.

Choose Studio records on the example page. Find the same name, address, readable time and confirmation number. Choose Back to signup, then Work form. Your own page is still incomplete and has no new work booking. Its name and email are examples to read, not evidence that all five controls are finished.
Switch to Workspace. cd changes this terminal's current directory; the absolute path starts at /. pwd prints the directory. Enter:
cd /home/labex/project/stillspoke
pwd
Expected output:
/home/labex/project/stillspoke
Now switch to WebIDE. In Explorer, expand project if necessary, expand stillspoke, and open form.html. The folder corresponds to the full path you just checked. You are editing the existing file, not creating another form.html in a different folder. The branded shell and submission-result section are already supplied; leave them in place.
Click in the editor before using its shortcuts. Use Ctrl+F, or Cmd+F on macOS, to find an exact fragment named below; Escape closes Find and returns to the match. Edit only the stated element or function boundary. Save with Ctrl+S, or Cmd+S on macOS, while the editor has focus. A dirty-file indicator, if present, should clear. Auto Save may also be enabled, so do not assume an edit remains unsaved until the shortcut. Use Undo (Ctrl+Z or Cmd+Z) for an accidental edit, inspect what it restored, then save again. If Undo affects more than intended, use Redo (Ctrl+Shift+Z or Cmd+Shift+Z) and correct only the named fragment. These are editor actions, not terminal commands.
Read the existing name field. This is an example already in the file; do not paste a second copy:
<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>
An HTML id identifies one page element. A label's for refers to that id, so clicking its visible text identifies the control. The name attribute has a different job: it becomes the API key. Here the label targets person, but the submitted key is name. The email field uses email for both; matching spelling there does not make the two jobs identical. autocomplete="name" and autocomplete="email" describe the kind of information a browser may offer to fill. A disappearing placeholder is not a replacement for a visible label.

Use the existing name field as the model: for="person" points to id="person", while name="name" supplies the API key. Explorer shows the existing project / stillspoke / form.html file.
required rejects an empty field before a request. type="email" rejects malformed email syntax, not an address whose owner is unknown. minlength="2" requires two typed name characters. aria-describedby connects a control to space-separated help/error IDs: help is available before an error, and the initially empty paragraph can later explain a refusal. Keep the supplied help/error IDs so the prepared feedback functions can use them. Predict which name field receives focus when its label is clicked, then compare the email example in the file.
Complete the session relationship. Find the existing <span>Session</span> and replace just that element with:
<label for="session">Session (required)</label>
Find the opening <select id="session" name="session"> and replace only that opening tag with:
<select id="session" name="session" required aria-describedby="session-help session-error">
Keep all of its options and the existing help/error paragraphs. The empty first option means that required demands a real choice. An option's value is its stable service identity; the visible title is for people. Do not identify a session by option position.
The assistance input currently sits in a div with visible text, but that text is not its label. Replace only that complete div element with the implicit label association below:
<label class="check"><input type="checkbox" name="support"> I need seating assistance</label>
Finally replace the existing <span>Seating note</span> with:
<label for="note">Seating note (required when assistance is selected)</label>
Replace only the opening-and-closing textarea element, retaining the separate note help and error paragraphs:
<textarea id="note" name="note" rows="2" aria-describedby="note-help note-error"></textarea>
Do not put unconditional required on this textarea: whether a note is necessary depends on the checkbox. You will implement that rule next. Keep the button, passive Sending status and separately headed result section. role="status" is for a passive announcement; the result uses aria-labelledby to name its heading and starts hidden while empty. Its tabindex="-1" permits script focus without an extra ordinary Tab stop. The arrow's aria-hidden="true" marks decoration; ↗ is the HTML character reference that draws it. None of these prepared elements silently creates a booking.
Save form.html, then return to Studio and refresh Work form. Click Email and then the Session label: focus should move to the associated control. The assistance label should toggle its checkbox. Keep the form empty and activate Reserve my place: the browser focuses Your name and explains that it is required. Type one character and try again to see the length rule. With a name such as Morgan Vale, type not-an-email to observe the different email-format refusal. These are native refusals, not HTTP 422: no booking request is sent. Correct a focused value with Ctrl+A or Cmd+A and replacement text. Do not complete a valid work booking until the later step.
If a label click does nothing, return to its exact element in WebIDE and compare for with the target id; do not change name to repair a label. If the page still shows old content, check the open editor filename and save, then refresh Studio. Never reset the VM or overwrite the whole file for a one-attribute mistake. Self-check: every control now has an associated visible label, persistent help remains, and empty required values cannot submit. The note is still incorrectly optional when assistance is selected, and keyboard focus styling still needs work.
Make the Seating Note Follow the Checkbox
In this step, implement the function that changes the note's constraints when a visitor requests assistance, without erasing their draft.
Switch to WebIDE and open validity.js under project / stillspoke. Read its two control references, updateNote function and the last two lines. querySelector returns the first matching element; [name="support"] and [name="note"] find the API-named controls. The prepared change listener already calls updateNote after a checkbox change, and the final updateNote(); already initializes it on page load. You are changing the function's two assignments, not adding a second listener or another initialization call.
The current assignments always leave the note optional with a zero minimum. Predict what should change for a checked box and what should stay when it is unchecked. A checkbox's Boolean .checked is true or false; its .value is not that Boolean. A textarea's .required and .minLength are native constraint properties. STUDIO, loaded by scenario.js, supplies this workshop's noteMin of 5; it does not implement your rule. In condition ? first : second, the conditional operator chooses the first value for true and the second for false.

Before editing, the selected function still sets false and 0. Replace only this complete function; keep the two declarations above it and the existing change listener and initial call below it.
Find function updateNote(). Select only that function, from its declaration through its closing brace, and write the following complete function in its place. Keep the two declarations above and the existing listener/initial call below:
function updateNote() {
note.required = support.checked;
note.minLength = support.checked ? STUDIO.noteMin : 0;
}
Read the saved function aloud as two rules: requiredness follows the checkbox, and the minimum is the configured five only when selected. Neither assignment touches .value, so toggling assistance retains the note. Save the file.
Return to Studio and refresh Work. Use Tab to enter a name of at least two characters, morgan@example.test, and Clay, slowly / 10:00. With focus on I need seating assistance, use Space to select it. Leave the note empty and activate Reserve my place. The browser should focus Seating note and ask for a value. Type near, leave the note with Tab, and try again: four typed characters are shorter than five. A minlength constraint applies to text typed by a user; these tests type it normally rather than assigning it from a script.
Return to the checkbox using Shift+Tab as needed and press Space to clear it. The note becomes optional and near stays. Select assistance again and replace the note with A seat near the entrance. Changing LabEx Tabs leaves this page open; a refresh discards its unsent fields.
If the checkbox does not change the constraint, use Workspace to read the actual saved file. cat displays a file without changing it:
cd /home/labex/project/stillspoke
cat validity.js
Compare the two assignments with the function above, especially .checked, .required, .minLength and STUDIO.noteMin. Return to that function in WebIDE, correct only the wrong property, save and refresh Studio. Keep the already provided listener and initialization. A script error is different from a native constraint refusing an empty note: no new booking by itself does not prove the rule works. Self-check: selected assistance enforces the configured minimum, unselected assistance leaves an optional note, and toggling retains existing text.
Keep the Form Readable and Its Focus Visible
In this step, improve only the layout and focus rules, then complete a real work booking and compare its accepted record.
The ordinary form already fits its page and has prepared typography, colours and input styling. That does not finish the layout work: the paired name/email fields always stack, and the initial stylesheet explicitly suppresses their focus indication. In WebIDE, open form.css. Leave the decorative, record and result styles in place. You will change three existing rules and add one media query.
CSS Grid's 1fr 1fr gives name and email equal shares. .field needs min-width: 0 so its contents can shrink inside those columns. Find the first .pair rule and replace only that rule with:
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
Replace only the .field rule with:
.field { min-width: 0; margin-bottom: 18px; }
Read the prepared input rule: input:not([type="checkbox"]) excludes the small checkbox from the full-width input styling. width: 100% and box-sizing: border-box include padding and borders in the available width. You do not need to replace these working rules.
At a maximum viewport width of 800 pixels, the pair should return to one column. Add the following rule immediately before the existing reduced-motion rule near the end of the file:
@media (max-width: 800px) { .pair { grid-template-columns: 1fr; gap: 0; } }
The page shell already stacks the story above the form at that width. Your media query handles the two fields inside the form. Keep all fields and help available; hiding a difficult field is not a layout repair.
Find the rule that starts form :focus, form :focus-visible and currently sets outline: none. Replace that whole rule with:
:focus-visible { outline: 3px solid #ad4d28; outline-offset: 4px; }
:focus-visible supplies a clear keyboard focus change; the outline's shape matters as well as its colour. An equally clear changing focus border or shadow is valid. A permanent border around every input does not identify which one is focused. Keep the prepared [aria-invalid="true"] and .error rules: errors need words, and .error:empty hides only an empty message. Keep the existing overflow-wrapping and reduced-motion rules too. This form does not need animation.
Save, return to Studio and refresh. At a wider pane the two fields should share a row. For a small editing-recovery exercise, return to form.css and temporarily change only the first selector from .pair to .pairs. Save and refresh the wide Work page. The HTML still uses class="pair", so the name/email fields stack again: the selector no longer matches. Return to the editor and undo that typo, inspect that the selector reads .pair, then save and refresh. If Undo restores more than the typo, redo and correct just that selector directly. Do not rename the HTML class or replace the entire stylesheet. This is an ordinary spelling recovery, not a reason to reset the VM.

After Undo and Save, inspect the singular .pair selector. The :focus-visible and narrow-screen @media rules remain in place.
Now complete the working form. Enter Morgan Vale, morgan@example.test, select Clay, slowly / 10:00 using C and read the displayed choice. Select assistance with Space, enter A seat near the entrance, then Tab to Reserve my place and press Enter once. Wait for the prepared handler's real confirmation. This lab supplies that handler; your work is the semantics, constraints and styling around it. The next lab builds its request and feedback logic.
Choose Studio records and match the name, email, readable time, note and issued confirmation number. Preserve Existing Visitor and Avery Reed, and every earlier booking. The service, not a disabled frontend button, enforces capacity. Choose Back to signup after reading. The accepted booking remains even though a refresh or page navigation can clear unsent fields.
Use a narrow browser or Studio pane near 390 pixels, a tablet width near 768 and a wider view. There should be no horizontal scrolling; the name/email pair stacks on small screens, and all labels and help remain readable. Use Tab through every control and the button to see its individual focus indicator. Restore the wider view to see the two columns. These observations check different things: a fitted form may still have no visible focus, while a visible outline alone does not prove the field relationships or a real booking.
If a change is missing, confirm the editor is showing form.css in /home/labex/project/stillspoke, not base.css, then save and refresh the correct Work page. Self-check: the form works at each width with keyboard focus, the actual work booking appears in records, and all earlier visitors remain. The lab check reads the live records and exercises an unchanged copy of your files with isolated temporary data; it never submits your live form for you.
Summary
You connected visible labels, native constraints and help to real form controls, made the seating note conditional without losing input, and built a responsive layout with visible keyboard focus. You used a separate example, then completed a genuine work-page booking and matched its confirmation to accepted records. The next independent lab builds the request and feedback handler.
