Close Menu
    Latest Post

    Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling

    February 22, 2026

    How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic

    February 21, 2026

    Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry

    February 21, 2026
    Facebook X (Twitter) Instagram
    Trending
    • Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling
    • How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic
    • Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry
    • How to Cancel Your Google Pixel Watch Fitbit Premium Trial
    • GHD Speed Hair Dryer Review: Powerful Performance and User-Friendly Design
    • An FBI ‘Asset’ Helped Run a Dark Web Site That Sold Fentanyl-Laced Drugs for Years
    • The Next Next Job, a framework for making big career decisions
    • Google Introduces Lyria 3: A Free AI Music Generator for Gemini
    Facebook X (Twitter) Instagram Pinterest Vimeo
    NodeTodayNodeToday
    • Home
    • AI
    • Dev
    • Guides
    • Products
    • Security
    • Startups
    • Tech
    • Tools
    NodeTodayNodeToday
    Home»Dev»Implementing Contrast-Color Functionality Using Current CSS Features
    Dev

    Implementing Contrast-Color Functionality Using Current CSS Features

    Samuel AlejandroBy Samuel AlejandroFebruary 19, 2026No Comments6 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    src 15oqhye featured
    Share
    Facebook Twitter LinkedIn Pinterest Email

    When an element has a customizable background color, determining whether the foreground text should be light or dark is a common challenge, particularly with accessibility in mind.

    Several specification functions have been drafted for this capability, including the recent contrast-color() (previously color-contrast()) within the CSS Color Module Level 5 draft. However, with only Safari and Firefox currently supporting it, a final, widely adopted version appears to be distant. Given the significant new functionality introduced in CSS, an exploration was conducted to determine if a cross-browser compatible implementation could be achieved today. The following demonstrates a potential solution:

    color: oklch(from <your color> round(1.21 - L) 0 0);

    The methodology behind this approach is detailed below.

    WCAG 2.2

    WCAG provides the formulas it uses for calculating the contrast between two RGB colors, which Stacie Arellano has described in great detail. This method is based on older techniques, calculating the luminance of colors (their perceived brightness) and attempting to account for monitor limitations and screen flare:

    L1 + 0.05 / L2 + 0.05

    …where the lighter color (L1) is positioned at the top. Luminance values range from 0 to 1, and this fraction accounts for contrast ratios spanning from 1 (1.05/1.05) to 21 (1.05/.05).

    The formulas for calculating the luminance of RGB colors are intricate, but the objective is to determine whether white or black offers superior contrast against a specific color, allowing for some simplification. This leads to a simplified representation:

    L = 0.1910(R/255+0.055)^2.4 + 0.6426(G/255+0.055)^2.4 + 0.0649(B/255+0.055)^2.4

    This can be translated into CSS as follows:

    calc(.1910*pow(r/255 + .055,2.4)+.6426*pow(g/255 + .055,2.4)+.0649*pow(b/255 + .055,2.4))

    The entire expression can be rounded to 1 for white or 0 for black using the round() function:

    round(.67913 - .1910*pow(r/255 + .055, 2.4) - .6426*pow(g/255 + .055, 2.4) - .0649*pow(b/255 + .055, 2.4))

    Multiplying this value by 255 and applying it to all three color channels with the relative color syntax yields the following:

    color: rgb(from <your color>  
      round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
      round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
      round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)  
    );

    This formula, when provided a color, returns either white or black according to WCAG 2 guidelines. While functional, its readability is challenging. It is worth noting that APCA is expected to supersede it as a more advanced formula in future WCAG guidelines. Re-calculating with APCA would be even more complex. Although CSS functions could potentially simplify the code, this implementation would likely remain difficult to access, comprehend, and maintain.

    New Approach

    A different approach considered available features, specifically color spaces. The ‘L*’ value within the CIELAB color space signifies perceptual lightness, aiming to mirror human visual perception. While distinct from luminance, it offers a similar measure. This suggests a potential method for determining optimal black or white text contrast based on perceptual lightness. The goal is to identify a threshold where colors with lower lightness use black text, and those with higher lightness use white text.

    An intuitive assumption might place this threshold at 50% or 0.5; however, this is not the case. Many colors, even bright ones, exhibit better contrast with white text than with black. Examples using lch(), where lightness gradually increases while hue remains constant, illustrate this point:

    The transition point, where black text becomes more legible than white, typically falls between 60-65. A Node.js application, utilizing Colorjs.io and APCA for contrast calculations, was developed to determine the precise cutoff.

    For oklch(), the identified threshold ranges from .65 to .72, with an average of .69.

    • When the OKLCH lightness is .72 or above, black will always contrast better than white.
    • Below .65, white will always contrast better than black.
    • Between .65 and .72, typically both black and white have contrasts between 45-60.

    Therefore, by employing `round()` and an upper bound of .72, a more concise implementation can be created:

    color: oklch(from <your color> round(1.21 - L) 0 0);

    The value 1.21 is used to ensure that .72 rounds down and .71 rounds up: 1.21 – .72 results in .49, which rounds down, while 1.21 – .71 results in .5, which rounds up.

    This formula demonstrates good performance and has undergone several production iterations. It offers improved readability and maintainability. However, this formula aligns more closely with APCA than WCAG, leading to occasional discrepancies. For instance, WCAG indicates black has higher contrast (4.70 vs. 4.3 for white) on #407ac2, while APCA suggests the reverse: black at 33.9 contrast and white at 75.7. The new CSS formula aligns with APCA, displaying white text:

    A blue rectangle with white and black text compared on top. White says APCA and black says WCAG.

    This formula could be considered superior to WCAG 2.0 due to its closer alignment with APCA. Nevertheless, accessibility checks remain crucial. If legal compliance mandates WCAG rather than APCA, this simpler formula might be less suitable.

    LCH vs. OKLCH

    Analysis of both LCH and OKLCH data indicates that OKLCH is the superior choice, beyond its design as a replacement for LCH.

    In LCH, the range where colors are too dark for black text and too light for white text is often wider and more variable. For instance, colors from #e862e5 to #fd76f9 fall into this problematic range. In LCH, this corresponds to lightness values between 63 and 70, whereas in OKLCH, it is between .7 and .77. The lightness scaling of OKLCH demonstrates a better match with APCA.

    One Step Further

    While maximizing contrast is always beneficial, an additional technique can be implemented. The current logic provides only white or black text (a limitation of the color-contrast() function). This can be modified to offer white or another specified color, such as a base text color. Beginning with this:

    color: oklch(from <your color> round(1.21 - L) 0 0);  
    
    /* becomes: */
    
    --white-or-black: oklch(from <your color> round(1.21 - L) 0 0);  
    color: rgb(  
      from color-mix(in srgb, var(--white-or-black), <base color>)  
      calc(2*r) calc(2*g) calc(2*b)  
    );

    This involves some ingenious mathematical operations, though its readability is not optimal:

    • If –white-or-black is white, color-mix() produces rgb(127.5, 127.5, 127.5) or brighter. When doubled, this results in rgb(255, 255, 255) or higher, effectively yielding white.
    • If –white-or-black is black, color-mix() reduces each RGB channel value by 50%. Doubling this then restores the original value of the <base color>.

    Regrettably, this formula is not compatible with Safari 18 and earlier versions, requiring targeting of Chrome, Safari 18+, and Firefox. Nevertheless, it provides a pure CSS method for switching between white and a base text color, rather than solely white and black. A fallback to white and black can be implemented for Safari versions below 18.

    These approaches can also be refactored using CSS Custom Functions, though their support is not yet universal:

    @function --white-black(--color) {  
      result: oklch(from var(--color) round(1.21 - l) 0 0);  
    }
    
    @function --white-or-base(--color, --base) {  
      result: rgb(from color-mix(in srgb, --white-black(var(--color)), var(--base)) calc(2*r) calc(2*g) calc(2*b));  
    }

    Conclusion

    This technique offers a flexible and adaptable implementation by identifying a threshold and a straightforward formula. The threshold can be readily adjusted to suit specific requirements.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleX (formerly Twitter) Launches Voice and Video Calls: How to Disable the Feature
    Next Article Moltworker: Running a Self-Hosted Personal AI Agent on Cloudflare
    Samuel Alejandro

    Related Posts

    Dev

    Docker vs Kubernetes in Production: A Security-First Decision Framework

    February 21, 2026
    Dev

    Effortless VS Code Theming: A Guide to Building Your Own Extension

    February 19, 2026
    Tools

    Announcing Interop 2024

    February 17, 2026
    Add A Comment
    Leave A Reply Cancel Reply

    Latest Post

    ChatGPT Mobile App Surpasses $3 Billion in Consumer Spending

    December 21, 202513 Views

    Creator Tayla Cannon Lands $1.1M Investment for Rebuildr PT Software

    December 21, 202511 Views

    Automate Your iPhone’s Always-On Display for Better Battery Life and Privacy

    December 21, 202510 Views
    Stay In Touch
    • Facebook
    • YouTube
    • TikTok
    • WhatsApp
    • Twitter
    • Instagram
    About

    Welcome to NodeToday, your trusted source for the latest updates in Technology, Artificial Intelligence, and Innovation. We are dedicated to delivering accurate, timely, and insightful content that helps readers stay ahead in a fast-evolving digital world.

    At NodeToday, we cover everything from AI breakthroughs and emerging technologies to product launches, software tools, developer news, and practical guides. Our goal is to simplify complex topics and present them in a clear, engaging, and easy-to-understand way for tech enthusiasts, professionals, and beginners alike.

    Latest Post

    Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling

    February 22, 20260 Views

    How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic

    February 21, 20260 Views

    Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry

    February 21, 20260 Views
    Recent Posts
    • Trump Reinstates De Minimis Exemption Suspension Despite Supreme Court Ruling
    • How Cloudflare Mitigated a Vulnerability in its ACME Validation Logic
    • Demis Hassabis and John Jumper Receive Nobel Prize in Chemistry
    • How to Cancel Your Google Pixel Watch Fitbit Premium Trial
    • GHD Speed Hair Dryer Review: Powerful Performance and User-Friendly Design
    Facebook X (Twitter) Instagram Pinterest
    • About Us
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    • Disclaimer
    • Cookie Policy
    © 2026 NodeToday.

    Type above and press Enter to search. Press Esc to cancel.