Tillgängliga STEM-diagram:
SVG, ARIA-beskrivet innehåll, syntolkning
En kemimolekyl, ett mitokondrie-tvärsnitt, ett fritt-kropp-kraftdiagram, grafen för ett tredjegraddspolynom — varje STEM-lärobok publicerad det senaste decenniet är byggd på bilder som en skärmläsare inte kan konsumera på ett meningsfullt sätt. Lösningen är inte “lägg till alternativtext.” Det är ett fyrlagers-system av tillgänglig SVG, strukturerade beskrivningar, syntolkning för animerade diagram och AT-kompatibilitetskunskap som inte överförs mellan operativsystem. Den här artikeln är produktionshandboken.
1. Varför STEM-diagram skiljer sig från alla andra tillgänglighetsproblem
En bloggartikelsbild med ett alt-attribut är ett löst problem. Ett STEM-diagram är det inte. Tre egenskaper hos vetenskapliga bilder bryter de antaganden som är inbyggda i alt, aria-label och skärmläsarens talmodell.
För det första är informationstätheten tillräckligt hög för att en enda mening inte kan bära den. En bensenring är sex kolatomer, sex väteatomer, omväxlande dubbelbindningar, ett delokaliserat pi-system, en plan geometri och en bindningslängd på 1,39 ångström. Alternativtextkonventionen ber om “ett kort textersättning”; bensen behöver ett stycke. Att komprimera det till en mening tappar antingen den strukturella informationen (“en bensenmolekyl”) eller producerar en oläslig löpande text som skärmläsaren måste stava ut i 180 ord per minut.
För det andra bär relationerna mellan element lika mycket mening som elementen själva. I ett fritt-kropp-diagram är pilen från lådan till väggen inte dekoration — det är normalkraften, och dess vinkel relativt gravitationsvektorn är svaret på problemet. En platt beskrivning kan inte koda “vinkeln mellan dessa två pilar är 90 grader och det är därför problemet löses”, eftersom en platt beskrivning saknar struktur. SVG, använt omsorgsfullt, har det.
För det tredje behöver STEM-studenter navigera diagrammet, inte bara höra det. En inlärare som arbetar igenom grafen för ett tredjegraddspolynom vill inte höra alternativtexten från början till slut — de vill landa på det lokala maxvärdet, fråga “vad är lutningen här”, och sedan gå till inflexionspunkten. Det är en annan interaktionsmodell än den skärmläsare levereras med som standard. Att bygga den kräver tangentbordshanterare på enskilda SVG-noder, ett ARIA-beskrivet innehållsträd och en reservlösning för de hjälpmedelstech-stackar som inte kan hänga med.
Kemimolekyler (atomer och bindningar), biologiska cellstrukturer (märkta regioner), fysikens kraftdiagram (vektorer med magnituder och vinklar) och matematiska funktionsgrafer (kurvor med namngivna drag). Var och en belastar ett annat lager av den tillgängliga SVG-stacken, och handboken i slutet formas av vad som går sönder för vilken.
2. SVG som tillgängligt underlag — och varför rasterformat är en återvändsgränd
Nästan varje publicerad STEM-lärobok levererar fortfarande sina diagram som PNG eller JPG. En rasterbild är ett ogenomträngligt pixelrutnät: den har en ingångspunkt för hjälpmedelstek, attributet alt, och en reservlösning, attributet longdesc som webbläsare spenderat tio år med att avveckla stödet för. Det finns ingen struktur inuti bilden som en skärmläsare kan adressera. Diagrammet är en svart låda med en etikett på framsidan.
SVG inverterar modellen. Varje form i ett SVG-dokument är en DOM-nod — adresserbar, fokuserbar, märkbar. En bensenring renderad som SVG har sex circle-element för kolen, sex line-element för bindningarna och ett omslutande g-element som namnger helheten. Var och en av dessa noder kan bära attributen role, aria-label, aria-labelledby, aria-describedby och tabindex. Skärmläsaren ser ett tillgänglighetsträd av namngivna regioner istället för en enda ogenomtränglig klump.
Den minimalt tillgängliga SVG:n bär tre saker på sitt rot-svg-element: role=“img”, aria-labelledby som pekar på ett title-barn, och aria-describedby som pekar på ett desc-barn eller på ett externt stycke via ID. Var och en är liten. Var och en gör arbete som de andra två inte kan.
<img src="benzene.png"
alt="Bensenmolekyl" />Bilden är ogenomtränglig. “Bensenmolekyl” ger ett namn och inget mer. En inlärare som behöver bindningsmönstret, ringgeometrin eller kol-väte-antalet kan inte få det från denna kod. Det finns ingen väg till den strukturella informationen utan att konsultera en annan källa.
<svg role="img"
aria-labelledby="benz-title"
aria-describedby="benz-desc"
viewBox="0 0 200 200">
<title id="benz-title">Bensenring</title>
<desc id="benz-desc">
En regelbunden hexagon av sex kolatomer,
var och en bunden till ett väte. Omväxlande
enkelbindningar och dubbelbindningar bildar en
plan aromatisk ring med delokaliserade elektroner.
</desc>
<g role="group" aria-label="Kolatomer">
<circle cx="100" cy="40" r="6"
tabindex="0"
aria-label="C1, topp"/>
</g>
<g role="group" aria-label="Bindningar">
</g>
</svg>Roten namnger och beskriver sig själv. Varje atom är en fokusbar, namngiven region. En skärmläsaranvändare kan höra sammanfattningen och sedan tabbas in i strukturen för att inspektera en enskild bindning. Samma kod betjänar en seende och en icke-seende läsare utan kompromiss.
En skarp varning: role=“img” på rot-svg:n ändrar vad hjälpmedelstek gör med barnen. Med role=“img” behandlar NVDA och JAWS hela SVG:n som en enda märkt bild och exponerar inte de inre noderna — även om dessa inre noder har tabindex. För att få båda beteendena — en sammanfattningsetikett överst och adresserbara barn inuti — lämna rotens roll oinställd (eller sätt role=“graphics-document” från W3C Graphics ARIA-modulen) och placera etiketten på ett barn-g snarare än på roten. Webbläsare och skärmläsare hanterar denna kombination ojämnt. Matrisen i avsnitt 6 dokumenterar vad som fungerar var.
3. Longdesc-ekvivalentstacken: var den långa beskrivningen faktiskt bor
Attributet longdesc var det ursprungliga svaret på “ett alt-attribut räcker inte.” Webbläsare har tyst tagit bort stödet under åren; Firefox tappade det i version 90, Safari implementerade det aldrig, Chrome behandlar det som en no-op. Den som fortfarande skriver longdesc=“benzene-desc.html” år 2026 levererar kod som ingenting läser. Ersättningen är inte ett enda attribut utan ett trelagersmönster som kombinerar en inlinebeskrivning, en synlig expanderbar panel och maskinläsbar metadata.
Lager ett är det inlinede desc-elementet inuti SVG:n. Två till fyra meningar. Läses av skärmläsare när SVG-roten annonseras. Det här är den nya longdesc — en beskrivning som är en del av SVG-dokumentet och följer med det vart SVG:n än hamnar.
Lager två är en synlig expanderbar beskrivningspanel bredvid diagrammet, tillgänglig för alla läsare, inte bara skärmläsaranvändare. En sammanfattningsrad plus en avslöjningsknapp som öppnar en längre textuell genomgång — vanligtvis tre till tio meningar för en kemimolekyl, längre för ett cellstrukturdiagram med tjugo märkta organeller. Den synliga panelen löser ett problem som den inlinede desc:n inte kan: studenter som kan se diagrammet men inte avkoda det (lässvaga inlärare, dyslexi-inlärare, vem som helst som lär sig materialet för första gången) behöver den långa beskrivningen också. Att placera den bakom en knapp döljer den inte för skärmläsare — de räknar upp avslöjningen, användaren aktiverar den och beskrivningen läses in i bufferten.
Lager tre är strukturerad metadata via JSON-LD. Ett CreativeWork-objekt vars accessibilityFeature-array räknar upp vad diagrammet erbjuder: longDescription, alternativeText, structuralNavigation, describedMath, tactileGraphic (om ett utskrivbart taktilt finns tillgängligt). Sökmotorer, innehållsrekommenderare och tillgänglighetsövervakningstjänster konsumerar alla denna metadata. Den gör ingenting för den omedelbara skärmläsarupplevelsen, men den gör diagrammet sökbart som tillgängligt innehåll — vilket spelar roll när en inlärare väljer mellan tre läroböcker och en av dem annonserar sina tillgänglighetsfunktioner i maskinläsbar form.
CreativeWork-objektet bor i ett script type=“application/ld+json”-block var som helst på sidan. Nycklar: accessibilityFeature (array av strängar — longDescription, alternativeText, structuralNavigation, MathML, describedMath), accessibilityHazard (noFlashingHazard, noMotionSimulationHazard), accessibilityAPI (ARIA), och accessMode (textual, visual) plus accessModeSufficient (de åtkomstlägen som ensamma räcker för att uppfatta verket). Ett diagram som levererar alla tre beskrivningslager bör publicera alla dessa.
4. Syntolkning för animerade diagram: DOM-mutationer som ledtrådsström
Statiska diagram är det enkla fallet. Det svåra fallet är det animerade diagrammet — ett mitokondrie som roterar i 3D, en sinuskurva som spåras längs x-axeln, en kemisk reaktion med bindningar som bryts och reformas under fyra sekunder. Det konventionella svaret är en videofil med ett syntolkningsspår, men det överger adresserbarheten hos SVG: det ögonblick du bakar in animationen i en video slutar varje noggrant märkt nod att vara en DOM-nod och blir ett pixel igen.
Det tillgängliga alternativet är att behålla animationen som SVG (eller Canvas med ett offscreen-tillgänglighetsträd) och sända ut syntolkningar allt eftersom animationen fortskrider, drivna av samma DOM-mutationer som driver den visuella förändringen. Mönstret: en MutationObserver bevakar SVG:n för förändringar — ett nytt transform-attribut, en bindning som dyker upp, en vektor som roterar — och vid varje signifikant förändring skriver en kort textuppdatering till en global aria-live=“polite”-region. Den visuella animationen driver en ljudberättelse, genererad i realtid från samma sanningskälla.
Implementeringen har tre rörliga delar. Den första är animationen själv, uttryckt som en sekvens av tidslinjenycklar — samma data som SVG-renderaren konsumerar. Den andra är ett anteckningslager: varje nyckelruta bär en kort text som beskriver vad som förändras vid det ögonblicket (“bindning bildas mellan C1 och C2,” “vågen korsar noll nerifrån”). Den tredje är syntolkningsdrivrutinen, som prenumererar på tidslinjen, plockar upp varje annoterad nyckelruta och skriver sin text till live-regionen ett par hundra millisekunder innan den visuella förändringen inträffar. Försprångstiden matchar vad professionell syntolkning gör för film: beskrivningen hörs precis innan den visuella händelsen, inte efter.
Tre felsätt är tillräckligt vanliga för att vara värda att nämna. För det första, burst-uppdateringar. En animation som avfyrar 60 mutationer per sekund dränker skärmläsarens syntetiserare — annonserna köar upp, fördröjer animationen och blir obegripliga. Annotera bara de semantiskt signifikanta nyckelrutorna, inte varje bildruta, och begränsa till ungefär en annons per 1 500 ms. För det andra, att missa starten. En live-region som inte existerade innan animationen börjat annonserar inte sin första uppdatering tillförlitligt (se artikeln om aria-live-ramverket för det underliggande schemaläggningsproblemet). Montera live-regionen tom vid sidladdning. För det tredje, ingen pauskontroll. Användare behöver kunna pausa syntolkningen, sakta ner den eller gå igenom den en händelse åt gången. Bygg ett litet kontrollspält — spela, pausa, föregående händelse, nästa händelse — och koppla dess knappar till samma tidslinjesdrivrutin.
Varje animerat STEM-diagram måste respektera mediafrågan prefers-reduced-motion: reduce. Ersättningen är inte “ingen animation, ingen beskrivning” — det är en statisk SVG med den långa beskrivningen från lager två i beskrivningsstacken expanderad som standard. Animation är ett åtkomstläge; beskriven statisk bild är ett annat. En inlärare med vestibulär störning som slagit på reducerad rörelse behöver fortfarande diagrammet, bara inte rotationen.
5. Tangentbordsnavigering mellan datapunkter i interaktiva diagram
En matematisk funktionsgraf som en seende student kan skrubba med markören är inte tillgänglig förrän en icke-seende student kan skrubba den med tangentbordet. Mekanismen är välkänd och dåligt implementerad i praktiken: varje signifikant datapunkt på kurvan får tabindex=“0”, en aria-label som beskriver dess koordinater och eventuellt namngivet drag (“lokalt maxvärde vid x = -1, y = 4”), och en tangentbordshanterare som svarar på piltangenter för finkornig rörelse mellan angränsande punkter.
Rätt granularitet spelar större roll än folk inser. Att tabba igenom varje plottad pixel av en tredjegradskurva är fientligt — användaren hör tusentals “x är lika med 0,1, y är lika med 0,001”-annonser innan de når något intressant. Att bara tabba igenom de namngivna dragen (lokala maxvärden, minvärden, inflexionspunkter, x-skärningspunkter, y-skärningspunkter) är för grovkornigt. Den pragmatiska kompromissen: två navigeringslager. Tabbtangenten cyklar genom namngivna drag enbart — vanligtvis tre till sju på en kurva — och piltangenterna, när ett drag är i fokus, stegar vänster och höger längs kurvan i en inlärardefinerad stegstorlek, och annonserar koordinaten vid varje steg. Home och End hoppar till kurvans vänstra och högra gränser. Page Up och Page Down hoppar till nästa namngivna drag.
För ett diagram med flera serier — ett kemikinetek-diagram, ett fysikaliskt oscillationsdiagram med två vågformer — lägg till en seriebytar-axel. Uppåt- och nedåtpiltangenterna rör sig mellan serier vid den aktuella x-koordinaten; vänster och höger rör sig längs den aktuella serien. Konventionen parallellerar hur kalkylbladläsare navigerar rader och kolumner och återanvänder en mental modell som många användare redan har.
En detalj som ofta missas: den fokuserade datapunkten behöver en synlig fokusindikator. En icke-seende användare behöver den inte, men en seende skärmläsaranvändare gör det, liksom partnerinstruktörer som tittar över studentens axel. Rendera en fokusring runt det fokuserade SVG-elementet med :focus-visible-styling — samma konvention som knappfokusringar, tillämpad på SVG-noder som webbläsaren inte stilsätter som standard.
| Diagramtyp | SVG-kod | Lång beskrivning | Syntolkning | Tangentbordsnavigering |
|---|---|---|---|---|
| Kemimolekyl | Krävs — role group per atom, per bindning | Krävs — 3 till 6 meningar | Endast om animerad reaktion | Tabba igenom atomer, pil till bindningar |
| Biologisk cellstruktur | Krävs — role group per märkt region | Krävs — 5 till 12 meningar | Endast om animerad process | Tabba igenom organeller i z-ordning |
| Fysikaliskt kraftdiagram | Krävs — role group per vektor | Krävs — 3 till 5 meningar med magnituder och vinklar | Krävs om interaktivt (dragning av vektorer) | Tabba igenom vektorer, pil för rotation |
| Matematisk funktionsgraf | Krävs — namngivna drag som noder | Krävs — domän, räckvidd, asymptoter, drag | Valfritt — bara om spårningsanimation | Tabb för drag, pil för finkornig skrubbning |
6. AT-kompatibilitet: vad som fungerar och var SVG-trädet är brutet
Den hårdaste sanningen i den här artikeln: den tillgängliga SVG-stacken fungerar inte på samma sätt i olika webbläsare och skärmläsare, och bristerna är inte buggar i din kod. NVDA på Firefox är den mest tillförlitliga kombinationen — den enda där varje mönster i den här artikeln beter sig som W3C:s SVG-tillgänglighetsmappning säger att det ska. Varje annan kombination har minst en känd brist.
Safari på macOS med VoiceOver är den mest problematiska av de stora stackarna. WebKits SVG-tillgänglighetsträd har dokumenterade hål i hur det exponerar inre g-element med ARIA-etiketter: etiketterna finns i DOM:en och är inspekterbara med tillgänglighetsgranskaren, men VoiceOver plockar inte alltid upp dem när användaren navigerar med VO-Höger-Pil. Beteendet är inkonsekvent — ibland annonseras de inre etiketterna, ibland läses bara SVG-rotens etikett, utan något klientsynligt mönster. WebKit bugzilla har öppna ärenden som går tillbaka till 2020 om detta. Den pragmatiska implikationen: om ditt STEM-diagram fungerar på Mac är det ett nödvändigt villkor, inte ett tillräckligt. Testa på Windows med NVDA innan du levererar.
Chrome på Windows med JAWS är den näst mest tillförlitliga stacken — nära NVDA + Firefox, med en brasklapp: JAWS behandlar SVG:s role=“img” mer aggressivt än NVDA och kollapsar inre noder oftare. Lösningen är att använda role=“graphics-document” från W3C Graphics ARIA-modulen på rot-svg:n, vilket JAWS hanterar korrekt. NVDA hanterar det också korrekt. Firefox och Chrome levererar båda de nödvändiga plattforms-API-mappningarna.
Mobil är ett separat problem. iOS VoiceOver ärver WebKits SVG-brister; Android TalkBack på Chrome hanterar inre noder tillförlitligt men stödjer ännu inte W3C Graphics ARIA-roller, så det faller tillbaka till role=“img”-beteende. För en läroboksförläggare som riktar sig mot både stationär och mobil är det säkra valet att leverera två SVG-lägen: ett strukturellt navigerbart läge för stationär och ett “sammanfattning plus lång beskrivning”-läge som inaktiverar inre navigering på mobil. Lägesbytet drivs av user agent och av användarpreferens, lagrat mellan sessioner.
| NVDA + Firefox | JAWS + Chrome | VoiceOver + Safari | TalkBack + Chrome | |
|---|---|---|---|---|
| SVG title och desc (rot) | OK | OK | OK | OK |
| Inre g med aria-label | OK | OK | Partiellt | OK |
| tabindex på SVG-noder | OK | OK | Partiellt | Misslyckas |
| role=“graphics-document” | OK | OK | Misslyckas | Misslyckas |
| aria-live driven av mutationer | OK | OK | Partiellt | Partiellt |
| focus-visible på SVG-noder | OK | OK | OK | OK |
En tolkning av matrisen: leverera NVDA + Firefox som baseline-konfomitetsmål, dokumentera Safari- och TalkBack-reserverna och använd aldrig frånvaron av en inre nodannons på en Mac som bevis på att SVG:n är otillgänglig. Diagrammet kan vara helt tillgängligt — plattformen exponerar bara inte de etiketter du skrivit. Tillgänglighetsgranskaren i Safaris Develop-meny visar vad som finns i trädet även när VoiceOver misslyckas med att läsa det, och är rätt verktyg för att skilja “trasig kod” från “trasig plattform.”
7. Produktionshandboken
Skapa varje STEM-diagram som SVG, aldrig som rasterformat
PNG och JPG är återvändsgränder. SVG ger dig en DOM, och DOM:en är där varje tillgänglighetsfunktion i den här artikeln bor. Om din produktionspipeline producerar raster (de flesta kemiteckningsverktyg gör fortfarande det), lägg till ett steg som även exporterar SVG, och leverera båda — SVG:n är den tillgängliga artefakten, PNG:n är en reserv för äldre skrivare.
Placera title och desc på varje SVG-rot
Två barn. Title är kortnamnet. Desc är två till fyra meningar som beskriver vad diagrammet visar. Koppla dem med aria-labelledby och aria-describedby på roten. Inga undantag, inte ens för “lilla” diagram — en liten molekyl är fortfarande en molekyl, och en skärmläsaranvändare har samma rätt att höra strukturen som en seende användare har att se den.
Lägg till en synlig expanderbar lång-beskrivningspanel bredvid varje diagram
Tre till tio meningar, i en avslöjningspanel som vilken läsare som helst kan öppna. Löser beskrivningsbehovet för lässvaga och dyslexi-inlärare som SVG:ns desc ensam inte betjänar. Spegla beskrivningstexten i SVG:ns desc för skärmläsaranvändare som inte möter avslöjningen.
Publicera ett JSON-LD CreativeWork med accessibilityFeature
Ett block per sida eller per diagram. Räkna upp varje tillgänglighetsfunktion som diagrammet faktiskt bär. Sökmotorer och konformitetskanners läser detta; inlärare som använder ett CMS som filtrerar för tillgängligt innehåll läser detta. Det är billigt att skriva och ger utdelning det ögonblick någon väljer mellan resurser.
Driv syntolkning för animerade diagram från DOM-mutationer
En MutationObserver per animerad SVG. Annoterade nyckelrutor i animationstidslinjen. En global tom aria-live=“polite”-region vid appstart, monterad innan något diagram renderas. Begränsa till ungefär en annons per 1 500 ms. Respektera prefers-reduced-motion: reduce genom att kollapsa till reservlösningen statisk-plus-lång-beskrivning.
Gör interaktiva diagram tangentbordsnaveringsbara i två granulariteter
Tabba igenom namngivna drag enbart. Piltangenter för finkornig rörelse längs kurvan. Home, End, Page Up, Page Down för gräns- och draghoppning. Uppåt- och nedåtpilar byter serier i diagram med flera serier. Rendera en synlig fokusring på den fokuserade SVG-noden — icke-seende användare behöver den inte, seende skärmläsaranvändare gör det.
Testa på NVDA + Firefox före alla andra kombinationer
Referensplattformen. Om ett mönster misslyckas där är koden fel. Om det fungerar där men misslyckas på Safari är plattformen fel och nästa steg är att dokumentera reservlösningen snarare än att skriva om SVG:n. JAWS + Chrome är det sekundära acceptanstestet. VoiceOver + Safari är nödvändigt för paritet men aldrig tillräckligt.
Slutsats: STEM-tillgänglighet är ett kodproblem med en interaktionsdesignsvans
Det mesta publicerade råd om STEM-diagramtillgänglighet stannar vid title-och-desc-lagret. Det är de enkla 30 procenten. De återstående 70 procenten — den långa beskrivningspanelen, syntolkningstidslinjen driven av DOM-mutationer, tvågradighetstangentbordsnavigeringen, de plattformsspecifika reserverna — är interaktionsdesign lika mycket som det är kod. Skärmläsaren är en användare; en icke-seende inlärare som använder en skärmläsare för att navigera en funktionsgraf i takten hos en seende klasskamrat är en annan användare, med andra behov.
Utdelningen är stor och ojämn. En läroboksförläggare som levererar den fullständiga stacken för ungefär 600 diagram i en kalkyllärobok betjänar varje icke-seende inlärare som använder den läroboken, varje lässvag inlärare som uppskattar avslöjningspanelen, varje dyslexi-inlärare som kan läsa den långa beskrivningen men inte avkoda det visuella, varje inlärare med ett annat förstaspråk som finner den strukturerade beskrivningen lättare än fältets visuella konventioner och varje seende instruktör som producerar ljudsammanfattningar för poddar. Samma kod betjänar fem distinkta målgrupper. Kostnaden är ett par timmar per diagram, amorterad över decennier av studentanvändning.
Det nuvarande läget är ojämnt eftersom tillgänglighetsträds-implementeringarna skiljer sig åt mellan de operativsystem studenter faktiskt använder. NVDA och JAWS på Windows har stängt de flesta SVG-bristerna. Safari på macOS har inte gjort det. Tills plattformarna konvergerar är produktionsmönstret att skapa för det striktaste målet — NVDA + Firefox — och dokumentera reserverna för plattformarna med kända brister. Det är mer arbete än alt-attributmodellen brukade kräva. Det är också det enda sättet att leverera en STEM-lärobok som inte utestänger de läsare den ska undervisa.
”En bensenring är sex kolatomer, sex väteatomer, omväxlande dubbelbindningar, ett delokaliserat pi-system, en plan geometri, en bindningslängd på 1,39 ångström. Alternativtextkonventionen ber om en mening. SVG ställer istället rätt fråga — vilken atom vill du landa på först?”