Report / DEMO-01
Two barriers,
step by step.
Nordljus Demoaffär shows how to describe and hand over a small code change.
Fictional work sample. The shop and product are made up. No customer data, real purchases or payments are used. The before barriers are deliberate.
The demo pages and literal code examples are in Swedish. The report is in English.
1. What the work sample covers
The task is to choose a lantern, read the cart and select delivery in a fictional shop. We show code changes for two barriers: delivery options that respond only to clicks, and a cart that is too wide for a narrow screen.
A report like this is part of the handover. In customer work, you also get the changed code. Your shop’s developer reviews, merges and deploys it.
2. Which parts can be changed?
- Product page
- The same in both versions. Neither deliberate barrier is here.
- Cart
- The work sample’s own code. Width and order change in the after version.
- Delivery options
- The work sample’s own code. Clickable options become standard radio buttons.
- Payment and identity checks
- Outside the scope. The work sample has no payment component.
You would need to confirm an equivalent map for your shop. Being outside the scope says nothing about accessibility or legal responsibility.
3. What has been tested?
The source work sample has an automated Chromium run from 2 September 2026 and a separate AI-agent browser run that day. These are older observations of source material, not human test results or results for this adapted website.
Checks of this website are recorded separately for each build and test run. Older observations do not prove that every current page works.
Manual testing, screen-reader testing, testing with users and production testing have not been carried out. A Chromium run gives no results for Safari, Firefox or other environments.
4. Delivery options need a mouse
Task: Choose delivery and continue to the illustrated payment step using a keyboard.
Before the change
Tab reaches the options, but Space, Enter and arrow keys select nothing. Mouse clicks work. Continuing without a choice shows a message and stays on the step.
- Open checkout before the change (Swedish).
- Tab to Hemleverans (home delivery).
- Try Space, Enter and Arrow Down. Check whether anything is selected.
- Tab to Fortsätt till betalning (continue to payment) and press Enter.
- Compare with clicking an option and continuing.
The code change
Use standard radio buttons with clear labels in one group. The browser handles keyboard selection. Connect the error message to the options and move focus there when someone continues without a choice.
After the change
Open checkout after the change (Swedish). Repeat the steps. Space should select the focused option; arrow keys should move the selection. Continuing with a selection should name it and move focus to the payment heading.
This checks only the delivery options. Payment and identity checks cannot be tested here. WCAG 2.1.1 is a reference for keyboard behaviour, not certification of the page.
5. The cart is too wide
Task: Read the items and summary and reach the checkout button on a narrow screen.
Before the change
Two columns with fixed minimum widths require horizontal scrolling at 320 pixels. The checkout button falls outside the viewport.
- Open the cart before the change (Swedish).
- Set the viewport to 320 CSS pixels wide.
- Check horizontal scrolling and whether Till kassan (checkout) is visible.
- Compare the cart after the change (Swedish) at the same width.
The code change
Start with one column. Use two only when there is room. Let long product codes wrap. Put the summary and button after the items.
After the change
At 320 CSS pixels, the page should fit without horizontal scrolling. The order should be items, summary, checkout. At wider widths, two columns should fit without overlap.
Layout is checked at a viewport width of 320 CSS pixels. These checks do not test browser zoom or text resizing. WCAG 1.4.10 is the reference for this check.
6. Payment is outside the scope
Both checkouts show the same payment explanation. No external service is connected. No payment, identity check or order can be completed.
We have not tested or changed such a component. In customer work, you and the provider need to consider any barriers there.
7. Repeat the checks
We use Codex to run repeatable scripted checks of specified keyboard interactions, focus, accessibility-tree data and layout, plus axe checks. Layout is checked at a viewport width of 320 CSS pixels and other specified narrow widths. These checks do not test browser zoom or text resizing. Manual testing, screen-reader testing and testing with users have not been carried out.
Delivery options
- Try Tab, Space and arrow keys in the after version.
- Continue without a choice. The error should appear and focus should reach the first option.
- Select an option. The error should clear.
- Continue again. The selected method should be named and focus should reach the payment heading.
- Also try clicking an option’s label.
- Compare options, delivery times and prices with the before version.
Cart
- Check the after version at 320 CSS pixels wide.
- Check that the whole checkout button fits and Tab reaches it.
- Read items, summary, checkout in that order.
- Increase the width and check for overlapping columns.
- Check that text and prices match in both versions.
8. If a change needs to be rolled back
Revert the change through the shop’s version control. Repeat the same checks and see whether the original behaviour returns. The work sample affects no saved data or external systems.
Your developer needs to know the rollback steps before deployment. You or your agency carry them out. We do not work in production.
9. Code changes
These are the parts that control delivery selection and cart width in this version. The literal examples below are in Swedish. They help explain the difference; they are not instructions to paste into another shop without review.
Before: delivery behaviour
import './delivery-options.css';
// Hinder 01 — FÖRE (avsiktligt seedat hinder).
// Leveransvalen är div-element med role="radio" och tabindex="0". De får fokus
// och fungerar med pekare (click), men det finns ingen hantering av mellanslag,
// Enter eller piltangenter. Ett div-element har ingen inbyggd aktivering, så
// tangentbordet ändrar aldrig valet och kassan kan inte fortsätta utan pekdon.
const continueButton = document.querySelector<HTMLButtonElement>('.kassa-fortsatt button');
const options = Array.from(document.querySelectorAll<HTMLElement>('.leveransval[role="radio"]'));
const warning = document.getElementById('leverans-varning');
const status = document.getElementById('kassa-status');
const paymentHeading = document.getElementById('betalning-rubrik');
let selected: HTMLElement | null = null;
function choose(option: HTMLElement): void {
for (const candidate of options) {
candidate.setAttribute('aria-checked', candidate === option ? 'true' : 'false');
}
selected = option;
if (warning) {
warning.hidden = true;
}
}
for (const option of options) {
option.addEventListener('click', () => choose(option));
}
continueButton?.addEventListener('click', (event) => {
event.preventDefault();
if (!selected) {
if (warning) {
warning.hidden = false;
}
if (status) {
status.textContent = '';
}
return;
}
const name = selected.querySelector('.leveransval-namn')?.textContent?.trim() ?? 'okänt';
if (status) {
status.textContent = `Leveranssätt valt: ${name}. Nästa steg är betalning hos extern leverantör. Steget visas bara som exempel.`;
}
paymentHeading?.focus();
});
Before: cart width
/* Hinder 02 — FÖRE (avsiktligt seedat hinder).
Varukorgen har en fast tvåkolumnslayout med fasta minsta bredder, en
sticky-summering med fast bredd och artikelnummer som inte får radbrytas.
Vid 320 CSS-pixlars bredd (motsvarar 1280 px vid 400 % zoom) kräver
sidan horisontell rullning och knappen ”Till kassan” ligger utanför vyn. */
.varukorg-layout {
display: grid;
grid-template-columns: minmax(440px, 1fr) 280px;
gap: 1.5rem;
min-width: 760px;
align-items: start;
}
.varukorg-rad {
display: grid;
grid-template-columns: 96px minmax(300px, 1fr) 120px;
gap: 1rem;
align-items: start;
}
.rad-pris {
text-align: right;
}
.artikelnummer {
white-space: nowrap;
}
.varukorg-summering {
position: sticky;
top: 1rem;
width: 280px;
}
After: delivery behaviour
import './delivery-options.css';
// Hinder 01 — EFTER (ändring).
// Leveransvalen är native radioknappar i ett fieldset med legend och label.
// Webbläsaren sköter mellanslag, piltangenter och pekare. Skriptet kopplar bara
// feltexten programmatiskt (aria-describedby/aria-invalid), flyttar fokus vid fel
// och ger statustext när kassan går vidare.
const continueButton = document.querySelector<HTMLButtonElement>('.kassa-fortsatt button');
const radios = Array.from(document.querySelectorAll<HTMLInputElement>('input[name="leverans"]'));
const error = document.getElementById('leverans-fel');
const status = document.getElementById('kassa-status');
const paymentHeading = document.getElementById('betalning-rubrik');
function selectedRadio(): HTMLInputElement | null {
return radios.find((radio) => radio.checked) ?? null;
}
function clearError(): void {
if (error) {
error.hidden = true;
}
for (const radio of radios) {
radio.removeAttribute('aria-invalid');
radio.removeAttribute('aria-describedby');
}
}
function showError(): void {
if (error) {
error.hidden = false;
for (const radio of radios) {
radio.setAttribute('aria-invalid', 'true');
radio.setAttribute('aria-describedby', error.id);
}
}
if (status) {
status.textContent = '';
}
radios[0]?.focus();
}
for (const radio of radios) {
radio.addEventListener('change', clearError);
}
continueButton?.addEventListener('click', (event) => {
event.preventDefault();
const choice = selectedRadio();
if (!choice) {
showError();
return;
}
clearError();
const label = document.querySelector(`label[for="${choice.id}"] .leveransval-namn`);
const name = label?.textContent?.trim() ?? choice.value;
if (status) {
status.textContent = `Leveranssätt valt: ${name}. Nästa steg är betalning hos extern leverantör. Steget visas bara som exempel.`;
}
paymentHeading?.focus();
});
After: cart width
/* Hinder 02 — EFTER (ändring).
Enkolumnslayout som standard, tvåkolumnslayout först från 48 rem.
Inga fasta minsta bredder, min-width: 0 på grid-barn, radbrytning
tillåten för långa artikelnummer och sticky-summering endast vid
bredare fönster. Ingen horisontell rullning på sidnivå vid 320 CSS-px. */
.varukorg-layout {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1.5rem;
align-items: start;
}
.varukorg-layout > * {
min-width: 0;
}
@media (min-width: 48rem) {
.varukorg-layout {
grid-template-columns: minmax(0, 1fr) minmax(16rem, 20rem);
}
.varukorg-summering {
position: sticky;
top: 1rem;
}
}
.varukorg-rad {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.75rem;
align-items: start;
}
.varukorg-rad > * {
min-width: 0;
}
@media (min-width: 30rem) {
.varukorg-rad {
grid-template-columns: 6rem minmax(0, 1fr) auto;
gap: 1rem;
}
.rad-pris {
text-align: right;
}
}
.artikelnummer {
overflow-wrap: anywhere;
}
10. Older automated evidence
The original run used Playwright 1.61.1 and Chromium 149.0.7827.55 on macOS 27.0 on 2 September 2026 at 23:10:44. Fable 5.1 Max, an AI agent, started it. No assistive technology was used.
It detected the deliberate before keyboard barrier and the expected after selection. It also measured an overflowing before cart and an after cart that fitted at 320 CSS pixels.
In that run, axe-core 4.13.0 reported no findings from the selected rules on the after pages. Automated checks can miss barriers. This is neither certification nor a user test.
Run reference: PW-CHROMIUM-20260902-2310440200. Original sources and run details are kept in the project documentation.
11. Agent and human checks
The separate agent browser run on 2 September 2026 compared cart widths and tried delivery options with a pointer. It could not perform the keyboard check on the available surface. It was not a human manual run.
Run reference: AGENT-CURSOR-BROWSER-2026-09-02-2224.
VoiceOver, NVDA, JAWS and TalkBack: not tested. User tests with people with disabilities: not tested. Human checks of printing and 400% browser zoom: not tested.
12. What the evidence does not show
The sample shows an approach in a small fictional shop. It does not show customer results, demand for the service, that a whole shop meets a standard or that legal requirements have been met.
No results are given for production, real payments or environments that were not run. The report’s print view is HTML. A PDF you save from the browser has not been reviewed as an accessible, tagged PDF.