From 82d6628af1c81224458ad5da6decba9a98cf1a32 Mon Sep 17 00:00:00 2001 From: retoor Date: Tue, 8 Sep 2026 20:48:30 +0200 Subject: [PATCH] Fix low-contrast links, inline code, and warning badges on dark surfaces Two independent contrast bugs, both from a token being used somewhere it was never validated as a text color: - Links/inline code used --accent as their text color against dark card surfaces, measuring 2.8-3.6:1 (below the 4.5:1 AA threshold for normal text) depending on the surface. Worst case: the sender's own DM chat bubble uses --accent as its *background*, so a link inside your own message was the exact same color as the bubble - 1.0:1, functionally invisible. Added --link/--link-hover (same hue/ saturation as --accent, lightened until they clear 4.5:1 against the darkest realistic surface) and repointed the sitewide `a`/`a:hover` default, `.rendered-content a`/`code`, `.docs-chat-bubble a`, and `.ds-chat-citations a`. The DM chat bubble additionally needed bubble-scoped overrides (link, AI-adjusted note, attachment-pending placeholder, failed-send outline/retry-hint) since its background *is* --accent, which --link alone doesn't fix. - `background: var(--warning); color: var(--white)` (chat's connection banner, a container badge, a service-status badge, and docs' PUT/4xx badges) measured ~1.7:1 - yellow needs dark text. Added --on-warning mirroring the existing --on-accent pattern and repointed just the warning-backed variants, leaving sibling info/success/danger badges untouched (they have the same underlying issue but are out of scope for this pass). --accent itself is untouched, so every button/badge/nav element using it as a solid fill is unchanged. Verified with computed WCAG contrast ratios and Playwright screenshots against a live render of the real CSS, plus the production homepage to confirm no regression to brand colors. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Vnp7vzE4hvsytMo5YjszJm --- devplacepy/static/css/base.css | 4 ++-- devplacepy/static/css/chat.css | 28 ++++++++++++++++++++++++++-- devplacepy/static/css/containers.css | 2 +- devplacepy/static/css/deepsearch.css | 2 +- devplacepy/static/css/docs-chat.css | 2 +- devplacepy/static/css/docs.css | 4 ++-- devplacepy/static/css/markdown.css | 6 +++--- devplacepy/static/css/services.css | 2 +- devplacepy/static/css/variables.css | 4 ++++ pyproject.toml | 2 +- 10 files changed, 42 insertions(+), 14 deletions(-) diff --git a/devplacepy/static/css/base.css b/devplacepy/static/css/base.css index f318157..327f5ca 100644 --- a/devplacepy/static/css/base.css +++ b/devplacepy/static/css/base.css @@ -377,12 +377,12 @@ body { } a { - color: var(--accent); + color: var(--link); text-decoration: none; } a:hover { - color: var(--accent-hover); + color: var(--link-hover); } button { diff --git a/devplacepy/static/css/chat.css b/devplacepy/static/css/chat.css index 577c652..50e09d2 100644 --- a/devplacepy/static/css/chat.css +++ b/devplacepy/static/css/chat.css @@ -178,7 +178,7 @@ dp-chat[mode="embed"] { flex-shrink: 0; padding: var(--space-xs) var(--space-lg); background: var(--warning); - color: var(--white); + color: var(--on-warning); font-size: 0.75rem; font-weight: 600; text-align: center; @@ -283,6 +283,14 @@ dp-chat[mode="embed"] { border-bottom-left-radius: 4px; } +.message-bubble.mine .rendered-content a { + color: var(--on-accent); +} + +.message-bubble.mine .rendered-content a:hover { + opacity: 0.8; +} + .message-bubble.pending { opacity: 0.6; } @@ -292,6 +300,10 @@ dp-chat[mode="embed"] { cursor: pointer; } +.message-bubble.mine.failed { + outline-color: var(--on-accent); +} + .retry-hint { display: block; margin-top: 0.25rem; @@ -299,6 +311,10 @@ dp-chat[mode="embed"] { color: var(--danger); } +.message-bubble.mine .retry-hint { + color: var(--on-accent); +} + .message-bubble.mine .content-copy-btn, .message-bubble.theirs .content-copy-btn { opacity: 0; @@ -328,7 +344,7 @@ dp-chat[mode="embed"] { } .message-bubble.mine .message-time { - color: rgba(255, 255, 255, 0.7); + color: rgba(255, 255, 255, 0.85); } .message-receipt { @@ -355,6 +371,10 @@ dp-chat[mode="embed"] { opacity: 0; } +.message-bubble.mine .ai-adjusted-note { + color: rgba(255, 255, 255, 0.85); +} + .attachment-pending { margin-top: 0.35rem; font-size: 0.8rem; @@ -362,6 +382,10 @@ dp-chat[mode="embed"] { color: var(--text-secondary); } +.message-bubble.mine .attachment-pending { + color: rgba(255, 255, 255, 0.85); +} + .date-separator { position: sticky; top: 0; diff --git a/devplacepy/static/css/containers.css b/devplacepy/static/css/containers.css index 5b45468..9183d55 100644 --- a/devplacepy/static/css/containers.css +++ b/devplacepy/static/css/containers.css @@ -14,7 +14,7 @@ } .cm-badge.cm-running { background: var(--success); color: var(--white); } .cm-badge.cm-crashed { background: var(--danger); color: var(--white); } -.cm-badge.cm-paused { background: var(--warning); color: var(--white); } +.cm-badge.cm-paused { background: var(--warning); color: var(--on-warning); } .cm-badge.cm-stopped { background: var(--text-muted); color: var(--white); } .cm-badge.cm-removed { background: var(--text-muted); color: var(--white); } .cm-badge.cm-created, diff --git a/devplacepy/static/css/deepsearch.css b/devplacepy/static/css/deepsearch.css index b9eaf7a..348de7c 100644 --- a/devplacepy/static/css/deepsearch.css +++ b/devplacepy/static/css/deepsearch.css @@ -592,7 +592,7 @@ dp-deepsearch-chat { } .ds-chat-citations a { - color: var(--accent); + color: var(--link); } .ds-chat-status { diff --git a/devplacepy/static/css/docs-chat.css b/devplacepy/static/css/docs-chat.css index 1ced925..199aef8 100644 --- a/devplacepy/static/css/docs-chat.css +++ b/devplacepy/static/css/docs-chat.css @@ -91,7 +91,7 @@ dp-docs-chat { } .docs-chat-bubble a { - color: var(--accent); + color: var(--link); text-decoration: none; } diff --git a/devplacepy/static/css/docs.css b/devplacepy/static/css/docs.css index 1888ec8..78ec22b 100644 --- a/devplacepy/static/css/docs.css +++ b/devplacepy/static/css/docs.css @@ -171,7 +171,7 @@ .method-get { background: var(--info); } .method-post { background: var(--success); } .method-delete { background: var(--danger); } -.method-put { background: var(--warning); } +.method-put { background: var(--warning); color: var(--on-warning); } .auth-badge { border: 1px solid var(--border-light); @@ -380,7 +380,7 @@ textarea.param-input { .response-2xx { background: var(--success); } .response-3xx { background: var(--info); } -.response-4xx { background: var(--warning); } +.response-4xx { background: var(--warning); color: var(--on-warning); } .response-5xx { background: var(--danger); } .response-redirect { diff --git a/devplacepy/static/css/markdown.css b/devplacepy/static/css/markdown.css index 120b1f7..7e5b501 100644 --- a/devplacepy/static/css/markdown.css +++ b/devplacepy/static/css/markdown.css @@ -47,12 +47,12 @@ .rendered-content h4 { font-size: 0.9375rem; } .rendered-content a { - color: var(--accent); + color: var(--link); text-decoration: underline; } .rendered-content a:hover { - color: var(--accent-hover); + color: var(--link-hover); } .rendered-content hr { @@ -98,7 +98,7 @@ padding: 0.125rem 0.375rem; border-radius: 4px; background: var(--bg-card-hover); - color: var(--accent); + color: var(--link); } .rendered-content pre { diff --git a/devplacepy/static/css/services.css b/devplacepy/static/css/services.css index 2debacf..c0bbba0 100644 --- a/devplacepy/static/css/services.css +++ b/devplacepy/static/css/services.css @@ -59,7 +59,7 @@ .service-status.stalled { background: var(--warning); - color: var(--white); + color: var(--on-warning); } .service-controls { diff --git a/devplacepy/static/css/variables.css b/devplacepy/static/css/variables.css index 77a5885..6bff8ab 100644 --- a/devplacepy/static/css/variables.css +++ b/devplacepy/static/css/variables.css @@ -16,6 +16,9 @@ --accent-gradient: linear-gradient(135deg, #b73f1e, #af3050, #8f3b71); --on-accent: #fff; + --link: #df5f3b; + --link-hover: #d4627e; + --text-primary: #e9e4ff; --text-secondary: #c8c3e5; --text-muted: #9b93c9; @@ -27,6 +30,7 @@ --warning: #fbbf24; --danger: #f87171; --info: #60a5fa; + --on-warning: #1c1200; --radius: 8px; --radius-input: 8px; diff --git a/pyproject.toml b/pyproject.toml index f18a924..89d3d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "devplacepy" -version = "1.0.9" +version = "1.0.10" description = "DevPlace - The Developer Social Network" requires-python = ">=3.12" dependencies = [