Skip to content

fix(a11y): full-contrast suggestion value spans (#380)#427

Merged
JohnMcLear merged 2 commits into
mainfrom
fix/380-suggestion-contrast
Jun 6, 2026
Merged

fix(a11y): full-contrast suggestion value spans (#380)#427
JohnMcLear merged 2 commits into
mainfrom
fix/380-suggestion-contrast

Conversation

@JohnMcLear

Copy link
Copy Markdown
Member

Problem

`static/css/comment.css` rendered the suggestion value spans (`.from-value` / `.to-value`) at `opacity: .8`. Alpha-blending the text against an unknown skin background drops contrast below WCAG 2.1 AA (4.5:1) on darker / branded skins, even though the default colibris skin still passes.

Fix

Remove the `opacity: .8` from both the display and create variants so the value text inherits the skin's full-strength text color. Each skin then controls the colour directly instead of relying on alpha-blending against an unknown background. The `font-style: italic` and quote pseudo-elements still visually distinguish the value from its label.

Pure CSS — works identically on the latest release (2.7.3) and develop, no core update required.

Test

Added a Playwright regression guard in `commentSuggestion.spec.ts` that creates a suggestion, opens the comment, and asserts the rendered `.from-value` / `.to-value` spans report `opacity: 1`.

Verified locally (frontend) on both Etherpad 2.7.3 and develop — 4/4 suggestion specs pass on each.

Closes #380

🤖 Generated with Claude Code

The .from-value / .to-value spans were rendered at opacity: .8, which
alpha-blends the suggestion text against an unknown skin background. On
the default colibris skin this still passes WCAG AA, but on darker or
branded skins it can drop below the 4.5:1 threshold.

Remove the opacity so the value text inherits the skin's full-strength
text color (the italic + quote affordances already distinguish it from
the labels). This keeps each skin in control of the colour and works on
the latest release (2.7.3) and develop without any core change.

Add a Playwright regression guard asserting the rendered value spans are
fully opaque.

Closes #380

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Fix suggestion value spans contrast by removing opacity styling

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Remove opacity styling from suggestion value spans for WCAG AA contrast compliance
• Ensure value text inherits skin's full-strength color instead of alpha-blending
• Add Playwright regression test asserting rendered spans maintain full opacity
Diagram
flowchart LR
  A["Suggestion value spans<br/>opacity: .8"] -->|Remove opacity| B["Full-strength text color<br/>opacity: 1"]
  B -->|Inherit from skin| C["WCAG AA compliant<br/>4.5:1 contrast"]
  D["Regression test"] -->|Assert opacity| C

Loading

Grey Divider

File Changes

1. static/css/comment.css 🐞 Bug fix +0/-2

Remove opacity from suggestion value spans

• Remove opacity: .8 from .suggestion-display .from-value and .to-value selectors
• Remove opacity: .8 from .suggestion-create .from-value selector
• Preserve font-style: italic and quote pseudo-elements for visual distinction

static/css/comment.css


2. static/tests/frontend-new/specs/commentSuggestion.spec.ts 🧪 Tests +32/-0

Add regression test for suggestion span opacity

• Add new test case "Suggestion value spans are fully opaque for contrast (#380)"
• Create suggestion with comment and verify rendered spans have opacity: 1
• Test both .from-value and .to-value spans using computed style evaluation
• Include detailed comment explaining the regression guard and WCAG AA context

static/tests/frontend-new/specs/commentSuggestion.spec.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0)

Grey Divider


Action required

1. No token for suggestion values ✓ Resolved 📎 Requirement gap ⚙ Maintainability
Description
The opacity-based styling was removed but no explicit, skin-overridable color token/variable was
introduced for suggestion value spans. Without a dedicated token, skins cannot predictably override
suggestion value color independently of surrounding text styling.
Code

static/css/comment.css[R119-122]

.suggestion-display .from-value,
.suggestion-display .to-value {
-  opacity: .8;
font-style: italic;
}
Evidence
PR Compliance ID 2 requires replacing opacity: .8 with an explicit, skin-overridable color
token/variable for the relevant suggestion value selectors. The updated CSS removes opacity but does
not add any color token/variable on .suggestion-display .from-value/.to-value or
.suggestion-create .from-value.

Replace opacity-based styling with explicit, skin-overridable color token for suggestion values
static/css/comment.css[119-137]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`suggestion` value spans no longer use `opacity: .8`, but they also do not define an explicit, skin-overridable color token/variable as required.
## Issue Context
Compliance requires replacing opacity-based styling with an explicit token derived from skin text color, so skins can override the value color predictably.
## Fix Focus Areas
- static/css/comment.css[119-137]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Test checks opacity, not contrast ✓ Resolved 📎 Requirement gap ≡ Correctness
Description
The added Playwright regression test only asserts opacity equals 1 for suggestion value spans
and does not verify WCAG 2.1 AA contrast (>= 4.5:1) on the default skin. This can miss regressions
where text is fully opaque but still fails contrast due to color changes.
Code

static/tests/frontend-new/specs/commentSuggestion.spec.ts[R102-132]

+  // #380: the suggestion value spans used to be rendered at opacity: .8, which
+  // alpha-blends the text against an unknown skin background and can drop below
+  // WCAG AA on darker/branded skins. The fix removes the opacity so the value
+  // inherits the skin's full-strength text color. Guard against the regression
+  // by asserting the rendered spans are fully opaque.
+  test('Suggestion value spans are fully opaque for contrast (#380)',
+      async ({page}) => {
+        const outer = await getPadOuter(page);
+        const inner = await getPadBody(page);
+        const suggestedText = 'a contrast-safe suggestion';
+        await openCommentFormWithSuggestion(page, 'This content will receive a comment');
+        await expect(page.locator('#newComment.popup-show')).toBeVisible();
+        await page.locator('#newComment textarea.comment-content').fill('A comment');
+        await expect.poll(async () =>
+          page.locator('#newComment textarea.to-value').count()).toBeGreaterThan(0);
+        await page.locator('#newComment textarea.to-value').fill(suggestedText);
+        await page.locator('#comment-create-btn').click();
+
+        await expect.poll(async () =>
+          inner.locator('div').first().locator('.comment').count()).toBeGreaterThan(0);
+        await inner.locator('div').first().locator('.comment').first().click();
+        await expect.poll(async () =>
+          outer.locator('.comment-container .full-display-content:visible').count())
+            .toBeGreaterThan(0);
+
+        const opacityOf = async (selector: string) =>
+          outer.locator(selector).first().evaluate((el) =>
+            window.getComputedStyle(el).opacity);
+        expect(await opacityOf('.suggestion-display .from-value')).toBe('1');
+        expect(await opacityOf('.suggestion-display .to-value')).toBe('1');
+      });
Evidence
PR Compliance ID 3 requires an automated test that verifies suggestion value text contrast meets
WCAG 2.1 AA on the default skin. The added test computes getComputedStyle(el).opacity and asserts
it is 1, but it does not compute or assert contrast ratio or run axe-core contrast checks.

Add automated accessibility regression check for suggestion value contrast on default skin
static/tests/frontend-new/specs/commentSuggestion.spec.ts[102-132]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new test asserts `opacity: 1` but does not validate the actual text/background contrast ratio against WCAG 2.1 AA (>= 4.5:1).
## Issue Context
Compliance requires an automated regression check that verifies suggestion value text contrast meets AA on the default (colibris) skin, using axe-core rules or an explicit computed-style-based contrast calculation.
## Fix Focus Areas
- static/tests/frontend-new/specs/commentSuggestion.spec.ts[102-132]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Opacity test incomplete ✓ Resolved 🐞 Bug ☼ Reliability
Description
The new Playwright regression test only asserts getComputedStyle(...).opacity on the
.from-value/.to-value spans, which would still be '1' if a future regression applies opacity
to the parent .suggestion-display (or another ancestor) instead. It also does not cover the
create-form variant .suggestion-create .from-value, even though the CSS fix modifies both display
and create styles.
Code

static/tests/frontend-new/specs/commentSuggestion.spec.ts[R127-131]

+        const opacityOf = async (selector: string) =>
+          outer.locator(selector).first().evaluate((el) =>
+            window.getComputedStyle(el).opacity);
+        expect(await opacityOf('.suggestion-display .from-value')).toBe('1');
+        expect(await opacityOf('.suggestion-display .to-value')).toBe('1');
Evidence
The CSS change affects both display and create suggestion value styles, but the new test only checks
the display spans’ own opacity and does not check the create-form value element. The DOM structure
shows .suggestion-display is a parent wrapper around the spans (so opacity could be applied there
instead).

static/tests/frontend-new/specs/commentSuggestion.spec.ts[102-132]
static/css/comment.css[111-137]
templates/comments.html[29-34]
templates/comments.html[99-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new spec checks opacity only on the `.suggestion-display` value spans themselves. This misses regressions where opacity is applied to `.suggestion-display` (or another ancestor) and does not assert the create-form `.suggestion-create .from-value` at all.
### Issue Context
- The CSS fix removes opacity from both `.suggestion-display` value spans and the create-form `.suggestion-create .from-value`.
- `getComputedStyle(span).opacity` reports the span’s own opacity, not compounded ancestor opacity, so an ancestor-level opacity regression can evade the test.
### Fix Focus Areas
- static/tests/frontend-new/specs/commentSuggestion.spec.ts[102-132]
### Suggested fix
1. Add an assertion while the new-comment popup is open that `#newComment .suggestion-create` (and/or `#newComment .suggestion-create .from-value`) is fully opaque.
2. In the display portion, also assert `.suggestion-display` (the parent container) is fully opaque (in addition to the spans), or compute *effective* opacity by traversing ancestors and multiplying opacities.
3. Optionally add `expect.poll`/`toHaveCount(1)` for the target locators before calling `.evaluate()` to reduce flakiness and improve failure messages.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread static/css/comment.css
Comment thread static/tests/frontend-new/specs/commentSuggestion.spec.ts
…city (#380 Qodo)

Investigating Qodo's contrast feedback revealed the real root cause: the
colibris skin renders .from-value / .to-value in its green --primary-color
(#64d29b), ~1.9:1 against the light comment background — failing WCAG AA.
It also already sets opacity:1, so removing the plugin's opacity alone had
no effect on the default skin.

Set the value text to the skin's regular --text-color token (readable on
every bundled skin, still skin-overridable) with a dark fallback, using
!important to beat the core skin's non-important green regardless of
stylesheet load order. This is the explicit, skin-overridable colour token
Qodo asked for.

Strengthen the regression test to compute the actual rendered contrast
ratio (folding in opacity and the effective background) and assert it meets
WCAG 2.1 AA (>= 4.5:1), instead of only checking opacity === 1. The test
fails against the green core styling and passes with the override; verified
on both 2.7.3 and develop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JohnMcLear

Copy link
Copy Markdown
Member Author

Thanks @qodo — both points were spot on and led to the real root cause. The default colibris skin already overrode the plugin's opacity and renders .from-value/.to-value in its green --primary-color (#64d29b), which is ~1.9:1 on the light comment background — so opacity removal alone didn't fix the default skin. I've:

  1. Added an explicit, skin-overridable colour token as you suggested: the values now use var(--text-color, #2b2b2b) (readable on every bundled skin), overriding the core green.
  2. Replaced the opacity-only assertion with a real computed contrast-ratio check (folding in opacity + effective background) asserting WCAG 2.1 AA (>= 4.5:1).

The strengthened test fails against the green core styling and passes with the override, on both 2.7.3 and develop. Pushed.

@JohnMcLear JohnMcLear merged commit 275cad7 into main Jun 6, 2026
7 checks passed
@JohnMcLear JohnMcLear deleted the fix/380-suggestion-contrast branch June 6, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

a11y: re-evaluate .from-value / .to-value contrast across skins

1 participant