{"id":3020,"date":"2022-04-07T04:54:44","date_gmt":"2022-04-07T04:54:44","guid":{"rendered":"https:\/\/rengga.dev\/blog\/?p=3020"},"modified":"2023-06-12T14:32:04","modified_gmt":"2023-06-12T14:32:04","slug":"css-tutorial-mixing-colors-in-pure-css","status":"publish","type":"post","link":"https:\/\/rengga.dev\/blog\/css-tutorial-mixing-colors-in-pure-css\/","title":{"rendered":"CSS Tutorial &#8211; Mixing Colors in Pure CSS"},"content":{"rendered":"<p><span style=\"color: #ef3207;\"><a style=\"color: #ef3207;\" href=\"https:\/\/rengga.dev\/\" target=\"_blank\" rel=\"noopener\"><strong>Rengga Dev<\/strong><\/a> <\/span>&#8211; Red + Blue = Purple\u2026 right? Is there some way to express that in CSS? Well, not easily. There is a\u00a0<a href=\"https:\/\/www.w3.org\/TR\/css-color-5\/%23colormix\" rel=\"noopener\">proposal draft for a\u00a0<code>color-mix<\/code>\u00a0function<\/a>\u00a0and\u00a0<a href=\"https:\/\/chromestatus.com\/features%23color-mix\" rel=\"noopener\">some degree of interest<\/a>\u00a0from Chrome, but it doesn\u2019t seem right around the corner. It would be nice to have native CSS color mixing, as it would give designers greater flexibility when working with colors. One example is to create tinted variants of a single base color to form a design palette.<\/p>\n<p>But this is CSS-Tricks so let\u2019s do some CSS tricks.<\/p>\n<p>We have a\u00a0<code>calc()<\/code>\u00a0function in CSS\u00a0for manipulating numbers<em>.<\/em>\u00a0But we have very few ways to operate directly on colors, even though some color formats (e.g.\u00a0<code>hsl()<\/code>\u00a0and\u00a0<code>rgb()<\/code>) are based on numeric values.<\/p>\n<h2 id=\"h-mixing-colors-with-animation\">Mixing colors with animation<\/h2>\n<p>We can transition from one color to another in CSS. This works:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">div {\r\n  background: blue;\r\n  transition: 0.2s;\r\n}\r\ndiv:hover {\r\n  background: red; \r\n}<\/pre>\n<p>And here\u2019s that with animations:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">div {\r\n  background: blue;\r\n  transition: 0.2s;\r\n}\r\ndiv:hover {\r\n  animation: change-color 0.2s forwards;\r\n}\r\n\r\n@keyframes change-color {\r\n  to {\r\n    background: red;\r\n  }\r\n}<\/pre>\n<p>This is an keyframe animation that runs infinitely, where you can see the color moving between red and blue. Open the console and click the page \u2014 you can see that even JavaScript can tell you the current color at any exact point in the animation.<br \/>\n<iframe style=\"width: 100%;\" title=\"CSS Tutorial - Mixing Colors in Pure CSS 01\" src=\"https:\/\/codepen.io\/renggagumilar\/embed\/KKoGmwy?default-tab=result&amp;theme-id=dark\" height=\"300\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/renggagumilar\/pen\/KKoGmwy\"><br \/>\nCSS Tutorial &#8211; Mixing Colors in Pure CSS 01<\/a> by Rengga Gumilar (<a href=\"https:\/\/codepen.io\/renggagumilar\">@renggagumilar<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><br \/>\nSo what if we\u00a0<em>pause<\/em>\u00a0the animation somewhere in the middle? Color mixing works! Here is a paused animation that is\u00a0<code>0.5s<\/code>\u00a0through it\u2019s\u00a0<code>1s<\/code>\u00a0duration, so exactly halfway through:<br \/>\n<iframe style=\"width: 100%;\" title=\"CSS Tutorial - Mixing Colors in Pure CSS 02\" src=\"https:\/\/codepen.io\/renggagumilar\/embed\/eYMPWmK?default-tab=result&amp;theme-id=dark\" height=\"300\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/renggagumilar\/pen\/eYMPWmK\"><br \/>\nCSS Tutorial &#8211; Mixing Colors in Pure CSS 02<\/a> by Rengga Gumilar (<a href=\"https:\/\/codepen.io\/renggagumilar\">@renggagumilar<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><br \/>\nWe accomplished that by setting an\u00a0<code>animation-delay\u00a0<\/code>of\u00a0<code>-0.5s<\/code>. And what color is halfway between red and blue? Purple. We can adjust that\u00a0<code>animation-delay<\/code>\u00a0to specify the percentage of two colors.<br \/>\n<iframe style=\"width: 100%;\" title=\"CSS Tutorial - Mixing Colors in Pure CSS 03\" src=\"https:\/\/codepen.io\/renggagumilar\/embed\/BarqRNQ?default-tab=result&amp;theme-id=dark\" height=\"300\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/renggagumilar\/pen\/BarqRNQ\"><br \/>\nCSS Tutorial &#8211; Mixing Colors in Pure CSS 03<\/a> by Rengga Gumilar (<a href=\"https:\/\/codepen.io\/renggagumilar\">@renggagumilar<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><br \/>\nThis works for Chromium core browsers and Firefox. In Safari, you must change animation-name to force browser to recalculate the animation progress.<\/p>\n<p>This trick can also be used for adding alpha channel to a color, which is typically useful for theming.<\/p>\n<h2 id=\"h-getting-the-mixed-color-to-a-css-custom-property\">Getting the mixed color to a CSS custom property<\/h2>\n<p>This is a neat trick so far, but it\u2019s not very practical to apply an animation on any element you need to use a mixed color on, and then have to set all the properties you want to change within the\u00a0<code>@keyframes<\/code>.<\/p>\n<p>We can improve on this a smidge if we add in a couple more CSS features:<\/p>\n<ol>\n<li>Use a\u00a0<code>@property<\/code>\u00a0typed CSS custom property, so it can be created as a proper color, and thus animated as a color.<\/li>\n<li>Use a Sass\u00a0<code>@function<\/code>\u00a0to easily call keyframes at a particular point.<\/li>\n<\/ol>\n<h3>HTML<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;div id=\"inputs\"&gt;\r\n  &lt;input type=\"color\" value=\"#FF0000\" oninput=\"result.style.setProperty('--from-color', this.value)\"&gt;\r\n  &lt;input type=\"range\" min=\"0\" max=\"1\" value=\"0.5\" step=\"0.01\" oninput=\"result.style.setProperty('--percent', this.value)\"&gt;\r\n  &lt;input type=\"color\" value=\"#0000FF\" oninput=\"result.style.setProperty('--to-color', this.value)\"&gt;\r\n&lt;\/div&gt;\r\n&lt;div id=\"result\" style=\"--from-color: #FF0000; --to-color: #0000FF; --percent: .5\"&gt;\r\n  Some text.\r\n  &lt;br&gt;&lt;br&gt;\r\n  &lt;svg viewBox=\"0 0 640 512\" width=\"60\" title=\"biking\"&gt;\r\n  &lt;path d=\"M400 96a48 48 0 1 0-48-48 48 48 0 0 0 48 48zm-4 121a31.9 31.9 0 0 0 20 7h64a32 32 0 0 0 0-64h-52.78L356 103a31.94 31.94 0 0 0-40.81.68l-112 96a32 32 0 0 0 3.08 50.92L288 305.12V416a32 32 0 0 0 64 0V288a32 32 0 0 0-14.25-26.62l-41.36-27.57 58.25-49.92zm116 39a128 128 0 1 0 128 128 128 128 0 0 0-128-128zm0 192a64 64 0 1 1 64-64 64 64 0 0 1-64 64zM128 256a128 128 0 1 0 128 128 128 128 0 0 0-128-128zm0 192a64 64 0 1 1 64-64 64 64 0 0 1-64 64z\" \/&gt;\r\n&lt;\/svg&gt;\r\n&lt;\/div&gt;<\/pre>\n<h3>SCSS<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"css\">@property --result-color {\r\n  syntax: \"&lt;color&gt;\";\r\n  initial-value: transparent;\r\n  inherits: false;\r\n}\r\n@keyframes kf-color-mix {\r\n  0% { --result-color: var(--from-color) }\r\n  100% { --result-color: var(--to-color) }\r\n}\r\n\r\n@function color-mix($percent) {\r\n  @return kf-color-mix 1s calc(-1s * #{$percent}) linear forwards paused;\r\n}\r\n\r\n#result {\r\n  height: 20px;\r\n  margin-top: 10px;\r\n  padding-top: 10px;\r\n  animation: color-mix(var(--percent));\r\n  border-top: 20px solid var(--result-color);\r\n  color: var(--result-color);\r\n  fill: var(--result-color);\r\n}\r\n\r\nbody {\r\n  margin: 1em;\r\n  width: 300px;\r\n}\r\n\r\n#inputs {\r\n  display: flex;\r\n  align-items: center;\r\n}\r\n\r\n[type=color] {\r\n  width: 50px;\r\n  padding: 1px 2px;\r\n}\r\n\r\n[type=range] {\r\n  margin: 0 10px;\r\n  flex: 1;\r\n}<\/pre>\n<h3>JS<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">window.result = document.getElementById('result');\r\n\r\nresult.onclick = function() {\r\n  console.log(getComputedStyle(this).backgroundColor);\r\n};<\/pre>\n<p>&nbsp;<\/p>\n<p><iframe style=\"width: 100%;\" title=\"color-mix with css variables\" src=\"https:\/\/codepen.io\/chriscoyier\/embed\/RwRKZOW?default-tab=result&amp;theme-id=dark\" height=\"300\" frameborder=\"no\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><br \/>\nSee the Pen <a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/RwRKZOW\"><br \/>\ncolor-mix with css variables<\/a> by Chris Coyier  (<a href=\"https:\/\/codepen.io\/chriscoyier\">@chriscoyier<\/a>)<br \/>\non <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<br \/>\n<\/iframe><br \/>\nNow we still need to call animation, but the result is that a custom property is altered that we can use on any other property.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rengga Dev &#8211; Red + Blue = Purple\u2026 right? Is there some <a class=\"read-more\" href=\"https:\/\/rengga.dev\/blog\/css-tutorial-mixing-colors-in-pure-css\/\" title=\"CSS Tutorial &#8211; Mixing Colors in Pure CSS\" itemprop=\"url\"><\/a><\/p>\n","protected":false},"author":1,"featured_media":3752,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11,20,2],"tags":[197,267,103,227],"newstopic":[],"class_list":{"0":"post-3020","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-html-css","8":"category-javascript","9":"category-web-design","10":"tag-css-tutorial","11":"tag-mixing-colors-in-pure-css","12":"tag-web-design","13":"tag-web-designer"},"_links":{"self":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/comments?post=3020"}],"version-history":[{"count":3,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3020\/revisions"}],"predecessor-version":[{"id":3023,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/posts\/3020\/revisions\/3023"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media\/3752"}],"wp:attachment":[{"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/media?parent=3020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/categories?post=3020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/tags?post=3020"},{"taxonomy":"newstopic","embeddable":true,"href":"https:\/\/rengga.dev\/blog\/wp-json\/wp\/v2\/newstopic?post=3020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}