Skip to content

Commit 6e62308

Browse files
committed
A11y: Fix serious, moderate, and minor violations
- Clean slate. - Also enabled failing on minor issues in the checker script.
1 parent 995f8c8 commit 6e62308

File tree

11 files changed

+88
-77
lines changed

11 files changed

+88
-77
lines changed

apps/desktop/src/lib/search/AiSearchRow.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
}
116116
117117
.ai-label {
118-
color: var(--color-accent);
118+
color: var(--color-cmdr-gold);
119119
}
120120
121121
.name-input {

apps/desktop/src/lib/settings/components/SettingsContent.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import AdvancedSection from '$lib/settings/sections/AdvancedSection.svelte'
1313
import LicenseSection from '$lib/settings/sections/LicenseSection.svelte'
1414
import AiSection from '$lib/settings/sections/AiSection.svelte'
15+
import ViewerSection from '$lib/settings/sections/ViewerSection.svelte'
1516
import SectionSummary from './SectionSummary.svelte'
1617
import { getMatchingSettingIdsInSection } from '$lib/settings/settings-search'
1718
import { searchCommands } from '$lib/commands/fuzzy-search'
@@ -109,6 +110,12 @@
109110
</section>
110111
{/if}
111112

113+
{#if shouldShowSection(['General', 'Viewer'])}
114+
<section data-section-id="general-viewer">
115+
<ViewerSection {searchQuery} />
116+
</section>
117+
{/if}
118+
112119
<!-- Network sections -->
113120
{#if shouldShowSection(['Network', 'SMB/Network shares'])}
114121
<section data-section-id="network-smb-network-shares">
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script lang="ts">
2+
import SettingsSection from '../components/SettingsSection.svelte'
3+
import SettingRow from '../components/SettingRow.svelte'
4+
import SettingSwitch from '../components/SettingSwitch.svelte'
5+
import { getSettingDefinition } from '$lib/settings'
6+
import { createShouldShow } from '$lib/settings/settings-search'
7+
8+
interface Props {
9+
searchQuery: string
10+
}
11+
12+
const { searchQuery }: Props = $props()
13+
14+
const shouldShow = $derived(createShouldShow(searchQuery))
15+
16+
const wordWrapDef = getSettingDefinition('viewer.wordWrap') ?? { label: '', description: '' }
17+
</script>
18+
19+
<SettingsSection title="Viewer">
20+
{#if shouldShow('viewer.wordWrap')}
21+
<SettingRow
22+
id="viewer.wordWrap"
23+
label={wordWrapDef.label}
24+
description={wordWrapDef.description}
25+
{searchQuery}
26+
>
27+
<SettingSwitch id="viewer.wordWrap" />
28+
</SettingRow>
29+
{/if}
30+
</SettingsSection>

apps/desktop/test/e2e-playwright/accessibility.spec.ts

Lines changed: 26 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Accessibility audit for Cmdr views and dialogs using axe-core.
33
*
44
* Injects axe-core into the real Tauri webview via tauriPage.evaluate(),
5-
* runs a WCAG audit on each view/dialog, and fails on critical violations.
5+
* runs a WCAG audit on each view/dialog, and fails on any violation.
66
*
77
* Dialog tests scope the audit to the dialog element itself, avoiding noise
88
* from the page behind the overlay.
@@ -139,9 +139,8 @@ test('main explorer view', async ({ tauriPage }) => {
139139
test.setTimeout(120_000)
140140
await ensureAppReady(tauriPage)
141141

142-
const { critical, serious } = await runAxeAudit(tauriPage, 'Main explorer')
143-
expect(critical, `Found ${critical.length} critical violation(s) in main explorer`).toHaveLength(0)
144-
expect(serious, `Found ${serious.length} serious violation(s) in main explorer`).toHaveLength(0)
142+
const { all } = await runAxeAudit(tauriPage, 'Main explorer')
143+
expect(all, `Found ${all.length} violation(s) in main explorer`).toHaveLength(0)
145144
})
146145

147146
test('Copy dialog', async ({ tauriPage }) => {
@@ -152,10 +151,9 @@ test('Copy dialog', async ({ tauriPage }) => {
152151
await tauriPage.keyboard.press('F5')
153152
await tauriPage.waitForSelector(TRANSFER_DIALOG, 5000)
154153

155-
const { critical, serious } = await runAxeAudit(tauriPage, 'Copy dialog', TRANSFER_DIALOG)
154+
const { all } = await runAxeAudit(tauriPage, 'Copy dialog', TRANSFER_DIALOG)
156155
await dismissDialog(tauriPage)
157-
expect(critical, `Found ${critical.length} critical violation(s) in Copy dialog`).toHaveLength(0)
158-
expect(serious, `Found ${serious.length} serious violation(s) in Copy dialog`).toHaveLength(0)
156+
expect(all, `Found ${all.length} violation(s) in Copy dialog`).toHaveLength(0)
159157
})
160158

161159
test('Delete dialog', async ({ tauriPage }) => {
@@ -167,10 +165,9 @@ test('Delete dialog', async ({ tauriPage }) => {
167165
const deleteDialog = '[data-dialog-id="delete-confirmation"]'
168166
await tauriPage.waitForSelector(deleteDialog, 5000)
169167

170-
const { critical, serious } = await runAxeAudit(tauriPage, 'Delete dialog', deleteDialog)
168+
const { all } = await runAxeAudit(tauriPage, 'Delete dialog', deleteDialog)
171169
await dismissDialog(tauriPage)
172-
expect(critical, `Found ${critical.length} critical violation(s) in Delete dialog`).toHaveLength(0)
173-
expect(serious, `Found ${serious.length} serious violation(s) in Delete dialog`).toHaveLength(0)
170+
expect(all, `Found ${all.length} violation(s) in Delete dialog`).toHaveLength(0)
174171
})
175172

176173
test('Move dialog', async ({ tauriPage }) => {
@@ -181,10 +178,9 @@ test('Move dialog', async ({ tauriPage }) => {
181178
await tauriPage.keyboard.press('F6')
182179
await tauriPage.waitForSelector(TRANSFER_DIALOG, 5000)
183180

184-
const { critical, serious } = await runAxeAudit(tauriPage, 'Move dialog', TRANSFER_DIALOG)
181+
const { all } = await runAxeAudit(tauriPage, 'Move dialog', TRANSFER_DIALOG)
185182
await dismissDialog(tauriPage)
186-
expect(critical, `Found ${critical.length} critical violation(s) in Move dialog`).toHaveLength(0)
187-
expect(serious, `Found ${serious.length} serious violation(s) in Move dialog`).toHaveLength(0)
183+
expect(all, `Found ${all.length} violation(s) in Move dialog`).toHaveLength(0)
188184
})
189185

190186
test('About dialog', async ({ tauriPage }) => {
@@ -194,10 +190,9 @@ test('About dialog', async ({ tauriPage }) => {
194190
await executeViaCommandPalette(tauriPage, 'About Cmdr')
195191
await tauriPage.waitForSelector('[data-dialog-id="about"]', 5000)
196192

197-
const { critical, serious } = await runAxeAudit(tauriPage, 'About dialog', '[data-dialog-id="about"]')
193+
const { all } = await runAxeAudit(tauriPage, 'About dialog', '[data-dialog-id="about"]')
198194
await dismissDialog(tauriPage)
199-
expect(critical, `Found ${critical.length} critical violation(s) in About dialog`).toHaveLength(0)
200-
expect(serious, `Found ${serious.length} serious violation(s) in About dialog`).toHaveLength(0)
195+
expect(all, `Found ${all.length} violation(s) in About dialog`).toHaveLength(0)
201196
})
202197

203198
test('License dialog', async ({ tauriPage }) => {
@@ -207,10 +202,9 @@ test('License dialog', async ({ tauriPage }) => {
207202
await executeViaCommandPalette(tauriPage, 'license')
208203
await tauriPage.waitForSelector('[data-dialog-id="license"]', 5000)
209204

210-
const { critical, serious } = await runAxeAudit(tauriPage, 'License dialog', '[data-dialog-id="license"]')
205+
const { all } = await runAxeAudit(tauriPage, 'License dialog', '[data-dialog-id="license"]')
211206
await dismissDialog(tauriPage)
212-
expect(critical, `Found ${critical.length} critical violation(s) in License dialog`).toHaveLength(0)
213-
expect(serious, `Found ${serious.length} serious violation(s) in License dialog`).toHaveLength(0)
207+
expect(all, `Found ${all.length} violation(s) in License dialog`).toHaveLength(0)
214208
})
215209

216210
test('Command palette', async ({ tauriPage }) => {
@@ -219,14 +213,13 @@ test('Command palette', async ({ tauriPage }) => {
219213

220214
await openCommandPalette(tauriPage)
221215

222-
const { critical, serious } = await runAxeAudit(tauriPage, 'Command palette', '.palette-overlay')
216+
const { all } = await runAxeAudit(tauriPage, 'Command palette', '.palette-overlay')
223217

224218
// Dismiss the palette
225219
await tauriPage.keyboard.press('Escape')
226220
await pollUntil(tauriPage, async () => !(await tauriPage.isVisible('.palette-overlay')), 3000)
227221

228-
expect(critical, `Found ${critical.length} critical violation(s) in command palette`).toHaveLength(0)
229-
expect(serious, `Found ${serious.length} serious violation(s) in command palette`).toHaveLength(0)
222+
expect(all, `Found ${all.length} violation(s) in command palette`).toHaveLength(0)
230223
})
231224

232225
test('Search dialog', async ({ tauriPage }) => {
@@ -235,14 +228,13 @@ test('Search dialog', async ({ tauriPage }) => {
235228

236229
await openSearchDialog(tauriPage)
237230

238-
const { critical, serious } = await runAxeAudit(tauriPage, 'Search dialog', '.search-overlay')
231+
const { all } = await runAxeAudit(tauriPage, 'Search dialog', '.search-overlay')
239232

240233
// Dismiss the search dialog
241234
await tauriPage.keyboard.press('Escape')
242235
await pollUntil(tauriPage, async () => !(await tauriPage.isVisible('.search-overlay')), 3000)
243236

244-
expect(critical, `Found ${critical.length} critical violation(s) in search dialog`).toHaveLength(0)
245-
expect(serious, `Found ${serious.length} serious violation(s) in search dialog`).toHaveLength(0)
237+
expect(all, `Found ${all.length} violation(s) in search dialog`).toHaveLength(0)
246238
})
247239

248240
test('Settings: all sections', async ({ tauriPage }) => {
@@ -276,8 +268,7 @@ test('Settings: all sections', async ({ tauriPage }) => {
276268
{ name: 'Advanced', path: ['Advanced'], sectionId: 'advanced' },
277269
]
278270

279-
const allCritical: { section: string; violations: AxeViolation[] }[] = []
280-
const allSerious: { section: string; violations: AxeViolation[] }[] = []
271+
const allViolations: { section: string; violations: AxeViolation[] }[] = []
281272

282273
for (const section of sections) {
283274
// Click sidebar to navigate to the section
@@ -301,22 +292,15 @@ test('Settings: all sections', async ({ tauriPage }) => {
301292
continue
302293
}
303294

304-
const { critical, serious } = await runAxeAudit(tauriPage, `Settings: ${section.name}`)
305-
if (critical.length > 0) {
306-
allCritical.push({ section: section.name, violations: critical })
307-
}
308-
if (serious.length > 0) {
309-
allSerious.push({ section: section.name, violations: serious })
295+
const { all } = await runAxeAudit(tauriPage, `Settings: ${section.name}`)
296+
if (all.length > 0) {
297+
allViolations.push({ section: section.name, violations: all })
310298
}
311299
}
312300

313-
const totalCritical = allCritical.reduce((sum, s) => sum + s.violations.length, 0)
314-
const failedSections = allCritical.map((s) => `${s.section} (${s.violations.length})`).join(', ')
315-
expect(totalCritical, `Critical violations in settings: ${failedSections}`).toBe(0)
316-
317-
const totalSerious = allSerious.reduce((sum, s) => sum + s.violations.length, 0)
318-
const failedSeriousSections = allSerious.map((s) => `${s.section} (${s.violations.length})`).join(', ')
319-
expect(totalSerious, `Serious violations in settings: ${failedSeriousSections}`).toBe(0)
301+
const totalViolations = allViolations.reduce((sum, s) => sum + s.violations.length, 0)
302+
const failedSections = allViolations.map((s) => `${s.section} (${s.violations.length})`).join(', ')
303+
expect(totalViolations, `Violations in settings: ${failedSections}`).toBe(0)
320304
})
321305

322306
test('File viewer with text file', async ({ tauriPage }) => {
@@ -329,7 +313,6 @@ test('File viewer with text file', async ({ tauriPage }) => {
329313
await tauriPage.waitForSelector('.viewer-container', 15000)
330314
await tauriPage.waitForSelector('.file-content', 10000)
331315

332-
const { critical, serious } = await runAxeAudit(tauriPage, 'File viewer')
333-
expect(critical, `Found ${critical.length} critical violation(s) in file viewer`).toHaveLength(0)
334-
expect(serious, `Found ${serious.length} serious violation(s) in file viewer`).toHaveLength(0)
316+
const { all } = await runAxeAudit(tauriPage, 'File viewer')
317+
expect(all, `Found ${all.length} violation(s) in file viewer`).toHaveLength(0)
335318
})

apps/website/e2e/accessibility.spec.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ for (const theme of ['light', 'dark'] as const) {
3939

4040
const results = await new AxeBuilder({ page }).analyze()
4141

42-
const critical = results.violations.filter((v) => v.impact === 'critical')
43-
const serious = results.violations.filter((v) => v.impact === 'serious')
44-
4542
// Log violations for visibility
4643
for (const v of results.violations) {
4744
// eslint-disable-next-line no-console
@@ -58,20 +55,14 @@ for (const theme of ['light', 'dark'] as const) {
5855
}
5956

6057
if (results.violations.length > 0) {
61-
const counts = [
62-
critical.length && `${critical.length} critical`,
63-
serious.length && `${serious.length} serious`,
64-
]
65-
.filter(Boolean)
66-
.join(', ')
67-
if (counts) {
68-
// eslint-disable-next-line no-console
69-
console.log(`⚠ [${name} ${theme}] ${counts} violation(s)`)
70-
}
58+
// eslint-disable-next-line no-console
59+
console.log(`⚠ [${name} ${theme}] ${results.violations.length} violation(s)`)
7160
}
7261

73-
expect(critical, `${name} (${theme}): ${critical.length} critical violation(s)`).toHaveLength(0)
74-
expect(serious, `${name} (${theme}): ${serious.length} serious violation(s)`).toHaveLength(0)
62+
expect(
63+
results.violations,
64+
`${name} (${theme}): ${results.violations.length} violation(s)`,
65+
).toHaveLength(0)
7566
})
7667
}
7768
})

apps/website/src/components/Download.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import NewsletterInlineWrapper from './NewsletterInlineWrapper.astro'
4040
<p>
4141
Free for personal use · <a
4242
href="/pricing"
43-
class="text-[var(--color-accent)] underline underline-offset-2 hover:text-[var(--color-accent-hover)]"
43+
class="text-[var(--color-accent-text)] underline underline-offset-2 hover:text-[var(--color-accent-hover)]"
4444
>Commercial from $59/yr</a
4545
>
4646
</p>

apps/website/src/pages/changelog.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const htmlContent = await marked.parse(changelogContent)
2626
href="https://keepachangelog.com/en/1.1.0/"
2727
target="_blank"
2828
rel="noopener noreferrer"
29-
class="text-[var(--color-accent)] underline underline-offset-2 hover:text-[var(--color-accent-hover)]"
29+
class="text-[var(--color-accent-text)] underline underline-offset-2 hover:text-[var(--color-accent-hover)]"
3030
>keep a changelog</a
3131
>.
3232
</p>

apps/website/src/pages/features.astro

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
3131
>
3232
<div class="mb-1 flex items-center gap-3">
3333
<img src="/icons/database.svg" alt="" class="size-8" />
34-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Live full-disk index</h3>
34+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Live full-disk index</h2>
3535
</div>
3636
<div class="mt-4 space-y-3 text-[var(--color-text-secondary)]">
3737
<p>
@@ -58,7 +58,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
5858
>
5959
<div class="mb-1 flex items-center gap-3">
6060
<img src="/icons/sparkles.svg" alt="" class="size-8" />
61-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Private LLM included</h3>
61+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Private LLM included</h2>
6262
</div>
6363
<div class="mt-4 space-y-3 text-[var(--color-text-secondary)]">
6464
<p>
@@ -82,7 +82,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
8282
>
8383
<div class="mb-1 flex items-center gap-3">
8484
<img src="/icons/rocket.svg" alt="" class="size-8" />
85-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Blazing fast</h3>
85+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Blazing fast</h2>
8686
</div>
8787
<div class="mt-4 space-y-3 text-[var(--color-text-secondary)]">
8888
<p>
@@ -108,7 +108,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
108108
>
109109
<div class="mb-1 flex items-center gap-3">
110110
<img src="/icons/keyboard.svg" alt="" class="size-8" />
111-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Keyboard-first</h3>
111+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Keyboard-first</h2>
112112
</div>
113113
<div class="mt-4 space-y-3 text-[var(--color-text-secondary)]">
114114
<p>
@@ -143,7 +143,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
143143
<div class="mb-1 flex items-center justify-between">
144144
<div class="flex items-center gap-3">
145145
<img src="/icons/search.svg" alt="" class="size-8" />
146-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Smart search</h3>
146+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Smart search</h2>
147147
</div>
148148
<a
149149
href="/roadmap"
@@ -179,7 +179,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
179179
<div class="mb-1 flex items-center justify-between">
180180
<div class="flex items-center gap-3">
181181
<img src="/icons/brain.svg" alt="" class="size-8" />
182-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">Natural language rename</h3>
182+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">Natural language rename</h2>
183183
</div>
184184
<a
185185
href="/roadmap"
@@ -208,7 +208,7 @@ import NewsletterInlineWrapper from '../components/NewsletterInlineWrapper.astro
208208
<div class="mb-1 flex items-center justify-between">
209209
<div class="flex items-center gap-3">
210210
<img src="/icons/zap.svg" alt="" class="size-8" />
211-
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]">AI batch operations</h3>
211+
<h2 class="text-xl font-semibold text-[var(--color-text-primary)]">AI batch operations</h2>
212212
</div>
213213
<a
214214
href="/roadmap"

apps/website/src/pages/pricing.astro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ const faq = [
7272
<!-- Personal (Free) -->
7373
<div class="flex flex-col rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] p-6">
7474
<div class="mb-4">
75-
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Personal</h3>
75+
<h2 class="text-lg font-semibold text-[var(--color-text-primary)]">Personal</h2>
7676
<p class="text-sm text-[var(--color-text-tertiary)]">For hobbyists and side projects</p>
7777
</div>
7878
<div class="mb-6">
@@ -110,7 +110,7 @@ const faq = [
110110
Most popular
111111
</div>
112112
<div class="mb-4">
113-
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Commercial</h3>
113+
<h2 class="text-lg font-semibold text-[var(--color-text-primary)]">Commercial</h2>
114114
<p class="text-sm text-[var(--color-text-tertiary)]">For work use</p>
115115
</div>
116116
<div class="mb-6">
@@ -150,7 +150,7 @@ const faq = [
150150
<!-- Commercial Perpetual -->
151151
<div class="flex flex-col rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] p-6">
152152
<div class="mb-4">
153-
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Perpetual</h3>
153+
<h2 class="text-lg font-semibold text-[var(--color-text-primary)]">Perpetual</h2>
154154
<p class="text-sm text-[var(--color-text-tertiary)]">Buy once, use forever</p>
155155
</div>
156156
<div class="mb-6">

0 commit comments

Comments
 (0)