openvibers/openvibe.community Commits

Community: filled controls use the theme's readable accent pair (WCAG AA contrast) (#39)

OP OpenVibers committed 10 Oct 2026 · 00369a4bec · 1 file, +4 −4

Accessibility (WCAG AA contrast). Filled buttons, chips and badges painted with the plain theme accent and white text now use the theme's readable pair: `background: var(--accent-strong, var(--accent, …))` with `color: var(--on-accent-strong, …)`.

White on the default accent `#3b82f6` is 3.68:1, under AA's 4.5:1 for normal-size text. The strong pair is 4.62:1 in the default theme, and every built-in theme defines it, using dark text on the light accents. The release UX check's axe run flagged this as `color-contrast` (serious), for example on the search buttons of openvibe.trade and openvibe.deals.

The change is mechanical and limited to rule blocks that set both an accent background and a white or `--on-accent` text colour. Bars, dots and outlines are untouched. A border drawn in the same accent follows the fill, and an existing `--on-accent` fallback is kept. Affected tests pass (`ov test`). The matching shared half is openvibe-shared 2.20.3.

Changes

ddiff --git a/public/css/community.css b/public/css/community.css
iindex 7792ab5..11ba198 100644
---- a/public/css/community.css
++++ b/public/css/community.css
@@@ -73,7 +73,7 @@ img { max-width: 100%; height: auto; }
.author { display: inline-flex; align-items: center; gap: 6px; color: inherit; min-width: 0; }
.author span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; flex: none; }
--.avatar-letter { display: inline-grid; place-items: center; background: var(--accent); color: var(--on-accent); font-size: .7rem; font-weight: 700; }
++.avatar-letter { display: inline-grid; place-items: center; background: var(--accent-strong, var(--accent, #3472d8)); color: var(--on-accent-strong, var(--on-accent)); font-size: .7rem; font-weight: 700; }
.badge { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 999px; font-size: .75rem; font-weight: 600; border: 1px solid var(--border-light); color: var(--text-secondary); }
a.badge:hover { border-color: var(--accent); text-decoration: none; color: var(--accent-light); }
.badge-nsfw { border-color: var(--danger); color: var(--danger); }
@@@ -178,7 +178,7 @@ a.badge:hover { border-color: var(--accent); text-decoration: none; color: var(-
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px; border-radius: 999px; border: 1px solid var(--border-light); color: var(--text-secondary); font-size: .85rem; font-weight: 600; }
.chip:hover { border-color: var(--accent); text-decoration: none; }
--.chip.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
++.chip.active { background: var(--accent-strong, var(--accent, #3472d8)); border-color: var(--accent-strong, var(--accent, #3472d8)); color: var(--on-accent-strong, var(--on-accent)); }
.chip.active .muted { color: inherit; opacity: .8; }
.inline-form { display: inline-flex; gap: 6px; align-items: center; margin: 0; }
.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; }
@@@ -188,7 +188,7 @@ a.badge:hover { border-color: var(--accent); text-decoration: none; color: var(-
.vote-score { font-weight: 700; }
.vote-btn { background: none; border: 1px solid var(--border-light); border-radius: 7px; color: var(--text-secondary); width: 2.2em; height: 2em; cursor: pointer; font: inherit; }
.vote-btn:hover { border-color: var(--accent); color: var(--accent-light); }
--.vote-btn.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
++.vote-btn.active { background: var(--accent-strong, var(--accent, #3472d8)); border-color: var(--accent-strong, var(--accent, #3472d8)); color: var(--on-accent-strong, var(--on-accent)); }
.mod-actions form { margin: 0; }
.posts { display: flex; flex-direction: column; gap: 12px; margin: 14px 0; }
.post { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 18px; }
@@@ -267,7 +267,7 @@ a.badge:hover { border-color: var(--accent); text-decoration: none; color: var(-
.postbit { display: grid; grid-template-columns: 170px 1fr; background: var(--bg-card); border: 1px solid var(--border-light); border-radius: 12px; scroll-margin-top: 80px; }
.postbit-author { padding: 14px; background: var(--bg-hover); border-right: 1px solid var(--border-light); border-radius: 12px 0 0 12px; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; }
.postbit-avatar { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; }
--.postbit-letter { display: grid; place-items: center; background: var(--accent); color: var(--on-accent); font-weight: 700; font-size: 1.5rem; }
++.postbit-letter { display: grid; place-items: center; background: var(--accent-strong, var(--accent, #3472d8)); color: var(--on-accent-strong, var(--on-accent)); font-weight: 700; font-size: 1.5rem; }
.postbit-name { font-weight: 700; word-break: break-word; }
.postbit-stats { display: grid; gap: 2px; margin: 0; font-size: .8rem; color: var(--text-secondary); }
.postbit-stats div { display: flex; gap: 6px; justify-content: center; }