{"id":1458,"date":"2021-12-02T01:33:19","date_gmt":"2021-12-02T00:33:19","guid":{"rendered":"https:\/\/nguenkam.com\/blog\/?p=1458"},"modified":"2021-12-06T14:55:39","modified_gmt":"2021-12-06T13:55:39","slug":"how-to-build-dynamic-theme-with-css-variables","status":"publish","type":"post","link":"https:\/\/nguenkam.com\/blog\/index.php\/2021\/12\/02\/how-to-build-dynamic-theme-with-css-variables\/","title":{"rendered":"How to build dynamic theme with CSS Variables ?"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/nguenkam.com\/blog\/wp-content\/uploads\/2021\/12\/ng-theming.gif\" alt=\"\" class=\"wp-image-1459\" width=\"345\" height=\"253\"\/><\/figure>\n\n\n\n<p>The above gif is the challenge we are going to conquer. Main tasks are:<\/p>\n\n\n\n<ol><li>Have two squares, one reflecting primary color, the other reflecting secondary color.<\/li><li>Title text \u201cangular-dynamic-theme-example\u201d has primary color, and secondary color on hover.<\/li><li>Have two inputs and one save button to dynamically change the two colors.<\/li><\/ol>\n\n\n\n<h4>Approach of  solution: Put color variables in JS and bind with ngStyle<\/h4>\n\n\n\n<ol><li>Have two JS variables&nbsp;<code><em><span class=\"has-inline-color has-vivid-cyan-blue-color\">primaryColor<\/span><\/em><\/code>&nbsp;and&nbsp;<code><em><span class=\"has-inline-color has-vivid-cyan-blue-color\">secondaryColor<\/span><\/em><\/code>&nbsp;and update the two variables when save button is hit.<\/li><li>Bind the two variables to the template through&nbsp;<code><span class=\"has-inline-color has-vivid-cyan-blue-color\"><em>ngStyle<\/em><\/span><\/code><\/li><\/ol>\n\n\n\n<p><strong>Big problem of this solution \u2014 Scss files not have access to color variables, leading to:<\/strong><\/p>\n\n\n\n<p>Colors can\u2019t be applied to pseudo class or pseudo element (such as&nbsp;<code>h1::before<\/code>&nbsp;<code>div::after<\/code>&nbsp;<code>p::first-line<\/code>&nbsp;<code>a:active<\/code>&nbsp;<code>input:disabled<\/code>&nbsp;<code>button:hover<\/code>&nbsp;etc.), so task 2 can\u2019t be done.<\/p>\n\n\n\n<p><code>SCSS<\/code>&nbsp;is compiled to&nbsp;<code>CSS<\/code>&nbsp;during compile time, and SCSS variables are replaced with resolved value during compile time, which means there is no way to change the variable during run time. So, &nbsp;If you have your design system implemented with scss variables like&nbsp;<code>$primary-color<\/code>&nbsp;and&nbsp;<code>$secondary-color<\/code>&nbsp;and they are applied everywhere. You will have to deprecate them and do a big refactoring.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4><span class=\"has-inline-color has-vivid-green-cyan-color\">CSS Variables to the Rescue<\/span><\/h4>\n\n\n\n<p><em>&#8220;CSS-Variable<\/em>&#8221; is a standard which enables you to have variables in CSS. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>:root {\n  --first-color: #488cff;\n  --second-color: #ffff8c;\n} \n\ndiv {\n  background-color: var(--first-color);\n  color: var(--second-color);\n}<\/code><\/pre>\n\n\n\n<h4><strong>Difference between CSS variables and SCSS variables<\/strong><\/h4>\n\n\n\n<p>SCSS is compiled to CSS during compile time, and SCSS variables are replaced with resolved value during compile time, which means there is no way to change the variable during run time. However, CSS variables just sits there during run time, and you can dynamically CRUD them&nbsp;<strong>during run time with JavaScript (Web API).<\/strong><\/p>\n\n\n\n<h5>Example: <\/h5>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/app.component.scss\n\n$dynamic-colour: var(--dynamic-colour);\n\n:root{\n   --dynamic-colour: #5260ff;\n}\n\nh1 {\n  color: $dynamic-colour;\n}\n\n\n.block {\n  width: 100px;\n  height: 100px;\n}\n\n.primary-background {\n  background-color: $dynamic-colour;\n}<\/code><\/pre>\n\n\n\n<p>Then we can change the color dynamicaly that way :  ( With Javascript).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/app.component.ts\n  changeColor(color:string){\n   document.documentElement.style.setProperty('--dynamic-colour', color);\n}<\/code><\/pre>\n\n\n\n<p>As you can see, we can use Web API&nbsp;<code>document.documentElement.style.setProperty<\/code>&nbsp;to set CSS variables.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4>Setting up the themes<\/h4>\n\n\n\n<p>Let us create a\u00a0<code><em><strong>theme.config.ts<\/strong><\/em><\/code>\u00a0file  where we will set all the themes. We can make a static config like this or maybe get the config from an API response. The latter is the better approach if you make changes to your themes often.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\r\nexport const THEMES = {\r\n  default: {\r\n    primaryColor: 'hsl(185, 57%, 35%)',\r\n    secondaryColor: 'hsl(0, 0%, 22%)',\r\n    textOnPrimary: 'hsl(0, 0%, 100%)',\r\n    textOnSecondary: 'hsl(0, 0%, 90%)',\r\n    background: 'hsl(0, 0%, 100%)',\r\n  },\r\n  dark: {\r\n    primaryColor: 'hsl(168deg 100% 29%)',\r\n    secondaryColor: 'hsl(161deg 94% 13%)',\r\n    textOnPrimary: 'hsl(0, 0%, 100%)',\r\n    textOnSecondary: 'hsl(0, 0%, 100%)',\r\n    background: 'hsl(0, 0%, 10%)',\r\n  },\r\n  netflix: {\r\n    primaryColor: 'hsl(357, 92%, 47%)',\r\n    secondaryColor: 'hsl(0, 0%, 8%)',\r\n    textOnPrimary: 'hsl(0, 0%, 100%)',\r\n    textOnSecondary: 'hsl(0, 0%, 100%)',\r\n    background: 'hsl(0deg 0% 33%)',\r\n  },\r\n  spotify: {\r\n    primaryColor: 'hsl(132, 65%, 55%)',\r\n    secondaryColor: 'hsl(0, 0%, 0%)',\r\n    textOnPrimary: 'hsl(229, 61%, 42%)',\r\n    textOnSecondary: 'hsl(0, 0%, 100%)',\r\n    background: 'hsl(0, 0%, 100%)',\r\n  },\r\n};<\/code><\/pre>\n\n\n\n<h4>Theming service<\/h4>\n\n\n\n<p>We create a service and call it\u00a0<code><em>ThemeService<\/em><\/code>. The logic for updating the themes will be handled by this service. We can inject the service into the application and then change the theme using a function we expose from the service.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>COPY\r\nimport { DOCUMENT } from '@angular\/common';\r\nimport { Inject, Injectable } from '@angular\/core';\r\nimport { THEMES } from '..\/config\/theme.config';\r\n\r\n@Injectable({\r\n  providedIn: 'root',\r\n})\r\nexport class ThemeService {\r\n  constructor(@Inject(DOCUMENT) private document: Document) {}\r\n\r\n  setTheme(name = 'default') {\r\n    const theme = THEMES&#91;name];\r\n    Object.keys(theme).forEach((key) => {\r\n      this.document.documentElement.style.setProperty(`--${key}`, theme&#91;key]);\r\n    });\r\n  }\r\n}<\/code><\/pre>\n\n\n\n<p>How this works is basically by overriding the CSS variable values that we have defined in the\u00a0<code>styles.scss<\/code>\u00a0file.<\/p>\n\n\n\n<p>The function takes the name of the theme to apply. What it does it, get the theme variables for the selected theme from our config file and then loop through it wherein we apply the new values to the variables.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4><span class=\"has-inline-color has-vivid-red-color\">Summary<\/span><\/h4>\n\n\n\n<ol><li>CSS itself has its variable mechanism to enable you write CSS value by reference.<\/li><li>CSS variable is just there during run time while Sass variable is replaced by resolved value during compile time.<\/li><li>CSS variable can be dynamically CRUD\u2019ed during run time with JavaScript Web API. It can also be defined initially in CSS\/SCSS<\/li><\/ol>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4><strong>Additional notes<\/strong><\/h4>\n\n\n\n<ol><li>CSS variable is not natively supported by IE , but&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/www.npmjs.com\/package\/css-vars-ponyfill\" target=\"_blank\">polyfill<\/a>&nbsp;is available.<\/li><li>You can set fallback value in case the variable is not define<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>.header {\n  color: var(--header-color, blue); \n\/* if header-color isn\u2019t set, fall back to blue*\/\n}<\/code><\/pre>\n\n\n\n<p>3. CSS variable can be directly referred by SASS variable, but SASS variable cannot be directly referred by CSS variable. e.g.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ RIGHT, CSS variable can be directly referred by SASS variable\n$primary-color: var(--my-primary-color);\n\n\n$accent-color: #fbbc04;\n\n:root {\n  \/\/ WRONG, will not work in recent Sass versions.\n  --accent-color-wrong: $accent-color;\n\n  \/\/ RIGHT, will work in all Sass versions.\n  --accent-color-right: #{$accent-color};\n}<\/code><\/pre>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h5>Reference:<\/h5>\n\n\n\n<p><a href=\"https:\/\/medium.com\/angular-in-depth\">https:\/\/medium.com\/angular-in-depth<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The above gif is the challenge we are going to conquer. Main tasks are: Have two squares, one reflecting primary color, the other reflecting secondary color. Title text \u201cangular-dynamic-theme-example\u201d has primary color, and secondary color on hover. Have two inputs and one save button to dynamically change the two colors. Approach of solution: Put color [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1460,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[37,89],"tags":[410,330,409,411,407,408,138],"_links":{"self":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/1458"}],"collection":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=1458"}],"version-history":[{"count":4,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/1458\/revisions"}],"predecessor-version":[{"id":1467,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/1458\/revisions\/1467"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media\/1460"}],"wp:attachment":[{"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=1458"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=1458"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nguenkam.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=1458"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}