fix: accessibility improvements and test infrastructure hardening

Add aria-labels to form controls (selects, checkboxes, switches), set
html lang attribute and page title, fix color contrast for --candle-dim
and --text-faint tokens, underline inline links, remove opacity hack.
Harden dev login endpoints with atomic findOneAndUpdate and tokenVersion
in JWT. Update Playwright timeouts and E2E test helpers.
This commit is contained in:
Jennie Robinson Faber 2026-04-05 21:59:02 +01:00
parent 61c16d8bac
commit c40f2c7c63
35 changed files with 787 additions and 173 deletions

View file

@ -20,13 +20,13 @@
--border: #b8a880;
--border-d: #a89470;
--candle: #7a5a10;
--candle-dim: #9a7420;
--candle-dim: #866518;
--candle-faint: #c4a448;
--ember: #8a4420;
--text: #2a2015;
--text-bright: #1a1008;
--text-dim: #5a5040;
--text-faint: #8a7e6a;
--text-faint: #746a58;
--parch: #2a2015;
--parch-hover: #3a3025;
--parch-text: #ede4d0;
@ -89,6 +89,7 @@ body {
a { color: var(--candle); text-decoration: none; }
a:hover { text-decoration: underline; }
p a, blockquote a { text-decoration: underline; text-underline-offset: 2px; }
/* ---- SECTION LABELS ---- */
.section-label {

View file

@ -106,21 +106,13 @@
<script setup>
const route = useRoute()
const isMobileMenuOpen = ref(false)
const { logout } = useAuth()
const currentPageName = computed(() => {
const path = route.path
if (path === '/admin') return 'admin'
return path.slice(1).replace(/\//g, ' / ')
})
const logout = async () => {
try {
await $fetch('/api/auth/logout', { method: 'POST' })
await navigateTo('/')
} catch (error) {
console.error('Logout failed:', error)
}
}
</script>
<style scoped>

View file

@ -102,6 +102,7 @@
</label>
<USelect
v-model="eventForm.eventType"
aria-label="Event type"
:items="[
{ label: 'Community Meetup', value: 'community' },
{ label: 'Workshop', value: 'workshop' },
@ -386,6 +387,7 @@
<div class="series-select-row">
<USelect
v-model="selectedSeriesId"
aria-label="Select series"
@update:model-value="onSeriesSelect"
:items="
availableSeries.map((series) => ({

View file

@ -30,7 +30,7 @@
/>
</div>
<div class="field" style="margin-bottom: 0;">
<select v-model="circleFilter">
<select v-model="circleFilter" aria-label="Filter by circle">
<option value="">All Circles</option>
<option value="community">Community</option>
<option value="founder">Founder</option>
@ -55,6 +55,7 @@
<tr>
<th class="col-check">
<UCheckbox
aria-label="Select all members"
:model-value="allVisibleSelected ? true : (someVisibleSelected ? 'indeterminate' : false)"
@update:model-value="toggleSelectAll"
/>
@ -73,6 +74,7 @@
<tr v-for="member in filteredMembers" :key="member._id">
<td class="col-check">
<UCheckbox
:aria-label="`Select ${member.name}`"
:model-value="selectedMemberIds.includes(member._id)"
@update:model-value="toggleSelect(member._id)"
/>

View file

@ -76,7 +76,7 @@
<!-- PARCHMENT INSET -->
<ParchmentInset>
<div class="label" style="color: var(--candle-faint); opacity: 0.6; margin-bottom: 12px;">From the Wiki</div>
<div class="label" style="color: var(--candle-faint); margin-bottom: 12px;">From the Wiki</div>
<h2>What is a cooperative studio?</h2>
<p>A cooperative studio is a game development company owned and governed by the people who work there. Decisions are made collectively. Profits are shared according to contribution, not ownership stake.</p>
<p>The games industry is full of stories about crunch, layoffs, and studios that extract value from workers. Cooperatives are one alternative not the only one, but one worth <a href="/wiki">practicing together</a>.</p>

View file

@ -145,7 +145,7 @@
<div class="section-label">Visibility</div>
<div class="toggle-field">
<USwitch v-model="formData.showInDirectory" />
<USwitch v-model="formData.showInDirectory" aria-label="Show in Member Directory" />
<div class="toggle-label">
Show in Member Directory
<span class="toggle-sub">Your profile will appear in the public member listing</span>
@ -162,7 +162,7 @@
<div class="section-label">Peer Support</div>
<div class="toggle-field">
<USwitch v-model="formData.peerSupportEnabled" />
<USwitch v-model="formData.peerSupportEnabled" aria-label="Offer Peer Support" />
<div class="toggle-label">
Offer Peer Support
<span class="toggle-sub">Let other members request 1:1 time with you</span>
@ -223,7 +223,7 @@
<div class="section-label">Notifications</div>
<div class="toggle-field">
<USwitch v-model="formData.notifyEvents" />
<USwitch v-model="formData.notifyEvents" aria-label="Event reminders" />
<div class="toggle-label">
Event reminders
<span class="toggle-sub">Get notified about upcoming events</span>
@ -231,7 +231,7 @@
</div>
<div class="toggle-field">
<USwitch v-model="formData.notifyUpdates" />
<USwitch v-model="formData.notifyUpdates" aria-label="Community updates" />
<div class="toggle-label">
Community updates
<span class="toggle-sub">New posts from members you follow</span>
@ -239,7 +239,7 @@
</div>
<div class="toggle-field">
<USwitch v-model="formData.notifyPeerRequests" />
<USwitch v-model="formData.notifyPeerRequests" aria-label="Peer support requests" />
<div class="toggle-label">
Peer support requests
<span class="toggle-sub">When someone wants to connect</span>