Color in CSS Or: How I Learned to Disrespect Tennis

A presentation at Beyond Tellerrand in November 2024 in Berlin, Germany by Manuel Matuzovic

Slide 1

Slide 1

matuzo.social

Slide 2

Slide 2

matuzo.social

Slide 3

Slide 3

matuzo.social S S C n i r o l o C

Slide 4

Slide 4

matuzo.social

Slide 5

Slide 5

matuzo.social

Slide 6

Slide 6

matuzo.social smashingconf.com/online-workshops/workshops/modern-css-manuel-matuzovic

Slide 7

Slide 7

tt matuzo.social container style queries scroll-driven animations lab()/oklab() display-p3 relative color syntax s of syntax container queries :is() / :where() transition-behavior nesting @property @starting-style scoping color-mix() color fonts color spaces :has() initial-le er logical properties subgrid text-wrap viewport units min() max() clamp() color() view transitions @media range syntax sin() cos() tan() gamuts lch()/oklch() light-dark()

Slide 8

Slide 8

tt matuzo.social container style queries scroll-driven animations lab()/oklab() display-p3 relative color syntax s of syntax container queries :is() / :where() transition-behavior nesting @property @starting-style scoping color-mix() color fonts color spaces :has() initial-le er logical properties subgrid text-wrap viewport units min() max() clamp() color() view transitions @media range syntax sin() cos() tan() gamuts lch()/oklch() light-dark()

Slide 9

Slide 9

matuzo.social display-p3 @media (color-gamut) conic-gradients color spaces @media (forced-colors) color-scheme gamuts @media (prefers-contrast) space-separated syntax relative color syntax @media(prefers-reduced- currentColor color-mix() transparency) system colors lch()/oklch() hue interpolation transparent lab()/oklab() perceptual uniform named colors color fonts accent-color hwb() color() gamut mapping … light-dark() gamut clipping

Slide 10

Slide 10

matuzo.social 1 2 3

<body bgcolor=”#00300” text=”#FFFFFF” alink=”#FFFFFF”> … </body>

Slide 11

Slide 11

matuzo.social

Slide 12

Slide 12

matuzo.social 1 body { 2 3 background-color: #003300; }

Slide 13

Slide 13

matuzo.social

Slide 14

Slide 14

matuzo.social

Slide 15

Slide 15

matuzo.social 1 body { 2 3 background-image: linear-gradient(45deg, #030, #FFF); }

Slide 16

Slide 16

matuzo.social 1 body { 2 3 background-color: rgba(0, 51, 0, 0.5); }

Slide 17

Slide 17

matuzo.social #D04A6D

Slide 18

Slide 18

matuzo.social rgb(208, 74, 109)

Slide 19

Slide 19

matuzo.social youtu.be/eqZqx6lRPe0

Slide 20

Slide 20

matuzo.social 1 body { 2 3 background-color: hsl(120deg, 100%, 10%); }

Slide 21

Slide 21

matuzo.social smashingmagazine.com/2021/07/hsl-colors-css

Slide 22

Slide 22

matuzo.social 1 body { 2 3 background-color: hsl(120deg, 100%, 10%); }

Slide 23

Slide 23

matuzo.social en.wikipedia.org/wiki/HSL_and_HSV

Slide 24

Slide 24

matuzo.social 1 .notification { 2 —background-color: hsl(206deg 74% 90%); 3 —border-color: hsl(206deg 74% 70%); 4 5 background-color: var(—background-color); 6 border: 2px solid var(—border-color); 7 }

Slide 25

Slide 25

matuzo.social 1 .notification { 2 —h: 206deg; 3 —s: 74%; 4 —l: 90%; 5 6 —background-color: hsl(var(—h) var(—s) var(—l)); 7 —border-color: hsl(var(—h) var(—s) calc(var(—l) - 20%)); 8 9 10 11 } background-color: var(—background-color); border: 2px solid var(—border-color);

Slide 26

Slide 26

matuzo.social 1 2 3

<div role=”status” class=”notification” style=”—h: 40deg;”> <strong>Warning:</strong> You dropped the ball. </div> 4 5 6 7 <div role=”status” class=”notification” style=”—h: 0deg;”> <strong>Error:</strong> This is a tennis racket. </div>

Slide 27

Slide 27

matuzo.social

Slide 28

Slide 28

matuzo.social SUCKS! 👎

Slide 29

Slide 29

matuzo.social GAMUT

Slide 30

Slide 30

matuzo.social en.wikipedia.org/wiki/Gamut

Slide 31

Slide 31

matuzo.social developer.chrome.com/docs/css-ui/access-colors-spaces

Slide 32

Slide 32

matuzo.social COLOR SPACES

Slide 33

Slide 33

matuzo.social en.wikipedia.org/wiki/HSL_and_HSV

Slide 34

Slide 34

matuzo.social en.wikipedia.org/wiki/HSL_and_HSV

Slide 35

Slide 35

matuzo.social cdpn.io/pen/debug/zdgXJj

Slide 36

Slide 36

matuzo.social cdpn.io/pen/debug/zdgXJj

Slide 37

Slide 37

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 38

Slide 38

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 39

Slide 39

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 40

Slide 40

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 41

Slide 41

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 42

Slide 42

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 43

Slide 43

matuzo.social hsl([x]deg, 100%, 50%) wildbit.com/blog/accessible-palette-stop-using-hsl-for-color-systems

Slide 44

Slide 44

matuzo.social 1 <button class=”btn”>Send</button> 2 3 <button class=”btn btn—success”>Save</button>

Slide 45

Slide 45

matuzo.social 1 .btn { 2 —h: 270; 3 —s: 100%; 4 —l: 50%; 5 6 background-color: hsl(var(—h) var(—s) var(—l)); 7 color: hsl(0 100% 100%); 8 } 9 10 .btn—success { 11 12 }

Slide 46

Slide 46

matuzo.social 1 .btn—success { 2 3 —h: 90; }

Slide 47

Slide 47

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 48

Slide 48

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 49

Slide 49

matuzo.social lea.verou.me/blog/2020/04/lch-colors-in-css-what-why-and-how

Slide 50

Slide 50

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 51

Slide 51

matuzo.social commons.wikimedia.org/wiki/File:Visible_gamut_within_CIELAB_color_space_D65_whitepoint_mesh.webm

Slide 52

Slide 52

matuzo.social evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl

Slide 53

Slide 53

matuzo.social evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl

Slide 54

Slide 54

matuzo.social evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl

Slide 55

Slide 55

matuzo.social cdpn.io/pen/pen/zYeXvXM

Slide 56

Slide 56

matuzo.social cdpn.io/pen/debug/rNRoBXO

Slide 57

Slide 57

matuzo.social github.com/w3c/csswg-drafts/issues/9449

Slide 58

Slide 58

matuzo.social

Slide 59

Slide 59

matuzo.social w3.org/TR/css-color-4/#GM-hue-curvature

Slide 60

Slide 60

matuzo.social w3.org/TR/css-color-4/#GM-hue-curvature

Slide 61

Slide 61

matuzo.social bottosson.github.io/posts/oklab

Slide 62

Slide 62

matuzo.social

Slide 63

Slide 63

matuzo.social

Slide 64

Slide 64

matuzo.social 1 body { 2 background-color: lch(17 34.86 136.89); 3 background-color: lab(17 -25.45 23.82); 4 5 background-color: oklch(0.28 0.09 142.49); 6 background-color: oklab(0.28 -0.08 0.06); 7 } 111 111 113 15.4

Slide 65

Slide 65

matuzo.social oklch() l: lightness: 0% to 100% or 0 to 1 c: chroma 0 and 0.37. h: 0deg to 360deg. / a: 0 to 1 e.g., oklch(0.73 0.2 352)

Slide 66

Slide 66

matuzo.social oklch() oklab() l: lightness: 0% to 100% or 0 to 1 l: lightness: 0% to 100% or 0 to 1 c: chroma 0 and 0.37. a: -0.4 to 0.4 h: 0deg to 360deg. b: -0.4 to 0.4 / a: 0 to 1 / a: 0 to 1 e.g., oklch(0.73 0.2 352) e.g., oklab(0.73 0.2 -0.03)

Slide 67

Slide 67

oklch.com

Slide 68

Slide 68

oklch.com

Slide 69

Slide 69

matuzo.social 1 .btn { 2 —h: 270; 3 —s: 100%; 4 —l: 50%; 5 background-color: hsl(var(—h) var(—s) var(—l)); 6 } 7 8 .btn—success { 9 10 } —h: 90;

Slide 70

Slide 70

matuzo.social 1 .btn { 2 —l: 39.29%; 3 —c: 120.99; 4 —h: 308.07; 5 background-color: lch(var(—l) var(—c) var(—h)); 6 } 7 8 .btn—success { 9 10 } —h: 135.84;

Slide 71

Slide 71

matuzo.social SPACE-SEPARATED FUNCTIONAL COLOR NOTATIONS

Slide 72

Slide 72

matuzo.social 1 div:is():not(button) { 2 background-color: rgb(255, 0, 0); 3 background-color: rgba(255, 0, 0, 0.5); 4 5 background-color: rgb(255 0 0); 6 background-color: rgb(255 0 0 / 0.5); 7 8 background-color: oklch(0.63 0.26 29.23); 9 background-color: oklch(0.63 0.26 29.23 / 0.5); 10 } 65 79 52 12.1

Slide 73

Slide 73

matuzo.social COLOR()

Slide 74

Slide 74

matuzo.social 1 .valid-css-color-function-colors { 2 —srgb: color(srgb 1 1 1); 3 —srgb-linear: color(srgb-linear 100% 100% 100% / 50%); 4 —display-p3: color(display-p3 1 1 1); 5 —rec2020: color(rec2020 0 0 0); 6 —a98-rgb: color(a98-rgb 1 1 1 / 25%); 7 —prophoto: color(prophoto-rgb 0% 0% 0%); 8 —xyz: color(xyz 1 1 1); 9 } 111 111 113 15

Slide 75

Slide 75

matuzo.social 1 :root { 2 3 —primary: #FF0000; } 4 5 body { 6 7 background-color: var(—primary); } 8 9 @media (color-gamut: p3) { 10 :root { 11 12 —primary: oklch(0.63 0.26 29.23); } 13 } 14 15 @media (color-gamut: rec2020) { 16 :root { 17 18 —primary: color(rec2020 1 0 0); } 19 } 58 79 110 10

Slide 76

Slide 76

matuzo.social GRADIENTS

Slide 77

Slide 77

matuzo.social 1 div { 2 3 background-image: linear-gradient(to right, hotpink, aqua); }

Slide 78

Slide 78

matuzo.social 1 div { 2 3 background-image: linear-gradient(to right in oklab, hotpink, aqua); } 111 111 127 16.2

Slide 79

Slide 79

matuzo.social

Slide 80

Slide 80

matuzo.social nerdy.dev/gradients-going-the-shorter-longer-increasing-or-decreasing-route

Slide 81

Slide 81

matuzo.social 1 .shorter { 2 3 background-image: linear-gradient(to right in oklch shorter hue, hotpink, aqua); } 4 5 .longer { 6 7 background-image: linear-gradient(to right in oklch longer hue, hotpink, aqua); } 111 111 127 16.2

Slide 82

Slide 82

1 background-image: linear-gradient(to right in oklch shorter hue, hotpink, aqua); 1 background-image: linear-gradient(to right in oklch longer hue, hotpink, aqua);

Slide 83

Slide 83

matuzo.social COLOR-MIX()

Slide 84

Slide 84

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, hotpink, aqua); } 111 111 113 16.2

Slide 85

Slide 85

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, hotpink, aqua); } 111 111 113 16.2

Slide 86

Slide 86

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, 70% hotpink, 30% aqua); } 111 111 113 16.2

Slide 87

Slide 87

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, 70% hotpink, 30% aqua); } 111 111 113 16.2

Slide 88

Slide 88

matuzo.social

Slide 89

Slide 89

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, 13% hotpink, 12% aqua); } 111 111 113 16.2

Slide 90

Slide 90

matuzo.social 1 div { 2 3 background-color: color-mix(in srgb, 13% hotpink, 12% aqua); } 111 111 113 16.2

Slide 91

Slide 91

matuzo.social

Slide 92

Slide 92

cdpn.io/pen/debug/JjBZLrm

Slide 93

Slide 93

cdpn.io/pen/debug/JjBZLrm

Slide 94

Slide 94

matuzo.social RELATIVE COLOR SYNTAX

Slide 95

Slide 95

matuzo.social 1 div { 2 3 background-color: oklch(from #00F l c h); } 119 119 128 16.4

Slide 96

Slide 96

matuzo.social 1 :root { 2 3 —color: #00F; } 4 5 div { 6 7 background-color: oklch(from var(—color) l c 150); } 119 119 128 16.4

Slide 97

Slide 97

matuzo.social 1 :root { 2 3 —color: #00F; } 4 5 div { 6 7 background-color: hsl(from var(—color) h s calc(l - 10%)); } 119 119 128 16.4

Slide 98

Slide 98

matuzo.social 1 :root { 2 background-color: rgb(from var(—color) 3 calc(r * .3 + g * .59 + b * .11) 4 calc(r * .3 + g * .59 + b * .11) 5 calc(r * .3 + g * .59 + b * .11)); 6 } 119 119 128 16.4

Slide 99

Slide 99

matuzo.social 1 .notification { 2 —h: 206deg; 3 —s: 74%; 4 —l: 90%; 5 6 —background-color: hsl(var(—h) var(—s) var(—l)); 7 —border-color: hsl(var(—h) var(—s) calc(var(—l) - 20%)); 8 9 10 11 } background-color: var(—background-color); border: 2px solid var(—border-color);

Slide 100

Slide 100

matuzo.social 1 :root { 2 —color-info: oklch(0.92 0.03 240.82); 3 —color-warning: oklch(from var(—color-info) l c 84.52); 4 —color-error: oklch(from var(—color-info) l c 17.98); 5 } 6 7 .notification { 8 —_color: var(—color, var(—color-info)); 9 10 —background-color: var(—_color); 11 —border-color: oklch(from var(—_color) calc(l - 0.1) calc(c + 0.06) h); 12 13 background-color: var(—background-color); 14 border: 2px solid var(—border-color); 15 }

Slide 101

Slide 101

matuzo.social 1 :root { 2 —color-info: oklch(0.92 0.03 240.82); 3 —color-warning: oklch(from var(—color-info) l c 84.52); 4 —color-error: oklch(from var(—color-info) l c 17.98); 5 } 6 7 .notification { 8 —_color: var(—color, var(—color-info)); 9 10 —background-color: var(—_color); 11 —border-color: oklch(from var(—_color) calc(l - 0.1) calc(c + 0.06) h); 12 13 background-color: var(—background-color); 14 border: 2px solid var(—border-color); 15 }

Slide 102

Slide 102

matuzo.social 1 :root { 2 —color-info: oklch(0.92 0.03 240.82); 3 —color-warning: oklch(from var(—color-info) l c 84.52); 4 —color-error: oklch(from var(—color-info) l c 17.98); 5 } 6 7 .notification { 8 —_color: var(—color, var(—color-info)); 9 10 —background-color: var(—_color); 11 —border-color: oklch(from var(—_color) calc(l - 0.1) calc(c + 0.06) h); 12 13 background-color: var(—background-color); 14 border: 2px solid var(—border-color); 15 }

Slide 103

Slide 103

matuzo.social COLOR FONTS

Slide 104

Slide 104

matuzo.social

Slide 105

Slide 105

matuzo.social

Slide 106

Slide 106

matuzo.social 1 @font-palette-values —pink { 2 font-family: ‘Rocher’; 3 base-palette: 1; 4 } 5 6 @font-palette-values —green { 7 font-family: ‘Rocher’; 8 base-palette: 2; 9 } 10 11 @font-palette-values —gray { 12 font-family: ‘Rocher’; 13 base-palette: 9; 14 } 15 16 h1 { 17 font-palette: —pink; 18 } 101 101 107 15.4

Slide 107

Slide 107

wakamaifondue.com

Slide 108

Slide 108

wakamaifondue.com

Slide 109

Slide 109

matuzo.social 1 @font-palette-values —gray { 2 font-family: ‘Rocher’; 3 base-palette: 9; 4 5 6 override-colors: 3 rgb(21 58 81); } 101 101 107 15.4

Slide 110

Slide 110

matuzo.social 1 @font-palette-values —custom { 2 font-family: ‘Rocher’; 3 base-palette: 0; 4 5 override-colors: 6 0 hotpink, 7 1 red, 8 2 rebeccapurple, 9 3 aqua; 10 } 101 101 107 15.4

Slide 111

Slide 111

matuzo.social

Slide 112

Slide 112

matuzo.social COLOR SCHEMES

Slide 113

Slide 113

Slide 114

Slide 114

Slide 115

Slide 115

matuzo.social 1 :root { 2 3 color-scheme: light dark; } 81 81 96 13

Slide 116

Slide 116

matuzo.social 1 :root { 2 color-scheme: light dark; 3 } 1

<head> 2 3 <meta name=”color-scheme” content=”light dark”> </head> 81 81 96 13

Slide 117

Slide 117

Slide 118

Slide 118

Slide 119

Slide 119

matuzo.social 1 :root { 2 —light: pink; 3 —dark: hotpink; 4 5 6 color-scheme: light dark; } 7 8 input { 9 10 —accent-color: var(—dark); accent-color: var(—accent-color); 11 } 12 13 @media(prefers-color-scheme: dark) { 14 input { 15 16 —accent-color: var(—light); } 17 } 76 79 67 12.1

Slide 120

Slide 120

matuzo.social 1 :root { 2 —light: pink; 3 —dark: hotpink; 4 5 6 color-scheme: light dark; } 7 8 input { 9 10 —accent-color: light-dark(var(—dark), var(—light)); accent-color: var(—accent-color); 11 } 123 123 120 17.5

Slide 121

Slide 121

Slide 122

Slide 122

Slide 123

Slide 123

matuzo.social css-tricks.com/come-to-the-light-dark-side/

Slide 124

Slide 124

matuzo.social MEDIA FEATURES

Slide 125

Slide 125

matuzo.social 1 dialog { 2 background: oklch(1 0 0 / var(—bg-opacity, 1)); 3 backdrop-filter: blur(5px); 4 } 5 6 @media(prefers-reduced-transparency: no-preference) { 7 dialog { 8 9 —bg-opacity: 0.2; } 10 } 118 118

Slide 126

Slide 126

matuzo.social

Slide 127

Slide 127

matuzo.social

Slide 128

Slide 128

matuzo.social 1 @media (prefers-contrast: more) { 2 :root { 3 —text-color: oklch(1 0 0); 4 5 } } 96 96 101 14.1

Slide 129

Slide 129

matuzo.social kilianvalkhof.com/2023/css-html/i-no-longer-understand-prefers-contrast

Slide 130

Slide 130

matuzo.social 1 @media (forced-colors: active) { 2 button { 3 border: 2px solid ButtonText; 4 5 } } 89 79 89 16

Slide 131

Slide 131

matuzo.social

Slide 132

Slide 132

matuzo.social blog.kizu.dev/querying-the-color-scheme/

Slide 133

Slide 133

matuzo.social lea.verou.me/blog/2024/contrast-color/

Slide 134

Slide 134

matuzo.social ❤ ! u o y k n a h T accessibility-cookbook.com matuzo.social matuzo.at htmhell.dev manuel@matuzo.at