BLOG-165 Adjust label text color based on background luminance for better contrast #298

Merged
squid merged 3 commits from BLOG-165_support_dark_color_label_for_posts into main 2026-05-23 00:31:57 +08:00
Owner

Description

This PR introduces support for dark background colors on post labels.

Specifically, the following changes have been made:

  1. Luminance & Color Contrast: Added isDark and contrastingTextColor getters in ColorViewModel. The color contrast detection has been improved to use the standard sRGB relative luminance formula (per WCAG 2.x standard) with a threshold of 0.179 to determine if a background is dark, ensuring much more accurate contrast adjustments than a simple YIQ calculation.
  2. Text Color Adjustment: Sets the text color of the PostLabel dynamically to #ffffff if the background is dark, and inherits the default color if the background is light.
  3. Indicator Dot Styling: Adjusts the background color of the small decorative dot inside PostLabel using a lighter variant (lighten(0.4)) on dark labels, and a darker variant (darken(0.2)) on light labels to ensure visual clarity.

Package Changes

No response

Screenshots

No response

Reference

Resolves #165

Checklist

  • A milestone is set
  • The related issues has been linked to this branch
### Description This PR introduces support for dark background colors on post labels. Specifically, the following changes have been made: 1. **Luminance & Color Contrast**: Added `isDark` and `contrastingTextColor` getters in `ColorViewModel`. The color contrast detection has been improved to use the standard sRGB relative luminance formula (per WCAG 2.x standard) with a threshold of `0.179` to determine if a background is dark, ensuring much more accurate contrast adjustments than a simple YIQ calculation. 2. **Text Color Adjustment**: Sets the text color of the `PostLabel` dynamically to `#ffffff` if the background is dark, and inherits the default color if the background is light. 3. **Indicator Dot Styling**: Adjusts the background color of the small decorative dot inside `PostLabel` using a lighter variant (`lighten(0.4)`) on dark labels, and a darker variant (`darken(0.2)`) on light labels to ensure visual clarity. ### Package Changes _No response_ ### Screenshots _No response_ ### Reference Resolves #165 ### Checklist - [x] A milestone is set - [x] The related issues has been linked to this branch
squid added 2 commits 2026-05-23 00:00:15 +08:00
BLOG-165 fix: use inherit instead of black for light backgrounds
All checks were successful
Frontend CI / build (push) Successful in 1m20s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 15s
PR Title Check / pr-title-check (pull_request) Successful in 15s
1fcbfc6746
squid changed title from BLOG-165 feat: adjust label text color based on background luminance for better contrast to BLOG-165 Adjust label text color based on background luminance for better contrast 2026-05-23 00:01:20 +08:00
Collaborator

/improve

/improve
Collaborator

PR Code Suggestions

CategorySuggestion                                                                                                                                    Impact
General
Use WCAG formula for luminance

The YIQ formula is a decent approximation for perceived brightness, but for web
accessibility, the W3C recommends a more accurate formula for relative luminance.
Using the standard WCAG formula will ensure better contrast calculations across a
wider range of colors and align with modern accessibility guidelines.

frontend/src/lib/label/adapter/presenter/colorViewModel.ts [86-89]

 get isDark(): boolean {
-	const yiq = (this.red * 299 + this.green * 587 + this.blue * 114) / 1000;
-	return yiq < 128;
+	// Per WCAG 2.0, https://www.w3.org/TR/WCAG20-TECHS/G17.html
+	const sRGB = [this.red, this.green, this.blue].map((c) => {
+		const s = c / 255;
+		return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
+	});
+	const luminance = 0.2126 * sRGB[0] + 0.7152 * sRGB[1] + 0.0722 * sRGB[2];
+	// The threshold 0.179 is where contrast with white and black is equal.
+	return luminance < 0.179;
 }
Suggestion importance[1-10]: 8

__

Why: This is an excellent suggestion. The YIQ formula is a good approximation, but the WCAG formula for relative luminance is the standard for web accessibility. Using it ensures more accurate contrast calculations and better adherence to modern accessibility guidelines.

Medium
Possible issue
Use specific color for better contrast

Using inherit for the text color on light backgrounds relies on the parent element
having a dark text color. This assumption can fail in different contexts, such as a
dark-themed page, leading to unreadable text with low contrast. To ensure the
component is robust and accessible, it's better to return a specific dark color like
#000000.

frontend/src/lib/label/adapter/presenter/colorViewModel.ts [91-93]

 get contrastingTextColor(): string {
-	return this.isDark ? '#ffffff' : 'inherit';
+	return this.isDark ? '#ffffff' : '#000000';
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that using inherit can lead to poor contrast if the parent element's color is not dark. Replacing it with a specific dark color like #000000 makes the component more robust and improves accessibility.

Medium
## PR Code Suggestions ✨ <!-- --> <table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=1>General</td> <td> <details><summary>Use WCAG formula for luminance</summary> ___ **The YIQ formula is a decent approximation for perceived brightness, but for web <br>accessibility, the W3C recommends a more accurate formula for relative luminance. <br>Using the standard WCAG formula will ensure better contrast calculations across a <br>wider range of colors and align with modern accessibility guidelines.** [frontend/src/lib/label/adapter/presenter/colorViewModel.ts [86-89]](https://git.squidspirit.com/squid/blog/src/branch/BLOG-165_support_dark_color_label_for_posts/frontend/src/lib/label/adapter/presenter/colorViewModel.ts#L86-L89) ```diff get isDark(): boolean { - const yiq = (this.red * 299 + this.green * 587 + this.blue * 114) / 1000; - return yiq < 128; + // Per WCAG 2.0, https://www.w3.org/TR/WCAG20-TECHS/G17.html + const sRGB = [this.red, this.green, this.blue].map((c) => { + const s = c / 255; + return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4); + }); + const luminance = 0.2126 * sRGB[0] + 0.7152 * sRGB[1] + 0.0722 * sRGB[2]; + // The threshold 0.179 is where contrast with white and black is equal. + return luminance < 0.179; } ``` <details><summary>Suggestion importance[1-10]: 8</summary> __ Why: This is an excellent suggestion. The YIQ formula is a good approximation, but the WCAG formula for relative luminance is the standard for web accessibility. Using it ensures more accurate contrast calculations and better adherence to modern accessibility guidelines. </details></details></td><td align=center>Medium </td></tr><tr><td rowspan=1>Possible issue</td> <td> <details><summary>Use specific color for better contrast</summary> ___ **Using <code>inherit</code> for the text color on light backgrounds relies on the parent element <br>having a dark text color. This assumption can fail in different contexts, such as a <br>dark-themed page, leading to unreadable text with low contrast. To ensure the <br>component is robust and accessible, it's better to return a specific dark color like <br><code>#000000</code>.** [frontend/src/lib/label/adapter/presenter/colorViewModel.ts [91-93]](https://git.squidspirit.com/squid/blog/src/branch/BLOG-165_support_dark_color_label_for_posts/frontend/src/lib/label/adapter/presenter/colorViewModel.ts#L91-L93) ```diff get contrastingTextColor(): string { - return this.isDark ? '#ffffff' : 'inherit'; + return this.isDark ? '#ffffff' : '#000000'; } ``` <details><summary>Suggestion importance[1-10]: 7</summary> __ Why: The suggestion correctly points out that using `inherit` can lead to poor contrast if the parent element's color is not dark. Replacing it with a specific dark color like `#000000` makes the component more robust and improves accessibility. </details></details></td><td align=center>Medium </td></tr></tr></tbody></table>
squid added 1 commit 2026-05-23 00:29:30 +08:00
BLOG-165 feat: improve relative luminance calculation for color contrast
All checks were successful
Frontend CI / build (push) Successful in 1m19s
PR Title Check / pr-title-check (pull_request) Successful in 15s
9a3dddaebb
Author
Owner

Use WCAG formula for luminance

9a3dddaebb

> Use WCAG formula for luminance 9a3dddaebbb23cddb044fbd73263ff0d6bd779de
squid merged commit 39e712036e into main 2026-05-23 00:31:57 +08:00
squid deleted branch BLOG-165_support_dark_color_label_for_posts 2026-05-23 00:31:57 +08:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: squid/blog#298