Can the text color be customized to match the brightness level of the background area being covered?

Does anyone know of a plugin or method that can automatically change the color of text or swap predefined images/icons based on the average brightness of its parent element's background? If the background is dark, it should make the text white or switch icons accordingly.

It would also be useful if the script could detect when the parent element has no defined background and then search for the nearest one in its parent hierarchy.

Do you have any thoughts on this concept? Are there existing solutions or examples available?

Answer №1

Here are some interesting resources related to this topic:

Check out the W3C algorithm below (includes JSFiddle demo link):

const rgb = [255, 0, 0];

// Continuously update colors to demonstrate changes
setInterval(setContrast, 1000);

function setContrast() {
  // Randomly change colors
  rgb[0] = Math.round(Math.random() * 255);
  rgb[1] = Math.round(Math.random() * 255);
  rgb[2] = Math.round(Math.random() * 255);

  // Using formula from w3.org to calculate color contrast
  const brightness = Math.round(((parseInt(rgb[0]) * 299) +
                      (parseInt(rgb[1]) * 587) +
                      (parseInt(rgb[2]) * 114)) / 1000);
  const textColour = (brightness > 125) ? 'black' : 'white';
  const backgroundColour = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
  $('#bg').css('color', textColour); 
  $('#bg').css('background-color', backgroundColour);
}
#bg {
  width: 200px;
  height: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="bg">Example Text</div>

Answer №2

Check out this informative piece on improving accessibility through Calculating Color Contrast featured on 24 ways. Although the initial functions may be incorrect, the YIQ formula provided can assist you in determining whether a light or dark foreground color is more suitable.

After obtaining the background color of an element (or its ancestor), utilize the following function from the article to find an appropriate foreground color:

function getContrastYIQ(hexcolor){
    var r = parseInt(hexcolor.substring(1,3),16);
    var g = parseInt(hexcolor.substring(3,5),16);
    var b = parseInt(hexcolor.substring(5,7),16);
    var yiq = ((r*299)+(g*587)+(b*114))/1000;
    return (yiq >= 128) ? 'black' : 'white';
}

Answer №3

Try using mix-blend-mode to achieve the desired effect:

section {
  overflow: hidden;
  height: 90vh;
  background: url(https://www.example.com/image.jpg) center/cover;
}

h3 {
  color: black;
  font: 800 30vmin/40vh sans-serif;
  text-align: center;
  mix-blend-mode: difference;
  filter: drop-shadow(0.1em 0.1em purple);
}
<section>
  <h3 contentEditable role='textbox' aria-multiline='true'>Click here to edit</h3>
</section>

For more information on different modes and implementations, you can check out this helpful tutorial: https://example.com/css-effects-and-knockout-text/

Answer №4

Great question! When considering how to change the look of text on a webpage, one interesting approach is to invert the background color with the text color. This can be achieved by extracting the RGB values of the background and inverting them.

A practical example can be found here: http://jsfiddle.net/3RJKL/4/

var rgb = $('#demo').css('backgroundColor');
var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
var brightness = 0.75;

var r = colors[1];
var g = colors[2];
var b = colors[3];

var ir = Math.floor((255 - r) * brightness);
var ig = Math.floor((255 - g) * brightness);
var ib = Math.floor((255 - b) * brightness);

$('#demo').css('color', 'rgb('+ir+','+ig+','+ib+')');

Answer №5

Calculating the contrast from a HEX 6-character color string (#123456) in ES6 can be done with this simple one-liner:

const calculateContrast = hexColor =>["#000","#fff"][~~([.299,.587,.114].reduce((result, value, index)=>parseInt(hexColor.substr(index*2+1,2),16)*value+result,0)<128)];

//const color = calculateContrast("#123456"); //#fff
//const color = calculateContrast("#ABCDEF"); //#000

Here is a more readable version of the code breakdown:

const calculateContrast = hexColor =>
{
  const luminance = [.299 /*red*/, .587 /*green*/, .114 /*blue*/]
    .reduce((result, value, index) =>
    {
      const number = parseInt(hexColor.substr(index * 2 + 1, 2), 16);
      return number * value + result;
    }, 0);

  const isDark = luminance < 128;
  const index = ~~isDark;
  return ["#000", "#fff"][index];
}

function setColors()
{
  for(let i = 0; i < 70; i++)
  {
    const bgColor = "#" + (~~(Math.random() * 16777216)).toString(16).padStart(6, 0);
    const color = calculateContrast(bgColor);

    node = test.children[i] || document.createElement("span");
    node.style.backgroundColor = bgColor;
    node.style.color = color;
    node.textContent = bgColor;
    if (!node.parentNode)
      test.appendChild(node);
  }
}

setColors();
#test
{
  display: flex;
  flex-wrap: wrap;
  font-family: monospace;
}
#test > *
{
  padding: 0.3em;
}
<button onclick="setColors()">change</button>
<div id="test"></div>

Answer №6

After reviewing the insights from contributors like alex-ball and jeremyharris, I have come up with a personalized solution that works best for my needs:

$('.elzahaby-bg').each(function() {
  var rgb = $(this).css('backgroundColor');
  var colors = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);

  var r = colors[1];
  var g = colors[2];
  var b = colors[3];

  var o = Math.round(((parseInt(r) * 299) + (parseInt(g) * 587) + (parseInt(b) * 114)) / 1000);

  if (o > 125) {
    $(this).css('color', 'black');
  } else {
    $(this).css('color', 'white');
  }
});
* {
  padding: 9px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class='elzahaby-bg' style='background-color:#000'>color is white</div>

<div class='elzahaby-bg' style='background-color:#fff'>color is black</div>
<div class='elzahaby-bg' style='background-color:yellow'>color is black</div>
<div class='elzahaby-bg' style='background-color:red'>color is white</div>

Answer №7

I've discovered the BackgroundCheck tool to be incredibly helpful.

This tool identifies the overall brightness of a background, whether it's an image or a color, and then adds a class to the designated text element (background--light or background--dark) based on the background's brightness.

It works for both stationary and moving elements.

(Source)

Answer №8

If you're utilizing ES6, there's a way to convert hex to RGB and then utilize this code snippet:

const hexToRgb = hex => {
    // Convert hex value to RGB
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    return result
        ? {
              r: parseInt(result[1], 16),
              g: parseInt(result[2], 16),
              b: parseInt(result[3], 16)
          }
        : null
}

// Calculate to determine if it will match better on black or white
const setContrast = rgb =>
    (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 > 125 ? 'black' : 'white'

const getCorrectColor = setContrast(hexToRgb(#ffffff))

Answer №9

My rendition:

(function ($) {
    $.fn.textInContrast = function () {
        var elem = this,
            isTransparent;
        isTransparent = function (color) {
            var match = color.match(/[0-9]+/g);
            if (match !== null) {
                return !!match[3];
            }
            else return false;
        };
        while (isTransparent(elem.css('background-color'))) {
            elem = elem.parent();
        }
        var components = elem.css('background-color').match(/[0-9]+/g);
        this.lightBackground = !!Math.round(
            (
                parseInt(components[0], 10) + // red
                parseInt(components[1], 10) + // green
                parseInt(components[2], 10) // blue
            ) / 765 // 255 * 3, so that we avg, then normalize to 1
        );
        if (this.lightBackground) {
            this.css('color', 'black');
        } else {
            this.css('color', 'white');
        }
        return this;
    };
}(jQuery));

To apply it:

var target = $('#my-element');
target.textInContrast();

This will instantly adjust the text color to black or white as needed. For icons:

if (target.lightBackground) {
    iconSuffix = 'black';
} else {
    iconSuffix = 'white';
}

Then each icon filename could be like 'save' + iconSuffix + '.jpg'.

Keep in mind, this won't function when any container exceeds its parent's dimensions (e.g., when CSS height is 0 and overflow isn't hidden). Making it work in those cases would require more intricate solutions.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

"Error in react-three-fiber: Only the last mesh retains its model, all others are

Currently, I am working on a React application where I am using react-three-fiber to visualize data in a 3D environment. My goal is to create various models by passing data representing their characteristics as props into components. However, I am encounte ...

Enhanced Rating System for Asp.Net

How can I retrieve the selected value (CurrentValue) of the ASP.NET Rating control in javascript? I have it implemented within a datagrid, and I am facing difficulty in accessing the CurrentValue property. Any suggestions or solutions for this issue woul ...

Can we safely save a value in session storage directly from the main.js file in Vue?

Throughout the user session managed by Vuex, I have a session storage value set to false that needs to be monitored. Setting up this value directly from the main.js file looks like this: import { createApp } from 'vue'; import App from './Ap ...

The onchange() function for a multi-checkbox dropdown is failing to work as

Issue with Multiple Drop Down Checkbox Functionality: The first parameter is always received as undefined, and the onchange event only captures the value of the first checkbox selected. <select name="multicheckbox[]" multiple="multiple" class="4colacti ...

The grid flex end is behaving differently than I anticipated

I am struggling to align two buttons vertically on the right side. Here is my code snippet: const WalletsContainer = () => { return ( <Grid style={{ background: 'red' }} direction={'column'} alignItems={'flex-end'} ...

The background hue does not cover the entire element

When I hover over my drop-down menu button, the grey color only covers up to where I set the padding. Here is my HTML snippet: Help ▾ Give Get Help Get Involved I want the grey color to expand fully across the "li" ...

Error: The function Stripe.customers.cancel is not recognized in Stripe version 14.18.0

When executing Cypress tests that involve calling a cleanup function to remove users in Stripe, I encounter the following error: Body: { "status": 500, "message": "Error while cleaning the stripe test data", "error" ...

Tips for determining the height of the entire webpage using jQuery

I attempted the following code: $(window).height(); $("#content").height(); However, it did not provide an accurate value. ...

How can I execute a basic query in jQuery or JavaScript based on a selected value that changes

After successfully retrieving the dropdown selection value with alert(selectedString) in this scenario, I am now looking to utilize that value for querying a table using mysqli and PHP. What is the best approach for querying a database table based on the ...

Challenges faced with react-bootstrap-autosuggest

After spending the entire day attempting to integrate the package from here into my create-react-app project upon ejection, I encountered the following error: Failed to compile. Error in ./~/react-bootstrap-autosuggest/lib/Autosuggest.js Module not found ...

Updating the database table using javascript

I am looking to dynamically update my database based on a condition in JavaScript. Currently, I have been using the following approach: <a href="#" onclick="hello()">Update me</a> <script> function hello(smthing) { if(smthing == true) ...

How to use jQuery to select an element based on a partial match of its ID using regular

I am trying to find input fields that follow this specific pattern: a_profile_contact_attributes_addresses_attributes_0_address b_store_contact_attributes_addresses_attributes_3_address somethingelse_contact_attributes_addresses_attributes_44_address To ...

The functionality to display dynamic images in Vue.js appears to be malfunctioning

As a newcomer to vue.js, I am facing a challenge in dynamically displaying images. Manually changing the images works fine, but I'm running into issues with dynamic display. I've come across similar problems online, but none of the solutions seem ...

How to properly align an object in a specified ul, li list

I'm currently facing an issue while building a Joomla site. I have set a background image for my li elements, but the items within the li are not centered as expected. The website I am working on is www.outlawsofhealth.com. The specific list in quest ...

What is the best way to pinpoint particular text within a paragraph that includes numerous line breaks?

So here is the puzzling situation I'm grappling with. Here's a peek at the HTML snippet: <p>This paragraph has <br><br> two unusual line breaks <br><br> and it happens TWICE!</p> The problem arises when tryi ...

Receiving a sleek black overlay with dynamic text upon hovering over an image

I have been searching extensively for a solution to my issue, but I haven't been able to make it work in my application. What I want is to create a black overlay over an image when it is hovered over, with text that resembles a button appearing on top ...

What is the best way to detect when an option is selected in a Material UI autocomplete component?

Utilizing the autocomplete feature with filterOptions to propose adding a new value: <Autocomplete multiple name="participant-tags" options={people} getOptionLabel={(option) => option.name} renderInput={(param ...

The ReactJS Ajax request returns a successful status code of 200 and retrieves the data, however, an error message

I have the following code snippet to fetch data from a backend API. When I paste the link into my browser's address bar, the API returns a result of '["2016-03-31T00:00:00"]'. However, when I use this AJAX call in my code, I receive a status ...

There seems to be an issue with the pseudo-elements :before and :after

I have been attempting to apply a double border to my table using the "border" property for one, and :before and :after for the second. However, I am facing an issue as it is not working properly. I am relatively new to CSS and HTML and have tried my bes ...

Looking for assistance with building a PHP-based live notifications system?

Today, I find myself stuck in a dilemma while attempting to create a notifications system using PHP and AJAX. My current approach involves fetching notification entries from the database and displaying them with a delete option. Each row is assigned a uni ...