Popisek čistě grafického tlačítka pomocí contentDescription (Jetpack Compose)
Tlačítko s jedinou ikonou a bez viditelného textu nemá žádný výchozí přístupný
název. Icon sám o sobě žádný text nenese a jeho contentDescription nemá
žádnou rozumnou výchozí hodnotu — pokud ho necháte null a nic jiného
v tlačítku popisek nedodá, TalkBack ohlásí jen holé "tlačítko", bez
jakéhokoli náznaku, k čemu slouží.
1. Špatně — ikona bez popisku
IconButton(onClick = { onSync() }) {
Icon(Icons.Default.Refresh, contentDescription = null)
}
Vizuálně jasné (ikona aktualizace), ale contentDescription = null tu ikonu
explicitně označuje jako dekorativní a nic jiného v tomhle tlačítku popisek
nedodá. Výsledek: tlačítko bez přístupného jména.
2. Správně — popisek přímo na Icon
IconButton(onClick = { view.clickSound(); showOverflowMenu = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Další možnosti")
}
Modifier.clickable, na kterém IconButton stojí, sám sloučí sémantiku
celého podstromu do jednoho uzlu (mergeDescendants = true) — popisek
nastavený přímo na vnořeném Icon se tak stane přístupným jménem celého
tlačítka. Nic dalšího není potřeba: roli tlačítka i akci kliknutí IconButton
zajišťuje sám.
3. Kde se to používá
V mé aplikaci pro čtení RSS kanálů takhle vypadá naprostá většina ikonových tlačítek v horní liště — tlačítko zpět, tlačítko s dalšími možnostmi, tlačítko pro otevření odkazu v prohlížeči:
IconButton(onClick = { view.clickSound(); onBack() }) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Zpět na domovskou obrazovku")
}
Tlačítko pro synchronizaci je trochu jiný případ — ikona se za běhu mění na
indikátor průběhu, takže popisek nejde pověsit na jeden konkrétní Icon.
Místo toho sedí na Modifier.semantics celého tlačítka a mění se podle
stavu:
IconButton(
onClick = { view.clickSound(); onSync() },
enabled = !isRefreshing,
modifier = Modifier.semantics {
contentDescription = if (isRefreshing) "Synchronizace probíhá" else "Synchronizovat"
}
) {
if (isRefreshing) {
CircularProgressIndicator(modifier = Modifier.size(24.dp))
} else {
Icon(Icons.Default.Refresh, contentDescription = null)
}
}
Icon uvnitř má tady contentDescription = null záměrně a správně —
popisek už dodává obalující IconButton, takže nastavit ho ještě jednou i
na ikonu by bylo jen zbytečné zdvojení.
Proč to takhle je
Modifier.clickableslučuje sémantiku podstromu automaticky — proto stačí popisek nastavit naIcon, nic se nemusí ručně slučovat ani přenášet výš do rodičovského uzlu.IconButtonsám nastavuje roli tlačítka — na rozdíl od vlastního contentDescription u textového tlačítka, kde se muselo použítModifier.clearAndSetSemanticsa roli i akci kliknutí ručně doplnit zpátky, tady se nic nezahazuje —IconButtonroli i akci zajišťuje sám, popisek je jediná věc, kterou je potřeba přidat.contentDescription = nullna ikoně není vždycky chyba — je v pořádku všude tam, kde popisek tlačítka dodává něco jiného (obalujícíIconButtonpřesModifier.semantics, nebo sousední viditelný text u položek menu). Chybou je to jen tehdy, když je ikona jediným obsahem tlačítka a přesto zůstane bez popisku.- Mění-li se popisek podle stavu a mění se přitom i samotná ikona (jako
u synchronizace — ikona se nahradí indikátorem průběhu), je jednodušší dát
popisek na
Modifier.semanticscelého tlačítka než ho řešit na obsahu, který se sám průběžně mění.
Rozdíl oproti vlastnímu contentDescription u textového tlačítka
je v tom, že tam existoval viditelný text, který zněl čtečce nepřirozeně
nebo neuváděl celou potřebnou informaci — proto bylo nutné jeho sémantiku
zahodit pomocí clearAndSetSemantics a nahradit ji ručně, včetně role a
akce kliknutí, protože zahazování smazalo i je. U čistě grafického
tlačítka žádný viditelný text není, takže není co zahazovat — stačí
popisek přidat tam, kam přirozeně patří.
Pravidla
- WCAG 4.1.2 (Název, role, hodnota) — i čistě grafické tlačítko musí mít programově zjistitelné jméno, ne jen vizuální ikonu, kterou vidí pouze uživatel bez zrakového postižení.
Shrnutí: čistě grafické tlačítko (IconButton s Icon a bez viditelného
textu) potřebuje popisek buď přímo na Icon, nebo — mění-li se podle stavu
spolu s ikonou — na Modifier.semantics celého tlačítka. IconButton už
sám slučuje sémantiku podstromu a nastavuje roli, takže na rozdíl od
nahrazování popisku u textového tlačítka tu není potřeba
clearAndSetSemantics.
Komentáře
Zatím zde nejsou žádné komentáře. Buďte první, kdo napíše svůj názor.
Přidat komentář k „Popisek čistě grafického tlačítka pomocí contentDescription (Jetpack Compose)“