Send feedback
Tell us what worked, what didn’t, or what you expected. It goes to the Duct team at Tensflare.
- +diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abd8003..431e408 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,3 +42,16 @@ jobs: with: name: dist path: dist/ + + accessibility: + # Every main screen against WCAG 2.2 A and AA with axe-core, in Chromium (scripts/a11y-electron.cjs). + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run build + - run: npm run a11y diff --git a/.gitignore b/.gitignore index b6ff400..98a6e66 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ ANNOUNCEMENT.md *.DS_Store social-preview.html release/ +a11y-report.json diff --git a/assets/ui/app.css b/assets/ui/app.css index f5f83f7..960764c 100644 --- a/assets/ui/app.css +++ b/assets/ui/app.css @@ -5,7 +5,8 @@ --s3: #1f1f1d; --border: #252522; --border2: #34342f; - --muted: #6a6a65; + --field-border: #6a6a65; /* text fields: 3:1 against every panel (WCAG 1.4.11) */ + --muted: #888883; /* 4.6:1 on the darkest panel: WCAG AA for small text */ --subtle: #9a9a93; --body: #CFCEC6; --text: #F0EFE8; @@ -15,7 +16,7 @@ --lime-mark: #23310a; --warn: #E8A020; --warn-bg: #1E1408; - --danger: #E05555; + --danger: #E85E5E; --danger-bg: #1E0808; --mono: 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace; --sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif; @@ -84,7 +85,7 @@ body.no-mascot .mascot-slot { display: none; } .app { height: 100%; display: flex; flex-direction: column; } .topbar { display: flex; align-items: center; gap: 14px; padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--s1); } .brand { display: flex; align-items: center; gap: 8px; color: var(--text); text-decoration: none; font-family: var(--mono); font-weight: 650; font-size: 17px; letter-spacing: -0.03em; flex-shrink: 0; } -.searchbar { flex: 1; max-width: 760px; margin: 0 auto; display: flex; align-items: center; gap: 8px; background: var(--bg); border: 1px solid var(--border2); border-radius: 12px; padding: 4px 10px 4px 4px; transition: border-color .15s; } +.searchbar { flex: 1; max-width: 760px; margin: 0 auto; display: flex; align-items: center; gap: 8px; background: var(--bg); border: 1px solid var(--field-border); border-radius: 12px; padding: 4px 10px 4px 4px; transition: border-color .15s; } .searchbar:focus-within { border-color: var(--lime); } .searchbar input { flex: 1; min-width: 0; background: transparent; border: 0; outline: none; color: var(--text); font-size: 15px; padding: 8px 4px; } .searchbar input::-webkit-search-cancel-button { filter: invert(.6); } @@ -435,3 +436,22 @@ dialog::backdrop { background: rgba(0,0,0,.55); } .visibility-row select, .visibility-row input { background: var(--bg); color: var(--text); border: 1px solid var(--border2); border-radius: var(--r); padding: 5px 8px; font: inherit; font-size: 12.5px; } .visibility-row input { flex: 1; min-width: 180px; } .visibility-row small { flex-basis: 100%; } + +/* Read by screen readers, not shown. */ +.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } + +/* Fields are outlined so they can be found (WCAG 1.4.11), and show where the keyboard is (2.4.7). */ +input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea { border-color: var(--field-border); } +input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--lime) !important; outline-offset: 1px; } +.searchbar input:focus-visible { outline: none !important; } /* the bar around it lights up instead */ + +.skip-link { position: absolute; left: 12px; top: -48px; z-index: 100; background: var(--lime); color: #0C0C0B; font-weight: 600; padding: 8px 14px; border-radius: var(--r); text-decoration: none; } +.skip-link:focus { top: 12px; } + +/* Zoomed in or narrow (WCAG 1.4.10: no sideways scrolling at 320 CSS pixels). */ +.result-actions { flex-wrap: wrap; } +@media (max-width: 480px) { + .searchbar { min-width: 0; padding-left: 2px; } + .mode-switch .labs { display: none; } + .mode-switch .mode { padding-left: 8px; padding-right: 8px; } +} diff --git a/assets/ui/app.js b/assets/ui/app.js index 46d51d0..9589486 100644 --- a/assets/ui/app.js +++ b/assets/ui/app.js @@ -263,11 +263,17 @@ function renderFeatureList(prefs, shortcut) { let html = FEATURE_GROUPS.map(g => '
Switched-off types are skipped when indexing and hidden from search. Turning one back on rescans watched folders.
' html += Object.keys(KIND_LABELS).map(kind => check('data-format-kind="' + kind + '"', kindOn(kind), KIND_LABELS[kind], '', !admin)).join('') + // Single-key shortcuts can be turned off (WCAG 2.1.4): speech input can type "/" by accident. + html += 'Tell us what worked, what didn’t, or what you expected. It goes to the Duct team at Tensflare.
- +