Merge remote-tracking branch 'lezurex/a11y-improvements' into update/1.10.5

Improve switch and settings tabs accessibility
This commit is contained in:
WardPearce
2025-09-05 09:02:36 +12:00
9 changed files with 155 additions and 79 deletions
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "materialious",
"version": "1.10.3",
"version": "1.10.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "materialious",
"version": "1.10.3",
"version": "1.10.4",
"hasInstallScript": true,
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
@@ -29,11 +29,12 @@
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$synciousStore}
onclick={() => synciousStore.set(!$synciousStore)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -43,11 +43,12 @@
<div class="max">
<p>{$_('layout.deArrow.titleOnly')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$deArrowTitlesOnly}
onclick={() => deArrowTitlesOnly.set(!$deArrowTitlesOnly)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -57,11 +58,12 @@
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$deArrowEnabledStore}
onclick={() => deArrowEnabledStore.set(!$deArrowEnabledStore)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -153,6 +153,7 @@
type="checkbox"
bind:checked={$interfaceAllowInsecureRequests}
onclick={allowInsecureRequests}
role="switch"
/>
<span></span>
</label>
@@ -184,11 +185,12 @@
<div class="max">
<div>{$_('layout.theme.AmoledTheme')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceAmoledTheme}
onclick={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
role="switch"
/>
<span></span>
</label>
@@ -201,7 +203,7 @@
<div class="max">
<div>{$_('layout.searchHistory')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceSearchHistoryEnabled}
@@ -209,6 +211,7 @@
interfaceSearchHistoryEnabled.set(!$interfaceSearchHistoryEnabled);
searchHistoryStore.set([]);
}}
role="switch"
/>
<span></span>
</label>
@@ -220,11 +223,12 @@
<div class="max">
<div>{$_('layout.lowBandwidthMode')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceLowBandwidthMode}
onclick={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
role="switch"
/>
<span></span>
</label>
@@ -236,11 +240,12 @@
<div class="max">
<div>{$_('layout.searchSuggestions')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceSearchSuggestionsStore}
onclick={() => interfaceSearchSuggestionsStore.set(!$interfaceSearchSuggestionsStore)}
role="switch"
/>
<span></span>
</label>
@@ -252,11 +257,12 @@
<div class="max">
<div>{$_('layout.expandDescription')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandDesc}
onclick={() => interfaceAutoExpandDesc.set(!$interfaceAutoExpandDesc)}
role="switch"
/>
<span></span>
</label>
@@ -268,11 +274,12 @@
<div class="max">
<div>{$_('layout.expandChapters')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandChapters}
onclick={() => interfaceAutoExpandChapters.set(!$interfaceAutoExpandChapters)}
role="switch"
/>
<span></span>
</label>
@@ -284,11 +291,12 @@
<div class="max">
<div>{$_('layout.expandComments')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandComments}
onclick={() => interfaceAutoExpandComments.set(!$interfaceAutoExpandComments)}
role="switch"
/>
<span></span>
</label>
@@ -301,11 +309,12 @@
<div class="max">
<div>{$_('layout.disableAutoUpdate')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$interfaceDisableAutoUpdate}
onclick={() => interfaceDisableAutoUpdate.set(!$interfaceDisableAutoUpdate)}
role="switch"
/>
<span></span>
</label>
@@ -141,11 +141,12 @@
<div class="max">
<div>{$_('layout.player.ccByDefault')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerCCByDefault}
onclick={() => playerCCByDefault.set(!$playerCCByDefault)}
role="switch"
/>
<span></span>
</label>
@@ -157,11 +158,12 @@
<div class="max">
<div>{$_('layout.player.autoPlay')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerAutoPlayStore}
onclick={() => playerAutoPlayStore.set(!$playerAutoPlayStore)}
role="switch"
/>
<span></span>
</label>
@@ -173,11 +175,12 @@
<div class="max">
<div>{$_('layout.player.alwaysLoopVideo')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerAlwaysLoopStore}
onclick={() => playerAlwaysLoopStore.set(!$playerAlwaysLoopStore)}
role="switch"
/>
<span></span>
</label>
@@ -190,11 +193,12 @@
<div class="max">
<div>{$_('layout.player.lockOrientation')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerAndroidLockOrientation}
onclick={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
role="switch"
/>
<span></span>
</label>
@@ -208,11 +212,12 @@
<div class="max">
<div>{$_('layout.player.proxyVideos')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerProxyVideosStore}
onclick={() => playerProxyVideosStore.set(!$playerProxyVideosStore)}
role="switch"
/>
<span></span>
</label>
@@ -225,11 +230,12 @@
<div class="max">
<div>{$_('layout.player.savePlaybackPosition')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerSavePlaybackPositionStore}
onclick={() => playerSavePlaybackPositionStore.set(!$playerSavePlaybackPositionStore)}
role="switch"
/>
<span></span>
</label>
@@ -241,11 +247,12 @@
<div class="max">
<div>{$_('layout.player.theatreModeByDefault')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerTheatreModeByDefaultStore}
onclick={() => playerTheatreModeByDefaultStore.set(!$playerTheatreModeByDefaultStore)}
role="switch"
/>
<span></span>
</label>
@@ -257,11 +264,12 @@
<div class="max">
<div>{$_('layout.player.autoPlayNextByDefault')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerAutoplayNextByDefaultStore}
onclick={() => playerAutoplayNextByDefaultStore.set(!$playerAutoplayNextByDefaultStore)}
role="switch"
/>
<span></span>
</label>
@@ -273,11 +281,12 @@
<div class="max">
<div>{$_('layout.player.playerStatistics')}</div>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
bind:checked={$playerStatisticsByDefault}
onclick={() => playerStatisticsByDefault.set(!playerStatisticsByDefault)}
role="switch"
/>
<span></span>
</label>
@@ -29,11 +29,12 @@
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$returnYtDislikesStore}
onclick={() => returnYtDislikesStore.set(!$returnYtDislikesStore)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -10,14 +10,17 @@
import SponsorBlock from './SponsorBlock.svelte';
import { isAndroidTvStore } from '$lib/store';
let activeTab:
| 'interface'
| 'player'
| 'data'
| 'ryd'
| 'api extended'
| 'sponsorblock'
| 'dearrow' = $state('interface');
let activeTab = $state('interface');
const isActive = (id: string) => activeTab === id;
const tabs = [
{ id: 'interface', label: $_('layout.interface'), icon: 'grid_view', component: Interface },
{ id: 'player', label: $_('layout.player.title'), icon: 'smart_display', component: Player },
{ id: 'ryd', label: 'RYD', icon: 'thumb_down', component: Ryd },
{ id: 'api extended', label: 'API Extended', icon: 'sync', component: ApiExtended },
{ id: 'sponsorblock', label: 'Sponsorblock', icon: 'block', component: SponsorBlock },
{ id: 'dearrow', label: $_('layout.deArrow.title'), icon: 'keyboard_double_arrow_down', component: DeArrow },
]
let dialogType = $state('');
@@ -29,10 +32,41 @@
}
}
function onKeydown(e: KeyboardEvent, idx: number) {
const keys = ["ArrowRight", "ArrowLeft", "Home", "End"] as const;
if (!keys.includes(e.key as typeof keys[number])) return;
e.preventDefault();
let next = idx;
switch (e.key) {
case "ArrowRight":
next = (idx + 1) % tabs.length;
break;
case "ArrowLeft":
next = (idx - 1 + tabs.length) % tabs.length;
break;
case "Home":
next = 0;
break;
case "End":
next = tabs.length - 1;
break;
}
activeTab = tabs[next].id;
const els = document.querySelectorAll<HTMLElement>('[role="tab"]');
els[next]?.focus();
}
onMount(() => {
checkWidth();
addEventListener('resize', () => checkWidth());
if ($isAndroidTvStore) {
document.getElementById(`tab-${activeTab}`)?.focus();
}
});
</script>
@@ -43,33 +77,32 @@
</nav>
<div>
<nav tabindex="0" class="tabbed small" style="outline: none" role="region">
<a class:active={activeTab === 'interface'} onclick={() => (activeTab = 'interface')}>
<i>grid_view</i>
<span>{$_('layout.interface')}</span>
</a>
<a class:active={activeTab === 'player'} onclick={() => (activeTab = 'player')}>
<i>smart_display</i>
<span>{$_('layout.player.title')}</span>
</a>
<a tabindex="0" class:active={activeTab === 'ryd'} onclick={() => (activeTab = 'ryd')}>
<i>thumb_down</i>
<span>RYD</span>
</a>
<a class:active={activeTab === 'api extended'} onclick={() => (activeTab = 'api extended')}>
<i>sync</i>
<span>API Extended</span>
</a>
<a class:active={activeTab === 'sponsorblock'} onclick={() => (activeTab = 'sponsorblock')}>
<i>block</i>
<span>Sponsorblock</span>
</a>
<a class:active={activeTab === 'dearrow'} onclick={() => (activeTab = 'dearrow')}>
<i>keyboard_double_arrow_down</i>
<span>{$_('layout.deArrow.title')}</span>
</a>
<nav class="tabbed small" style="outline: none" role="tablist" tabindex="0">
{#each tabs as tab, i}
<a
role="tab"
class:active={isActive(tab.id)}
aria-selected={isActive(tab.id)}
id={`tab-${tab.id}`}
aria-controls={`panel-${tab.id}`}
tabindex={isActive(tab.id) ? 0 : -1}
onclick={() => (activeTab = tab.id)}
onkeydown={(e) => onKeydown(e, i)}
>
<i>{tab.icon}</i>
<span>{tab.label}</span>
</a>
{/each}
{#if !$isAndroidTvStore}
<a class:active={activeTab === 'data'} onclick={() => (activeTab = 'data')}>
<a
role="tab"
class:active={isActive('data')}
aria-selected={isActive('data')}
id="tab-data"
aria-controls="panel-data"
tabindex="0"
onclick={() => (activeTab = 'data')}
>
<i>save</i>
<span>{$_('layout.dataPreferences.dataPreferences')}</span>
</a>
@@ -91,27 +124,32 @@
</a>
{/if}
</nav>
<div class="page padding" class:active={activeTab === 'interface'}>
<Interface />
</div>
<div class="page padding" class:active={activeTab === 'player'}>
<Player />
</div>
<div class="page padding" class:active={activeTab === 'data'}>
{#each tabs as t, _}
<div
class="page padding"
id={`panel-${t.id}`}
role="tabpanel"
aria-labelledby={`tab-${t.id}`}
hidden={!isActive(t.id)}
inert={!$isAndroidTvStore && !isActive(t.id)}
aria-hidden={!isActive(t.id)}
class:active={isActive(t.id)}
>
<t.component />
</div>
{/each}
<div
class="page padding"
id="panel-data"
role="tabpanel"
aria-labelledby="tab-data"
hidden={!isActive('data')}
inert={!$isAndroidTvStore && !isActive('data')}
aria-hidden={!isActive('data')}
class:active={isActive('data')}
>
<DataPreferences />
</div>
<div class="page padding" class:active={activeTab === 'ryd'}>
<Ryd />
</div>
<div class="page padding" class:active={activeTab === 'api extended'}>
<ApiExtended />
</div>
<div class="page padding" class:active={activeTab === 'sponsorblock'}>
<SponsorBlock />
</div>
<div class="page padding" class:active={activeTab === 'dearrow'}>
<DeArrow />
</div>
</div>
</dialog>
@@ -61,11 +61,12 @@
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$sponsorBlockStore}
onclick={() => sponsorBlockStore.set(!$sponsorBlockStore)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -75,11 +76,12 @@
<div class="max">
<p>{$_('layout.sponsors.disableToast')}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
bind:checked={$sponsorBlockDisplayToastStore}
onclick={() => sponsorBlockDisplayToastStore.set(!$sponsorBlockDisplayToastStore)}
type="checkbox"
role="switch"
/>
<span></span>
</label>
@@ -109,11 +111,12 @@
<div class="max">
<p>{sponsor.name}</p>
</div>
<label class="switch" tabindex="0" role="switch">
<label class="switch" tabindex="0">
<input
type="checkbox"
checked={sponsorCategoriesList.includes(sponsor.category)}
onclick={() => toggleSponsor(sponsor.category)}
role="switch"
/>
<span></span>
</label>