fix(#147): WCAG AA color-contrast failures in brand palette #149
@@ -16,3 +16,4 @@ bun.lockb
|
|||||||
# Test artifacts
|
# Test artifacts
|
||||||
/test-results/
|
/test-results/
|
||||||
/e2e/.auth/
|
/e2e/.auth/
|
||||||
|
/coverage/
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ import { test, expect } from '@playwright/test';
|
|||||||
* Automated accessibility checks using axe-core (wcag2a + wcag2aa).
|
* Automated accessibility checks using axe-core (wcag2a + wcag2aa).
|
||||||
* Authenticated pages use the stored admin session from playwright.config.ts.
|
* Authenticated pages use the stored admin session from playwright.config.ts.
|
||||||
* The login page test overrides to an unauthenticated context.
|
* The login page test overrides to an unauthenticated context.
|
||||||
*
|
|
||||||
* Known exclusion:
|
|
||||||
* color-contrast — brand palette (ink-3, text-ink/60) does not meet AA contrast
|
|
||||||
* ratios. Requires a design review with Leonie before fixing. Tracked separately.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const AUTHENTICATED_PAGES = [
|
const AUTHENTICATED_PAGES = [
|
||||||
@@ -18,7 +14,7 @@ const AUTHENTICATED_PAGES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function buildAxe(page: Parameters<typeof AxeBuilder>[0]['page']) {
|
function buildAxe(page: Parameters<typeof AxeBuilder>[0]['page']) {
|
||||||
return new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']).disableRules(['color-contrast']);
|
return new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa']);
|
||||||
}
|
}
|
||||||
|
|
||||||
test.describe('Accessibility — authenticated pages', () => {
|
test.describe('Accessibility — authenticated pages', () => {
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ onMount(async () => {
|
|||||||
{#if showReplyButton && canComment}
|
{#if showReplyButton && canComment}
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<button
|
<button
|
||||||
class="font-sans text-xs font-medium text-accent transition-colors hover:text-ink"
|
class="font-sans text-xs font-medium text-primary transition-colors hover:text-ink-2"
|
||||||
onclick={() => startReply(threadId)}
|
onclick={() => startReply(threadId)}
|
||||||
>
|
>
|
||||||
{m.comment_btn_reply()}
|
{m.comment_btn_reply()}
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ function zoomOut() {
|
|||||||
href={url}
|
href={url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="font-sans text-xs text-accent underline hover:opacity-80"
|
class="font-sans text-xs text-primary underline hover:text-ink-2"
|
||||||
>
|
>
|
||||||
Direkt öffnen
|
Direkt öffnen
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ let {
|
|||||||
{#if canWrite}
|
{#if canWrite}
|
||||||
<a
|
<a
|
||||||
href="/documents/new"
|
href="/documents/new"
|
||||||
class="inline-flex items-center gap-1 text-sm font-medium text-ink/60 transition-colors hover:text-ink"
|
class="inline-flex items-center gap-1 text-sm font-medium text-ink-2 transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Add/Add-General-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Add/Add-General-MD.svg"
|
||||||
@@ -168,7 +168,7 @@ let {
|
|||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onclick={() => goto('/')}
|
onclick={() => goto('/')}
|
||||||
class="mt-6 text-sm font-bold tracking-wide text-accent uppercase transition hover:text-ink"
|
class="mt-6 text-sm font-bold tracking-wide text-primary uppercase transition hover:text-ink-2"
|
||||||
>
|
>
|
||||||
{m.docs_empty_btn_clear()}
|
{m.docs_empty_btn_clear()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ function cancelEditGroup() {
|
|||||||
<div class="flex items-center justify-end gap-3">
|
<div class="flex items-center justify-end gap-3">
|
||||||
<button
|
<button
|
||||||
onclick={() => startEditGroup(group.id)}
|
onclick={() => startEditGroup(group.id)}
|
||||||
class="text-sm font-bold tracking-wide text-accent uppercase hover:text-ink"
|
class="text-sm font-bold tracking-wide text-primary uppercase hover:text-ink-2"
|
||||||
>
|
>
|
||||||
{m.btn_edit()}
|
{m.btn_edit()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ let {
|
|||||||
<div class="flex items-center justify-end gap-4">
|
<div class="flex items-center justify-end gap-4">
|
||||||
<a
|
<a
|
||||||
href="/admin/users/{user.id}"
|
href="/admin/users/{user.id}"
|
||||||
class="text-sm font-bold tracking-wide text-accent uppercase hover:text-ink"
|
class="text-sm font-bold tracking-wide text-primary uppercase hover:text-ink-2"
|
||||||
>
|
>
|
||||||
{m.btn_edit()}
|
{m.btn_edit()}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function swapPersons() {
|
|||||||
<!-- Page Header -->
|
<!-- Page Header -->
|
||||||
<div class="mb-8 border-b border-ink/10 pb-4">
|
<div class="mb-8 border-b border-ink/10 pb-4">
|
||||||
<h1 class="font-serif text-3xl font-medium text-ink">{m.conv_heading()}</h1>
|
<h1 class="font-serif text-3xl font-medium text-ink">{m.conv_heading()}</h1>
|
||||||
<p class="mt-2 font-sans text-sm text-ink/60">
|
<p class="mt-2 font-sans text-sm text-ink-2">
|
||||||
{m.conv_subtitle()}
|
{m.conv_subtitle()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const enrichedDocuments = $derived(
|
|||||||
<a
|
<a
|
||||||
data-testid="conv-new-doc-link"
|
data-testid="conv-new-doc-link"
|
||||||
href="/documents/new?senderId={senderId}&receiverId={receiverId}"
|
href="/documents/new?senderId={senderId}&receiverId={receiverId}"
|
||||||
class="inline-flex items-center gap-1 text-sm font-medium text-ink/60 transition-colors hover:text-ink"
|
class="inline-flex items-center gap-1 text-sm font-medium text-ink-2 transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"
|
||||||
|
|||||||
@@ -73,8 +73,11 @@
|
|||||||
|
|
||||||
--c-ink: #012851;
|
--c-ink: #012851;
|
||||||
--c-ink-2: #4b5563; /* gray-600 — 7.6:1 on white, 6.6:1 on canvas — WCAG AA ✓ */
|
--c-ink-2: #4b5563; /* gray-600 — 7.6:1 on white, 6.6:1 on canvas — WCAG AA ✓ */
|
||||||
--c-ink-3: #6b7280; /* gray-500 — 4.8:1 on white — WCAG AA ✓; use only on surface, not canvas */
|
--c-ink-3: #6b7280; /* gray-500 — 4.8:1 on white, ~4.6:1 on canvas — WCAG AA ✓ */
|
||||||
|
|
||||||
|
/* ⚠ accent — decorative use only (borders, icon tints, bg fills)
|
||||||
|
Light mode: #a1dcd8 on white = 1.52:1 — WCAG FAIL. Never use as text colour.
|
||||||
|
For interactive text labels use text-primary or text-ink instead. */
|
||||||
--c-accent: #a1dcd8;
|
--c-accent: #a1dcd8;
|
||||||
--c-accent-bg: rgba(161, 220, 216, 0.15);
|
--c-accent-bg: rgba(161, 220, 216, 0.15);
|
||||||
|
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ function handleSearch() {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="font-serif text-3xl font-medium text-ink">{m.persons_heading()}</h1>
|
<h1 class="font-serif text-3xl font-medium text-ink">{m.persons_heading()}</h1>
|
||||||
<p class="mt-2 max-w-xl font-sans text-sm text-ink/60">
|
<p class="mt-2 max-w-xl font-sans text-sm text-ink-2">
|
||||||
{m.persons_subtitle()}
|
{m.persons_subtitle()}
|
||||||
</p>
|
</p>
|
||||||
{#if data.canWrite}
|
{#if data.canWrite}
|
||||||
<a
|
<a
|
||||||
href="/persons/new"
|
href="/persons/new"
|
||||||
class="mt-3 inline-flex items-center gap-1 text-sm font-medium text-ink/60 transition-colors hover:text-ink"
|
class="mt-3 inline-flex items-center gap-1 text-sm font-medium text-ink-2 transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Add/Add-General-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Add/Add-General-MD.svg"
|
||||||
|
|||||||
Reference in New Issue
Block a user