ARIA-attributter og skærmlæsere
Kombiner Tailwinds sr-only-utility med aria-*-HTML-attributter for at give skærmlæsere kontekst uden at påvirke det visuelle layout.
ARIA-attributter og skærmlæsere er en gratis Tailwind CSS Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Tailwind CSS Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Tailwind CSS Academy-kurset indeholder 4 lektioner i alt.
Sådan fungerer skærmlæsere
Skærmlæsere er hjælpeteknologisk software, der omdanner visuelt indhold til tale eller output i punktskrift. De læser tilgængelighedstræet — en struktureret repræsentation af siden, der er afledt af DOM'en — i stedet for det visuelle layout. Tilgængelighedstræet indeholder elementroller, navne, tilstande og egenskaber. Tailwinds visuelle klasser påvirker DOM'en, men for at kommunikere korrekt med skærmlæsere har du også brug for korrekt HTML-semantik og ARIA-attributter.
Semantisk HTML først
Før du tager ARIA i brug, skal du bruge semantiske HTML-elementer — de har indbyggede tilgængelighedsroller, som skærmlæsere forstår uden ekstra attributter. Et <button> annonceres som »knap«, et <h1> som en overskrift på niveau 1, og et <nav> som et navigationslandmærke. Hvis du bruger <div> til alt, skal du tilføje ARIA for at kompensere. Den første regel for ARIA er: Brug semantisk HTML, hvis et indbygget element allerede leverer semantikken.
<!-- BAD: div-based — requires ARIA to make accessible -->
<div class='flex items-center gap-2 cursor-pointer' onclick='handleClick()'>
<span class='text-sm'>Submit</span>
</div>
<!-- GOOD: semantic button — accessible by default -->
<button
type='submit'
class='flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg'
>
Submit
</button>
<!-- BAD: no landmark semantics -->
<div class='flex gap-4'>Nav links...</div>
<!-- GOOD: nav landmark -->
<nav class='flex gap-4' aria-label='Main navigation'>Nav links...</nav>Tailwinds sr-only-hjælpeklasse
Tailwinds sr-only-klasse skjuler indhold visuelt, men gør det stadig tilgængeligt for skærmlæsere. Den bruger en særlig CSS-teknik: en udklipning på 1×1 pixel med skjult overflow og absolut positionering, som fjerner elementet fra det visuelle layout, men beholder det i tilgængelighedstræet. Brug sr-only til etiketter, beskrivelser og kontekst, som skærmlæserbrugere har brug for, men som seende brugere får gennem visuelle signaler.
/* What .sr-only does */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
<!-- Usage: icon buttons need visible labels for screen readers -->
<button class='p-2 rounded-lg hover:bg-gray-100'>
<XMarkIcon class='h-5 w-5 text-gray-700' />
<span class='sr-only'>Close dialog</span>
</button>ARIA-etiketter og -beskrivelser
aria-label angiver et tilgængeligt navn, når der ikke findes en synlig tekstetiket. Brug den til knapper, der kun indeholder et ikon, eller til elementer, hvis visuelle etiket ikke er tilstrækkelig. aria-labelledby refererer til ID'et for et synligt element, der skal bruges som det tilgængelige navn. aria-describedby refererer til et element, der giver supplerende kontekst — f.eks. en fejlmeddelelse eller et tip under et formularfelt — som annonceres efter elementets etiket.
<!-- aria-label for icon-only button -->
<button aria-label='Open notifications'
class='p-2 rounded-lg hover:bg-gray-100'>
<BellIcon class='h-5 w-5' />
</button>
<!-- aria-labelledby references visible heading -->
<section aria-labelledby='pricing-heading'>
<h2 id='pricing-heading' class='text-2xl font-bold'>Pricing</h2>
{/* screen reader: 'Pricing, region' */}
</section>
<!-- aria-describedby links to help text -->
<div>
<label for='email'>Email</label>
<input id='email' type='email' aria-describedby='email-hint' />
<p id='email-hint' class='text-sm text-gray-500 mt-1'>
We will never share your email.
</p>
</div>ARIA-liveområder
ARIA-liveområder annoncerer ændringer i dynamisk indhold til skærmlæsere uden at flytte fokus. Brug aria-live='polite' til ikke-hastende opdateringer (f.eks. når søgeresultater indlæses) og aria-live='assertive' til hastende meddelelser (f.eks. fejlalarmer). Indholdet i liveområdet annonceres, når det ændres. Tailwind-klasser formgiver den visuelle beholder; tilgængelighedsadfærden kommer udelukkende fra ARIA-attributten.
<!-- Polite: announced after current announcement finishes -->
<div
aria-live='polite'
aria-atomic='true'
class='sr-only' {/* screen reader only — no visual display */}
>
{searchStatus} {/* e.g., 'Loading results...' or '12 results found' */}
</div>
<!-- Assertive: interrupts current announcement -->
<div
aria-live='assertive'
role='alert'
class='fixed top-4 right-4 bg-red-50 border border-red-200 p-4 rounded-lg'
>
{errorMessage}
</div>ARIA-roller til brugerdefinerede komponenter
Når du bygger brugerdefinerede interaktive komponenter med ikke-semantisk HTML (f.eks. en div-baseret kombinationsboks eller en brugerdefineret til/fra-knap), skal du tilføje den relevante ARIA-rolle for at angive, hvad elementet er. Almindelige roller omfatter button, checkbox, combobox, listbox, option, tab, tabpanel, dialog og alert. Rollen definerer den implicitte tastaturkontrakt og angiver, hvilke tilstande der er relevante for denne elementtype.
<!-- Custom toggle switch -->
<div
role='switch'
aria-checked={enabled}
tabIndex={0}
onKeyDown={(e) => e.key === 'Enter' && toggle()}
onClick={toggle}
class={cn(
'relative inline-flex h-6 w-11 items-center rounded-full cursor-pointer',
'focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2',
enabled ? 'bg-blue-600' : 'bg-gray-300'
)}
>
<span class='sr-only'>{enabled ? 'Enabled' : 'Disabled'}</span>
<span class={cn('inline-block h-4 w-4 rounded-full bg-white transition-transform',
enabled ? 'translate-x-6' : 'translate-x-1'
)} />
</div>ARIA-tilstande og -egenskaber
ARIA-tilstande (aria-checked, aria-expanded, aria-selected, aria-disabled) angiver et elements aktuelle interaktive tilstand. Opdatér dem programmatisk, så tilgængelighedstræet holdes synkroniseret med den visuelle tilstand. Hvis en udfoldningssektion udvides, skal du angive aria-expanded='true' på udløseren. Hvis et listeelement er valgt, skal du angive aria-selected='true'. Skærmlæsere annoncerer disse tilstandsændringer, mens brugeren interagerer.
function AccordionItem({ title, content }) {
const [expanded, setExpanded] = useState(false);
const contentId = 'accordion-content-' + title.replace(/\s/g, '-');
return (
<div class='border-b border-gray-200'>
<button
onClick={() => setExpanded(!expanded)}
aria-expanded={expanded}
aria-controls={contentId}
class='flex w-full justify-between items-center py-4 text-left'
>
{title}
<ChevronDownIcon class={cn('h-5 w-5 transition-transform', expanded && 'rotate-180')} />
</button>
<div
id={contentId}
hidden={!expanded}
class='pb-4 text-gray-600 text-sm leading-relaxed'
>
{content}
</div>
</div>
);
}Skjul dekorativt indhold for skærmlæsere
Ikke alt visuelt indhold bør annonceres til skærmlæsere. Dekorative billeder, duplikerede ikoner (når tekst allerede formidler betydningen) og layoutelementer bør skjules fra tilgængelighedstræet. Brug aria-hidden='true' til at fjerne et element fra træet uden at skjule det visuelt. Tilføj aldrig aria-hidden='true' til elementer, der indeholder eller er relateret til elementer, som kan modtage tastaturfokus.
<!-- Decorative image: hide from screen reader -->
<img
src='/decorative-pattern.svg'
alt='' {/* empty alt = decorative */}
class='absolute inset-0 opacity-5'
aria-hidden='true'
/>
<!-- Icon alongside text: icon is decorative -->
<button class='flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg'>
<SaveIcon class='h-4 w-4' aria-hidden='true' />
Save Changes {/* text label is sufficient */}
</button>
<!-- Spinner: decorative, but announce loading state differently -->
<button disabled aria-busy='true'>
<span aria-hidden='true' class='animate-spin'>...</span>
<span class='sr-only'>Saving...</span>
</button>Landmærkeområder til navigation med skærmlæser
Landmærkeroller giver skærmlæserbrugere mulighed for at springe direkte til vigtige sektioner på siden. De semantiske HTML5-elementer <main>, <nav>, <header>, <footer>, <aside> og <section> (med et navn) opretter automatisk landmærkeområder. Hvis du har flere <nav>-elementer, skal du skelne mellem dem med aria-label, så skærmlæserbrugere kan navigere til det rigtige.
<body class='min-h-screen flex flex-col'>
{/* banner landmark */}
<header class='bg-white border-b border-gray-200'>
<nav aria-label='Main navigation' class='flex gap-6 px-6 h-16 items-center'>
{/* nav links */}
</nav>
</header>
<div class='flex flex-1'>
{/* complementary landmark */}
<aside class='w-64 border-r border-gray-200' aria-label='Sidebar'>
<nav aria-label='Section navigation'>{/* sidebar links */}</nav>
</aside>
{/* main landmark */}
<main class='flex-1 p-8'>{/* page content */}</main>
</div>
{/* contentinfo landmark */}
<footer class='bg-gray-50 border-t border-gray-200 py-8'>
{/* footer content */}
</footer>
</body>Visuelt skjulte fokusmeddelelser
Nogle brugergrænseflademønstre kræver meddelelser til skærmlæsere, som ikke har en visuel pendant. Når en formular f.eks. er sendt korrekt, er det et godt mønster at flytte fokus til en sr-only-overskrift med teksten »Formularen er sendt«, så skærmlæserbrugere informeres uden at den visuelle brugergrænseflade ændres. Kombinér sr-only med en ref med tabIndex={-1}, der modtager programmatisk fokus.
// After form submit, announce result to screen readers
function SubmitForm() {
const [submitted, setSubmitted] = useState(false);
const statusRef = useRef(null);
const handleSubmit = async (e) => {
e.preventDefault();
await submitForm();
setSubmitted(true);
// Move focus to the sr-only status message
statusRef.current?.focus();
};
return (
<form onSubmit={handleSubmit}>
{submitted && (
<p
ref={statusRef}
tabIndex={-1}
class='sr-only'
>
Your form was submitted successfully. We will be in touch.
</p>
)}
{/* form fields */}
<button type='submit'>Submit</button>
</form>
);
}Automatiseret test af skærmlæsere
Selv om intet kan erstatte test med en rigtig skærmlæser (NVDA på Windows, VoiceOver på macOS/iOS, TalkBack på Android), kan automatiserede værktøjer finde mange almindelige problemer. axe-core kontrollerer for manglende ARIA-etiketter, ugyldig brug af roller og utilgængelig farvekontrast. Integrér det i dit CI-forløb med jest-axe til komponenttest eller axe-playwright til ende-til-ende-test.
// jest-axe: test individual React components
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('Button has no accessibility violations', async () => {
const { container } = render(
<button class='px-4 py-2 bg-blue-600 text-white rounded'>
Submit
</button>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
test('Icon button has accessible label', async () => {
const { container } = render(
<button aria-label='Close dialog'>
<XMarkIcon class='h-5 w-5' />
</button>
);
expect(await axe(container)).toHaveNoViolations();
});Hurtigt tjek
Test din forståelse af begreberne fra denne lektion i Tailwind CSS Mastery.
Opsummering af lektionen
I denne lektion lærte du, at sr-only skjuler indhold visuelt, men gør det tilgængeligt for skærmlæsere, at ARIA-etiketter, -roller og -tilstande supplerer brugerdefinerede komponenter med tilgængelighedssemantik, og at aria-live-områder annoncerer ændringer i dynamisk indhold uden at flytte fokus. Nu anvender vi alle tilgængelighedsteknikkerne til at bygge tilgængelige formular-komponenter.
Lær HTML med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “ARIA-attributter og skærmlæsere” gratis?
Ja — hele teksten til “ARIA-attributter og skærmlæsere” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Tailwind CSS Academy-kurset, skal du opgradere til CoddyKit PRO. Tailwind CSS Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “ARIA-attributter og skærmlæsere”?
Kombiner Tailwinds sr-only-utility med aria-*-HTML-attributter for at give skærmlæsere kontekst uden at påvirke det visuelle layout. Du øver dig i Tailwind CSS Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Tailwind CSS Academy?
Der kræves ingen tidligere erfaring. Tailwind CSS Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “ARIA-attributter og skærmlæsere”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Tailwind CSS Academy-lektion?
Ja. Alle Tailwind CSS Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Farvekontrast og læsbar tekst
- Fokusindikatorer og tastaturnavigation
- ARIA-attributter og skærmlæsere
- Tilgængelige formularkomponenter