-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Swap between black and white text based on background color
- Loading branch information
1 parent
0ddbe97
commit 4f18c4e
Showing
1 changed file
with
62 additions
and
0 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
css/swap-between-black-and-white-text-based-on-background-color.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Swap between black and white text based on background color | ||
|
||
The title is overselling it a little, but not by much. Devon Govett posted [this clever trick](https://bsky.app/profile/devongovett.bsky.social/post/3lcedcdj4qk2y) on Bluesky using [CSS relative colors](https://developer.chrome.com/blog/css-relative-color-syntax) and [LCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch): | ||
|
||
```css | ||
.magic { | ||
--bg: red; | ||
background: var(--bg); | ||
color: lch(from var(--bg) calc((49.44 - l) * infinity) 0 0); | ||
} | ||
``` | ||
|
||
So it won't automagically work with any color behind the element; you need to have it stored as a CSS variable. | ||
|
||
LCH has three properties: lightness, chroma and hue. | ||
|
||
- Lightness is a number between `0` and `100` representing how bright a color is. `0` corresponds to black, while `100` corresponds to white. | ||
- Chroma is a technically unbounded number that represents "how much" color is present. | ||
- Hue is an angle that represents the hue angle, as in HSL or HSV. | ||
|
||
You supply them to the `lch` function like this: | ||
|
||
```css | ||
.magic { | ||
--lightness: 50; | ||
--chroma: 72.2; | ||
--hue: 56.2; | ||
color: lch(var(--lightness) var(--chroma) var(--hue)); | ||
} | ||
``` | ||
|
||
When used with relative color syntax, the color gets "broken up" into its constituent parts which are referred to by `l`, `c` and `h`. So, for example, this would set the background color to the same as the text color; `l`, `c` and `h` take their values from the `from` color and are placed in the appropriate slots unmodified. | ||
|
||
```css | ||
.magic { | ||
--bg: red; | ||
background: var(--bg); | ||
color: lch(from var(--bg) l c h); | ||
} | ||
``` | ||
|
||
Devon's example makes two big changes: | ||
|
||
1. It discards the chroma and hue values, replacing them with `0`. | ||
2. It inverts the color's lightness and multiplies it by `infinity` to obtain white or black. | ||
|
||
#2 might be confusing, so let's dig into some examples. Remember, the calculation is `(49.44 - l) * infinity`, clamped within the range `[0, 100]`: | ||
|
||
- CSS `red` has an LCH lightness of `54.29`. | ||
1. `49.44` - `54.29` = `-4.85` | ||
2. `-4.85` \* `infinity` = `-infinity` | ||
3. `-infintiy` gets clamped to `0` (black) | ||
- CSS `blue` has an LCH lightness of `29.57`. | ||
1. `49.44` - `29.57` = `19.87` | ||
2. `19.87` \* `infinity` = `-infinity` | ||
3. `infintiy` gets clamped to `100` (white) | ||
- CSS `white` has an LCH lightness of `100`. | ||
1. `49.44` - `100` = `-50.56` | ||
2. `-4.85` \* `infinity` = `-infinity` | ||
3. `-infintiy` gets clamped to `0` (black) | ||
|
||
Why `49.44`? Devon tested it with all RGB colors and found it had the least number of WCAG 4.5:1 contrast failures. |