Цвета в CSS

София Валитова, CSS-инженер

Меня зовут София. Я - CSS инженер.

Мы будем говорить:

Начнём с предыстории

Приходим мы в проект...

#5181b8 lch(52.4% 35. 263) #6186b5 hsl(205, 47%, 49%) #5381b8 lch(54% 29 263) #5081b9 #5282b8 rgb(83, 148, 184) #5084b8 hsl(218, 63%, 59%) #5383b8

Одинаковые, но разные 😭 😭 😭

Личная история

~6500 цветов

Хотим сделать хорошо

Хорошо - это как?

Что такое палитра?

            :root {
              --accent: #5181b8
              --text: #000000
              --light: #a1c1d8
              --dark: #012168
            }
        

- ограниченный набор цветов интерфейса

цвета из кода

#5181b8
hsl(214, 36%, 55%)
rgb(88, 117, 181)
black
#000
#a9c1d8
lch(76.27% 16 244)
rgb(30, 33, 104)
#012168

цвета из палитры

#5181b8
#000800
#a1c1d8
#012168

Какие цвета у нас есть в коде?

1. HEX

# ff ff ff

Какие цвета у нас есть в коде?

1. HEX

6 символов:

#ffffff

8 символов:

#ffffffff

3 символа:

#fff

4 символа:

#ffff

HEX

6 символов:

# ff ff ff

16*16* 16*16* 16*16

16777216 оттенков

3 символа:

# f f f

16* 16* 16

4096 оттенков

Какие цвета у нас есть в коде?

2. RGB() RGBA()

rgb( 123, 234, 110)

rgb( 123, 234, 110, 0.7)

rgb( 123 234 110)

rgb( 123 234 110 / 0.7)

RGB - разные синтаксисы

Новый синтаксис – color: rgb ( R G B / A );
color: rgb ( R G B );

Средний синтаксис – color: rgb ( R, G, B );
color: rgb ( R, G, B, A );

Старый синтаксис – color: rgb ( R, G, B );
color: rgba ( R, G, B, A );

RGB + RGBA

Сложно настраивать руками

Какие цвета у нас есть в коде?

4. именованные цвета

color: black;

background-color: white;

Какие цвета у нас есть в коде?

4. именованные цвета - 148 цветов

Какие цвета у нас есть в коде?

5. возможно, даже ... HSL() HSLA()

Hue

Тон цвета, представленный в виде угла цветового круга.

Saturation

Насыщенность цвета

Lightness

«Светлота» цвета

HSL - разные синтаксисы

Новый синтаксис – color: hsl ( H S L / A );
color: hsl ( H S L );

Средний синтакис – color: hsl ( H, S, L );
color: hsl ( H, S, L, A );

Старый синтакис – color: hsl ( H S L );
color: hsla ( H, S, L, A );

HSL

Просто настраивать руками

Как привести их все к палитре?

цвета из кода

#5181b8
hsl(214, 36%, 55%)
rgb(88, 117, 181)
#a9c1d8
lch(76.27% 16 244)

цвета из палитры

#5181b8
#a1c1d8

Нужно как-то соотнести. Желательно автоматически.

Как мы можем сравнить два цвета?

Расстояние между двумя точками в пространстве:

3 (r2 + g2 + b2)

Как мы можем сравнить два цвета?

Почему не rgb-куб:

rgb(100, 100, 100) rgb(200, 100, 100) r = 100

rgb(100, 100, 100) rgb(100, 200, 100) r = 100

rgb(100, 100, 100) rgb(100, 100, 200) r = 100

Одинаковое расстояние - разное визуальное изменение.

Как мы можем сравнить два цвета?

Lab

LAB()

Система, равномерная для человеческого глаза

Lightness

CIE-яркость

Может быть больше 100%!

B

От синего до желтого

A

От красного до зеленого

Как мы можем сравнить два цвета?

Считаем расстояние и заменяем

3 (l2 + c2 + h2)

Технически - colord

Технически - colord

            import { colord, extend } from "colord";

            import labPlugin from "colord/plugins/lab";

            extend([labPlugin]);
            const result = colord('#5181b8').toLab();
            // => { l: 73.13, a: 24.49,b: 63.71, alpha: 1 }
        

Технически

            const lab1 = { l: 22.56, a: 34.74, b: 62.97, alpha: 1 };
            const lab2 = { l: 52.37, a: -4.25, b: -34.48, alpha: 1 };
            const D = Math.sqrt(
            (lab1.l - lab2.l)^2 +
            (lab1.a - lab2.a)^2 +
            (lab1.b - lab2.b)^2 +
            (lab1.alpha - lab2.alpha)^2
            );
        

Технически

            colors.forEach((color) => {
            const result = palette.reduce((acc, paletteColor) => {
            const d = distance(color, paletteColor);
            if(d < acc.d) { /* заменяем */  }
            return acc;
            }, {name: null, d: Infinity } )
                }
        

Технически - postCSS

            root.walkDecls(declaration => {
            /* определяем, что это цвет */
            /* подбираем нужный из палитры */
            declaration.value = newRightColor;
            });
postcss style.css --replace

Хотим сделать хорошо

Хорошо - это как?

Простой интерфейс

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках, о том, как котики мурлыкают и делают ке-ке-ке.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

Разберем его на переменные цвета

--text_header + --bg_main

--text_primary + --bg_main

--text_reverse + --accent-cover-primary

--text_primary + --accent-cover-secondary

--text_reverse + --accent-cover-primary

--text_primary + --accent-cover-secondary

Разберем его на переменные цвета

--text_header: hsl(123, 60%, 30%) + --bg_main: hsl(123, 0%, 100%)

--text_primary: hsl(123, 0%, 2%) + --bg_main: hsl(123, 0%, 100%)

--text_reverse: hsl(123, 0%, 98%) + --accent-cover-primary: hsl(123, 60%, 60%)

--text_primary: hsl(123, 0%, 2%) + --accent-cover-secondary: hsl(123, 60%, 90%)

--text_reverse: hsl(123, 0%, 98%) + --accent-cover-primary: hsl(123, 60%, 60%)

--text_primary: hsl(123, 0%, 2%) + --accent-cover-secondary: hsl(123, 60%, 90%)

Дальше оперируем переменными

--bg_main: hsl(123, 0%, 100%)
--text_header: hsl(123, 60%, 30%)
--text_primary: hsl(123, 0%, 2%)
--text_reverse: hsl(123, 0%, 98%)
--accent-cover-primary: hsl(123, 60%, 60%)
--accent-cover-secondary: hsl(123, 60%, 90%)

Одинаковый цветовой угол

--bg_main: hsl(
--text_header: hsl(
--text_primary: hsl(
--text_reverse: hsl(
--accent-cover-primary: hsl(
--accent-cover-secondary: hsl(
123
123
123
123
123
123
60%, 100%)
0%, 30%)
0%, 2%)
0%, 98%)
60%, 60%)
60%, 90%)

Выделим в отдельную переменную

--hue: 123;
--accent_hs: var(--hue), 60%;
--normal_hs: var(--hue), 0%;
--bg_main: hsl(
--text_header: hsl(
--text_primary: hsl(
--text_reverse: hsl(
--accent-cover-primary: hsl(
--accent-cover-secondary: hsl(
var(--accent_hs)
var(--normal_hs)
var(--normal_hs)
var(--normal_hs)
var(--accent_hs)
var(--accent_hs)
100%)
30%)
2%)
98%)
60%)
90%)

Цветовая схема зависит только от акцентного цвета

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках, о том, как котики мурлыкают и делают ке-ке-ке.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

Простая цветовая схема

Управляем цветами через одну ручку!

--hue: 123;

--accent_hs: var(--hue), 60%;
--normal_hs: var(--hue), 0%

Хотим сделать хорошо

Хорошо – это как?

Вернемся к простому дизайну

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках, о том, как котики мурлыкают и делают ке-ке-ке.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

Темная тема - это не светлая

Это значит, что в HSL мы будем оперировать светловой.

hsl(123, 50%, 80%) - светлый цвет

hsl(123, 50%, 20%) - темный цвет

Вернемся к схеме

--hue: 123;
--accent_hs: var(--hue), 60%;
--normal_hs: var(--hue), 0%
--bg_main: hsl(
--text_header: hsl(
--text_primary: hsl(
--text_reverse: hsl(
--accent-cover-primary: hsl(
--accent-cover-secondary: hsl(
var(--accent_hs)
var(--normal_hs)
var(--normal_hs)
var(--normal_hs)
var(--accent_hs)
var(--accent_hs)
100%)
30%)
2%)
98%)
60%)
90%)

Вернемся к схеме и вынесем светлоту

--bg_main: hsl(
--text_header: hsl(
--text_primary: hsl(
--text_reverse: hsl(
--accent-cover-primary: hsl(
--accent-cover-secondary: hsl(
var(--accent_hs)
var(--normal_hs)
var(--normal_hs)
var(--normal_hs)
var(--accent_hs)
var(--accent_hs)
var(--l-100))
var(--l-30))
var(--l-2))
var(--l-98))
var(--l-60))
var(--l-90))

Вернемся к схеме и вынесем светлоту

                    [light] {
                      --l-100: 100%;
                      --l-30: 30%;
                      --l-98: 98%;
                      --l-2: 2%;
                      --l-60: 60%;
                      --l-90: 90%;
                    }
                    
                    [dark] {
                      --l-100: 0%;
                      --l-30: 70%;
                      --l-98: 2%;
                      --l-2: 98%;
                      --l-60: 40%;
                      --l-90: 10%;
                    }
                

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках, о том, как котики мурлыкают и делают ке-ке-ке.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

Почему не hsl?

1. Светлота не имеет ничего общего с визуальной яркостью.

hsl(40 100% 50%)
hsl(160 100% 50%)

Почему не hsl?

2. Цветовой угол не однородный.

hsl(30 100% 50%)
hsl(50 100% 50%)
hsl(230 100% 50%)
hsl(250 100% 50%)

LCH()

CIE LAB в полярных координатах

HUE

Угол цветового круга (другого!)

Chroma

Количество цвета

Lightness

CIE яркость

Повторим по другому

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках, о том, как котики мурлыкают и делают ке-ке-ке.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

Повторим по другому

--text_header: lch(50% 50 123) + --bg_main: lch(100% 0 123)

--text_primary: lch(2% 50 123) + --bg_main: lch(100% 0 123)

--text_reverse: lch(98% 0 123) + --accent-cover-primary: lch(60% 50 123)

--text_primary: lch(2% 0 123) + --accent-cover-secondary: lch(90% 50 123)

--text_reverse: lch(98% 0 123) + --accent-cover-primary: lch(60% 50 123)

--text_primary: lch(2% 0 123) + --accent-cover-secondary: lch(90% 50 123)

Повторим по другому

--hue: 123;
--accent_ch: 40 var(--hue);
--normal_ch: 0 var(--hue);
--bg_main: lch(
--text_header: lch(
--text_primary: lch(
--text_reverse: lch(
--accent-cover-primary: lch(
--accent-cover-secondary: lch(
var(--l-100)
var(--l-30)
var(--l-2)
var(--l-98)
var(--l-60)
var(--l-90)
var(--accent_ch));
var(--normal_ch));
var(--normal_ch));
var(--normal_ch));
var(--accent_ch));
var(--accent_ch));

Большой заголовок

Маленький текст о том, что это за блог

Заголовок поста блога

Довольно длинный текст поста, тут можно поговорить о котиках, о мягких котячьих лапках, о котиьх ушках.

Заголовок второго поста

Длинный текст еще одного поста, теперь можно поговорить о коржиках! Коржики пожожи на булки.

HSL

Маленький текст о том, что это за блог

Заголовок поста блога

Маленький текст поста.

Заголовок второго поста

Маленький текст поста.

LCH

Маленький текст о том, что это за блог

Заголовок поста блога

Маленький текст поста.

Заголовок второго поста

Маленький текст поста.

Все ли так хорошо?

Что мы видим? (подробнее)

Цветовой охват

- множество цветов, которое может отобразить монитор.

sRGB (люминофоры)

sRGB

rgb()
hsl()
hex

P3

P3 как задать цвет?

color( sRGB 1 1 1 );

P3 как задать цвет?

color(sRGB 1 0.5 0.3)
color(sRGB 0.4 0.6 0.1)
color(sRGB 0.1 0.1 0.3)
color(sRGB 0.7 0.5 0.8)

color() - разные пространства

color(sRGB 0 1 0)
color(display-P3 0 1 0)
color(sRGB 1 0 0)
color(display-P3 1 0 0)

color() - разные пространства

color() - разные пространства

P3 - пользоваться осторожно

привычно
ОЧЕНЬ ЯРКО
привычно
ОЧЕНЬ ЯРКО

Идеально для ярких единичных акцентов для пользователей маков.

Акценты для apple

            p {
              color: #00c2c9;
            }
            @supports (color: lch(0% 0 0)) {
              p {
               color: lch(70% 50 200);
              }
            }
        

Относительный колориметрический метод

lch(50% 80 270)

Относительный колориметрический метод

Мораль: проверяйте на разных экранах

Хотим сделать хорошо

Хорошо - это как?

Еще несколько удобных вещей в будущем

Хотим сделать хорошо

Хорошо - это как?

Удобные цвета для состояний

            :root {
              --button_bg: hsl(200 50% 50%)
              --button_bg_hover: hsl(200 50% 60%)
              --button_bg_active: hsl(200 50% 40%)
            }
        
-> hover -> -> active ->

Удобные цвета для состояний

            :root {
              --common_l: 50%;
              --hover_l: calc(var(--common_l) + 10%);
              --active_l: calc(var(--common_l) - 10%);
              --button_bg: hsl(200 50% var(--common_l));
              --button_bg_hover: hsl(200 50% var(--hover_l));
              --button_bg_active: hsl(200 50% var(--active_l));
            }
        

Удобные цвета для состояний

            :root {
              --common_l: 50%;
              --hover_l_diff: 10%;  --active_l_diff: -10%;
              --button_bg: hsl(200 50% var(--common_l));
              --button_bg_hover: 
                   hsl(200 50% calc(var(--common_l) + var(--hover_l_diff)));
              --button_bg_active:
                   hsl(200 50% calc(var(--common_l) + var(--active_l_diff)));
            }
        

Удобные цвета для состояний

            :root {
              --button_bg: hsl(200 50% 50%);
              --button_bg_hover: 
                   hsl(from var(--button_bg) h s calc(l + 10%));
              --button_bg_active:
                   hsl(from var(--button_bg) h s calc(l - 10%));
            }
        

Относительный синтаксис

hsl(from red h s l);
-> hsl(from red calc(h + 100) s l);
-> hsl(from red h calc(s - 50%) l);
-> hsl(from red h s calc(l + 20%));

Относительный синтаксис цветов

Относительный синтаксис цветов

Можно полифиллить, но не в рантайме.

Пока лучше так 😭

            :root {
              --common_l: 50%;
              --hover_l_diff: 10%;  --active_l_diff: -10%;
              --button_bg: hsl(200 50% var(--common_l));
              --button_bg_hover: 
                   hsl(200 50% calc(var(--common_l) + var(--hover_l_diff)));
              --button_bg_active:
                   hsl(200 50% calc(var(--common_l) + var(--active_l_diff)));
            }
        

Хотим сделать хорошо

Текст на пользовательском фоне

много много текста
много много текста

Текст на пользовательском фоне

В этой презентации много примеров цвета.

lch(50% 50 100)
lch(30% 80 250)
lch(80% 70 300)
<div class="color" style="--c: #fff"> #fff </div>

Текст на пользовательском фоне

            .color {
              background-color: var(--c);
              height: 1em;
              min-width: 1em;
              ...
            }
        

Текст на пользовательском фоне

            .color {
              background-color: var(--c);
              color: color-contrast(var(--c) vs #fff, #000);
              height: 1em;
              min-width: 1em;
              ...
            }
        

color-contrast()

color-contrast( <color> vs <color>#{2,} )

color-contrast()

lch(var(--l) 30 0)

Сделали хорошо!

Источники и ссылки

София Валитова из Cube

Оставить отзыв:

Презентация:

Презентация сделана с помощью Shower.