Usability & Accessibility

Marketplace Project: Make the Marketplace Usable


Learning Objectives

  • You can make marketplace pages reflow while preserving meaningful structure and navigation.
  • You can make forms and changing feedback usable through the keyboard and assistive technology.
  • You can test keyboard recovery and retained values against a fixed contract and targeted faults.
  • You can record a reproducible common-core baseline with its verification evidence.

Apply the responsive layout, semantic structure, keyboard and focus, and audit guidance to the authenticated marketplace. Milestones 32-36 complete the marketplace project.

Before starting

Continue on master in your accepted marketplace/ repository after milestone 31. Preserve its existing features, read decoders and tests. Consult the accepted session contract, permission matrix, API contract and listing representation when you need their baseline details.

Use the Part 5 local check commands for migrations and API/client checks. Follow each milestone card’s Git instructions when you reach it. Inspect git diff, commit the requested changes, and submit with git push origin master. The optional shared project reference below collects browser-check and submission reminders.

Page structure and keyboard navigation

Milestone 32 establishes the shared page structure: meaningful regions and headings, native navigation, visible keyboard focus, and a skip link to the main content. Check that a keyboard user can reach the existing marketplace tasks. Preserve the account controls and the original counter and todo sections.

Marketplace milestone 32: page structure and keyboard navigation

0 / 65 points

Start from accepted milestone 31. Use the project setup for contracts, checks, and submission.

Continue after milestone 31

After milestone 31 passes, continue on master in your accepted marketplace/ repository. Before starting this milestone or submitting, run:

Terminal window
git fetch origin

Fetching prepares milestone 32 for grading and makes its optional previous-step reference at origin/milestone-32 available. It does not change your working files. You do not need to merge the reference to complete this milestone.

Objective and checkpoints

Give the existing marketplace a clear page structure and a usable keyboard entry point. Add a shared layout in client/src/routes/+layout.svelte and base styles in client/src/app.css.

  1. Use meaningful header, primary navigation, main-content and footer regions. Give each page one clear h1. Preserve native links for navigation and native buttons for actions.
  2. Provide visible keyboard focus and a skip link that takes keyboard focus directly to the main content.
  3. Keep the existing account, seller, listing, counter and todo functionality when organizing the pages. Keep the counter and todo sections on the home page.

Use navigation name Primary, with native links Browse, My listings and Account. The first focusable element is a link named Skip to main content. Its destination is <main id="main-content" tabindex="-1">. Use the home-page h1 Browse listings; retain Account and My listings on those pages. Use href="#main-content" on the skip link and ensure activating it focuses that main element. Keep the browser’s visible focus indicator or supply an equivalent visible style for keyboard-focused links and controls. CSS classes and visual styling are your choice.

This checkpoint concerns the page shell. Keep the existing browsing components; responsive cards and loading feedback are developed in milestone 33.

Focused checks and points

Open the browse, account and seller pages. Use Tab to follow the native links, confirm visible focus, and use the skip link to reach main content. Run the client build after changing the layout. Reuse previously passing checks for unchanged behavior.

  • 35 points: meaningful page regions, headings and native primary navigation.
  • 30 points: visible keyboard focus, a visible first keyboard stop and a skip link that focuses main content.

Responsive browsing and feedback

Milestone 33 builds on that structure with category navigation and listing results. Apply reflow to narrow and wide views, including long content and increased text size. The supplied state helper handles category selection and reloads. Your markup should describe the bounded collection that the browser has loaded and make loading, empty and error states understandable. Check the affected views and keyboard tasks while preserving the existing API and authorization behavior.

Marketplace milestone 33: responsive browsing and feedback

0 / 60 points

Start from accepted milestone 32. Use the project setup for contracts, checks, and submission.

Receive the supplied files

After milestone 32 passes, receive the milestone 33 contribution in your accepted marketplace repository:

Terminal window
git fetch origin
git diff HEAD..origin/milestone-33
git merge --ff-only origin/milestone-33

Commit unfinished local edits before merging. If local commits prevent a fast-forward, inspect the contribution and use git cherry-pick origin/milestone-33, preserving your work. If a supplied path already exists, compare and resolve it without replacing your application, tests or README. The contribution contains only the additive support files described below.

Supplied browsing state

The milestone branch supplies client/src/lib/state/listing-browser.svelte.ts, e2e-tests/tests/helpers/public-browse-fixtures.ts, and docs/part6-browsing.md through Git.

If the merge reports an existing additive file, compare its contents and keep your accepted work while incorporating the supplied helper. Do not replace components or API modules with another project snapshot.

The helper supplies category selection and retained rows, loading/retry state, and request cancellation through your existing read API. Use it in ListingsBrowser.svelte:

<script lang="ts">
import ListingCard from "./ListingCard.svelte";
import { createListingBrowserState } from "#lib/state/listing-browser.svelte.ts";
let { selectedIds = null }: { selectedIds?: number[] | null } = $props();
const browser = createListingBrowserState(() => selectedIds);
</script>

Invoke browser.load from the load button. Read browser.rows, browser.visible, browser.status (idle, loading, ready, error), and browser.message in your markup. Keep your existing card rendering. Implement the semantics and layout below.

Objective and checkpoints

  1. Keep category navigation as nested native lists and listing cards as a named native list. Preserve the page shell and keyboard navigation from milestone 32.
  2. Make browse cards and category/results layout reflow from normal flow at narrow widths to a content-driven wide layout. Long titles and increased text size must not create horizontal page overflow.
  3. Communicate loading, empty and error states, and describe the bounded loaded collection accurately. Preserve loaded rows after a failed reload.

Modify client/src/app.css and client/src/lib/components/ListingsBrowser.svelte; update focused browser tests as needed. The API, schemas and migrations remain the accepted baseline. Preserve the existing counter and todo demonstration sections when reorganizing the browse page. Adapt earlier browser heading/label locators to the documented names below while retaining their relationship, navigation and reload assertions.

The earlier browse checks need public listings now that drafts are private. Set workers: 1 inside the existing defineConfig object in e2e-tests/playwright.config.ts, preserving its other settings. These specs share one test database; running their setup concurrently can create duplicate public examples and make the browse assertions ambiguous. In student_browse.spec.ts and student_listing.spec.ts, connect the supplied fixture helper using the import and beforeAll shown in docs/part6-browsing.md. It creates the public examples through the authenticated API. Keep the existing assertions and private seed drafts.

Observable names and states

Keep the accepted primary navigation, skip link and h1 Browse listings. Use navigation name Categories. Preserve the Load listings button and list named Marketplace listings.

Show Showing N of M loaded in a status region, where N is the number in the selected categories and M is the bounded loaded collection. Show No listings are currently available. when the loaded collection is empty. Explain an empty category selection, and expose a role=alert error on a failed reload without discarding loaded rows. Use these stable names for keyboard/browser checks; CSS classes and exact card widths are not prescribed. Test at 360 and 1280 CSS-pixel widths.

Focused checks and points

Run the affected client checks and build, then check browsing at 360 and 1280 CSS-pixel widths. Manually check keyboard navigation and 200% zoom, including a long unbroken title and failed reload with retained listings. Keep brief observations for the audit record completed in milestone 36.

Reuse existing client and controlled-response checks for loading and failed reloads. Reuse verification results for unchanged behavior.

  • 20 points: native category navigation and semantic listing structure.
  • 20 points: loading, empty and error feedback, with retained rows after a failed reload.
  • 20 points: loaded long titles reflow without page overflow.

Account error recovery

Milestone 34 completes account-form recovery: connect the error to the affected controls, move focus to the summary after rejection, retain the entered values, and provide a keyboard path back to the form. Add a “Return to login details” link in the login error summary. Activating it must focus the Email field without clearing the entered values. Use the recovery-link pattern from Chapter 6.5.

Retain keyboard operation of the existing seller forms and the earlier sign-in path for anonymous users. Draft creation, publication, and stale-edit recovery already work in the accepted application. Preserve those behaviors while changing markup and styles; this milestone does not ask you to rebuild every form.

Marketplace milestone 34: account error recovery

0 / 105 points

Start from accepted milestone 33. Use the project setup for contracts, checks, and submission.

Continue after milestone 33

After milestone 33 passes, continue on master in your accepted marketplace/ repository. Before starting this milestone or submitting, run:

Terminal window
git fetch origin

Fetching prepares milestone 34 for grading and makes its optional previous-step reference at origin/milestone-34 available. It does not change your working files. You do not need to merge the reference to complete this milestone.

Objective

Make a rejected login understandable and recoverable with the keyboard. Associate rejection feedback with the login controls, preserve the entered values in the mounted form, and focus a rendered error summary after an explicit failed submission. Add a Return to login details link inside the summary that focuses the login email input without clearing either value.

Keep the existing registration, session and logout behavior. A combined account form or separate login and registration forms are both valid. Never retain a password beyond its form or persist it in browser storage.

Account recovery details

The account page at /account has h1 Account, login controls Email and Password, and button Log in. On rejected login, focus its rendered role=alert summary (tabindex=-1). The summary can use any appropriate child markup; associate its visible rejection feedback with both login controls using aria-describedby. Include the Return to login details link inside the summary. Retain credentials only in this mounted form; clear them when leaving the form or completing login.

Files to create or modify

Modify client/src/lib/components/Account.svelte and its owning page only where needed. Keep the accepted state and API boundaries.

Test and submit

Run the focused existing checks for the account workflow and the client build. Try a rejected login using only the keyboard: the rendered summary receives focus, its recovery link returns focus to Email, and both entered values remain. Update an existing check if your markup changes its selectors. The final milestone records one complete verification run.

Type checking is optional and does not affect points. The grader checks the application behavior in a browser.

Points

  • 40 points: a rejected login focuses its rendered summary.
  • 40 points: visible rejection feedback is associated with both login controls.
  • 25 points: the summary’s recovery link returns keyboard focus to Email while preserving both entered values.

Accessible bidding

Milestone 35 associates bid rejection feedback with the bid input, retains the proposed amount after rejection, and distinguishes it from server-confirmed results. While a request is pending, keep the bid input editable. If the user continues editing it, a response must not move focus away. Expose meaningful feedback without announcing every edit.

The existing anonymous sign-in path and stale-draft recovery remain regression checks. A stale draft still keeps its proposal and blocks saving until an explicit discard and successful reload. Reuse these checks; the new points assess the bid interaction and its accessible feedback.

Marketplace milestone 35: accessible bidding

0 / 75 points

Start from accepted milestone 34. Use the project setup for contracts, checks, and submission.

Continue after milestone 34

After milestone 34 passes, continue on master in your accepted marketplace/ repository. Before starting this milestone or submitting, run:

Terminal window
git fetch origin

Fetching prepares milestone 35 for grading and makes its optional previous-step reference at origin/milestone-35 available. It does not change your working files. You do not need to merge the reference to complete this milestone.

Objective

Make bidding feedback accessible while keeping proposed, pending, and server-confirmed state separate. Associate a rejection with the bid input, announce one restrained response, and preserve a user’s focus if they are editing the input while a response or background refresh arrives.

Keep the proposed amount after low, closed, and unknown outcomes. Refresh and display the authoritative price and deadline; do not let a countdown or client state decide bid validity. The amount input remains editable while a submission is pending.

Bid feedback details

The bid form has Bid amount in euros and Place bid. A low bid keeps the entered amount, marks the input aria-invalid="true", and shows feedback including too low. Associate that visible feedback with the input through aria-describedby; a help-text association alone is insufficient. Use a polite live region, such as role="status", for the response. A background rejection or authoritative refresh must not move focus away from the amount input while the user is editing it.

Preserve the accepted baseline

Keep native keyboard operation for draft creation, editing, and publication. A stale draft keeps its proposal, blocks another save, and offers the explicit Discard edits and reload TITLE action. Do not read or resend automatically after 409 STALE_VERSION. Only a successful owned-list read discards the proposal; a failed reload keeps the input and the same action.

Anonymous users receive a sign-in path and cannot submit seller or bid actions. The seller route /my/listings keeps visible Sign in guidance and a link to /account; an anonymous listing detail explains that bidding requires signing in, links to /account, and hides or disables Place bid. Server session and ownership checks remain authoritative. The grader reports stale-draft and anonymous-access regressions separately as unscored safeguards; these do not remove points from independently working bid behavior.

Files to create or modify

Modify BidForm.svelte and ListingDetail.svelte as needed. Preserve the existing state and API boundaries, seller controls, and account recovery. Draft editing can remain in SellerListings.svelte.

Test and submit

Run the existing focused bid checks and client build. Check the interaction with the keyboard, including editing the amount while a response is pending. Reuse the earlier controlled-timing checks and update only those affected by your changes. The final milestone records the full verification run once.

Type checking is optional and does not affect points. The grader checks observable behavior, including controlled rejection and refresh responses.

Points

  • 40 points: low, closed, and unknown outcomes retain the proposal and display refreshed authoritative bid state.
  • 20 points: visible rejection feedback is associated with the bid input, which is marked invalid after rejection.
  • 15 points: one restrained status response is announced without stealing focus from the input while a response is pending.

Audit and verify the core marketplace

Use the Chapter 6.7 method to record one marketplace repair from milestones 32-35. Connect the barrier and repair to a repeated keyboard task, with the manual observations you actually made and the limits of those checks.

Apply the browser-test approach from Chapter 6.8 to the supplied login-recovery fixture. This separate application isolates a fixed recovery contract from your marketplace and its database. Its test result demonstrates coverage of that contract; your audit records evidence about the marketplace repair. The card below defines the assessed fixture requirements, faulty variants, test setup and runner commands.

Milestone 36 contributes the fixture runner, audit template and verification-record guidance through Git. Record the tested revision and actual results of the accepted API/client checks, client build and authenticated browser suite, with the fixture result recorded separately. Reuse earlier results only when the application and test files are unchanged; rerun affected checks after changes. Follow the card for the required records and submission details.

Marketplace milestone 36: audit and common-core verification

0 / 60 points

Start from accepted milestone 35. Use the project setup for contracts, checks, and submission.

Receive the supplied files

After milestone 35 passes, receive the milestone 36 contribution in your accepted marketplace repository:

Terminal window
git fetch origin
git diff HEAD..origin/milestone-36
git merge --ff-only origin/milestone-36

Commit unfinished local edits before merging. If local commits prevent a fast-forward, inspect the contribution and use git cherry-pick origin/milestone-36, preserving your work. If a supplied path already exists, compare and resolve it without replacing your application, tests or README. The contribution contains only the additive support files described below.

One marketplace audit record

Submit the single marketplace audit record introduced in Chapters 6.7-6.8. Use an actual repair from milestones 32-35. Keep ACCESSIBILITY-AUDIT.md concise, using the supplied template. Record the tested revision, route/task and browser/conditions. Explain the barrier before the repair, what changed, and what happened when you repeated that same keyboard sequence.

Keep the manual keyboard and reflow observations alongside the automated result. Name one specific untested condition. Label a controlled defect honestly if you use one on a separate copy. The automatic record check verifies completed evidence fields and removed placeholders; it cannot verify the truth of manual observations or establish accessibility conformance.

Write a test for the supplied login-recovery fixture

Write e2e-tests/tests/student_accessibility.ts against the supplied fixture, using the browser-test approach from Chapter 6.8. Keep this filename without .spec: the supplied runner selects it explicitly, while the normal marketplace suite ignores it. Rename an earlier student_accessibility.spec.ts if you have one. This replaces the open-ended test of a chosen marketplace repair. The fixture is a separate, fixed application: it needs no accounts, database, or running marketplace. Keep the existing tests of your actual application, including their controlled-timing coverage.

The supplied runner sets baseURL; open /account. The fixed contract is:

  1. The form has fields labelled Email and Password and a Log in button. Fill both fields with nonempty test values, focus the button, and submit with Enter.
  2. The submission is always rejected. A summary with role alert receives focus.
  3. Reach the summary link named Return to login details, then activate it with Enter. From the focused summary, Tab reaches it; alternatively, use the link’s focus() method to establish the starting point for keyboard activation.
  4. Email receives focus and both fields retain their submitted values.

The graded requirement is keyboard activation of recovery followed by assertions that Email receives focus and both values are retained. Summary-focus and Tab navigation assertions are optional extra coverage. The runner executes your test against the working fixture and four public faulty variants:

  • recovery-focus: recovery leaves focus on the link instead of Email.
  • email-cleared: recovery clears Email.
  • password-cleared: recovery clears Password.
  • pointer-only-recovery: mouse recovery works, but Enter does not activate recovery.

Your test must pass on the working fixture and fail on every faulty variant. Their source and the runner configuration are supplied in e2e-tests/tests/helpers/accessibility/. Run the same command used in grading from e2e-tests/:

Terminal window
node tests/helpers/accessibility/run.mjs

With the existing container setup, run it from the project root:

Terminal window
docker compose -p wsd-marketplace-test \
-f compose.yaml -f compose.test.yaml -f compose.browser-tests.yaml \
run --build --rm --no-deps e2e-tests node tests/helpers/accessibility/run.mjs

Perform recovery activation and its checks inside the test body, or in a helper called from that body. Hooks such as beforeEach may prepare the page and rejected login, but a failed hook or fixture does not establish defect detection.

Keep any test helpers under e2e-tests/tests/; grading copies that tree and uses the supplied runner and configuration. Retrying assertions, explicit waits and separate browser contexts are supported. Use the configured baseURL when creating a separate context. Each variant starts fresh, so no run depends on accounts or data created by another run. The runner reports each variant result and an overall outcome; record the actual command and outcome in the audit. The shared configuration allows 30 seconds per test, 5 seconds for assertions and actions, 10 seconds for navigation, and 45 seconds for each variant’s suite. A whole-test timeout or process failure does not count as detecting a defect. Generated reports are stored in test-results/accessibility/ under e2e-tests/; keep that directory out of your commit.

Keep the audit’s marketplace repair and the supplied fixture test distinct. Passing the fixture test demonstrates coverage of this fixed recovery contract; it does not establish that your own marketplace repair works. You may add a test of that repair, but it is not an additional requirement for these points.

Record the verified common core

Git supplies docs/part6-verification.md and docs/ACCESSIBILITY-AUDIT.template.md. Merge the baseline section from the first file into your existing README, and copy the second file to ACCESSIBILITY-AUDIT.md in the project root. Keep the supplied field names and replace TODO values with your observations. These additive files do not replace your README. If either path already exists during the Git merge, compare and merge it while preserving your accepted work.

The templates already state the schema versions and source-inferred browser contract. You record the tested revision, actual outcomes and known limit. The later electives use independent exercise files rather than a branch of this repository.

Use one complete final-candidate run of the accepted API/client tests, client build and authenticated browser suite. Run the supplied fixture test separately. An existing run from milestone 35 can be reused when the application and test files are unchanged; cite that tested revision and result.

If behavior or tests changed, rerun the affected checks before recording the final candidate. Record the outcomes, the tested revision and one known limit. Retain git rev-parse HEAD as the way to identify the final submitted checkout after committing the record.

Part 8 examines CI and release decisions using its own exercise files. Keep generated builds, dependencies, credentials, databases and test reports out of the commit.

Points

  • 20 points: the accessibility record completes the supplied fields for the marketplace task, before/after repair, fixture test result, manual observation and explicit limit.
  • 20 points: the submitted test passes the working fixture and detects all four public faulty variants, and the accepted accessible marketplace workflows remain intact.
  • 20 points: the common-core README record identifies the tested revision, verification outcomes and known limit using the supplied fields.

Shared project reference

The browser-proxy files installed in milestone 29 remain in use throughout milestones 32-36. Type checking is optional for Part 6 points.

After each preceding milestone passes, run git fetch origin as shown in the current card. Fetching prepares the step for grading without changing your working files. Milestones 32, 34 and 35 provide optional previous-step references; merging those references is not required. Milestones 33 and 36 supply additive support files: follow their cards’ inspection and merge steps when you reach them. If a contributed path already exists, compare and merge its contents while preserving your accepted application, tests and documentation.

Use the isolated two-file Compose configuration for migrations and API/client checks. For browser tests use the accepted three-file proxy topology:

Terminal window
docker compose -p wsd-marketplace-test \
-f compose.yaml \
-f compose.test.yaml \
-f compose.browser-tests.yaml \
run --build --rm e2e-tests
docker compose -p wsd-marketplace-test \
-f compose.yaml \
-f compose.test.yaml \
-f compose.browser-tests.yaml \
down

For the separate fixture check, use the runner command in the milestone 36 card.

Run suites sequentially and stop only their named project. Commit requested source/tests and completed records, inspect git diff, and submit with git push origin master. Keep generated builds, dependencies, credentials, databases and test results out of the commit. Milestone 36 is the submission for the marketplace audit and supplied-fixture keyboard test introduced in Chapters 6.7-6.8.

Check Your Understanding

  1. Why should layout checks include long content and increased text size as well as narrow viewports?
  2. When does moving focus to an error summary help a user recover from a form submission?
  3. How can dynamic bid feedback distinguish a proposal from a confirmed result?
  4. What does a manual keyboard review reveal that an automated accessibility scan may miss?
  5. Why should the verification record identify the tested revision and known limits together?