{
  "path": "/themeizer-part-3",
  "site": "at://did:plc:er6erflnnxcozlbqmrpflt6h/site.standard.publication/3miclijid6r24",
  "tags": [
    "css",
    "frontend",
    "figma",
    "theme",
    "color mode"
  ],
  "$type": "site.standard.document",
  "title": "Theming, Part 3: Themeizer - A Young Companion to Styles",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreianl5fuuwpkyncutbknwhimxlioexapxpznznudf4ci4c46k3uplq"
                },
                "mimeType": "image/png",
                "size": 135403
              },
              "aspectRatio": {
                "width": 1560,
                "height": 780
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "This is already the third article on a topic that doesn’t exist. The first article was written to describe a useful and interesting feature that also produces beautiful results. Now, however, it’s time to acknowledge that theming isn’t about imposing a soulless black-and-white world or catering to personal whims; it’s a crucial step in ensuring service accessibility and maximizing conversion rates."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "If the technical part of the first article focused on the client-side, and the second on the server-side, in this third article I’d like to talk about the difficult journey styles take before reaching the site, and about the companion I created to help them - a friendly guide assisting them at every step, from design to layout. I named it Themeizer, and in this final article of the trilogy, I’d like to introduce you to it, its capabilities, and tell you about how it came to be."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Before we get to that, it’s worth refreshing your memory and reviewing the key points from the previous articles."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Key Points"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 64,
                    "byteStart": 57
                  },
                  "features": [
                    {
                      "uri": "https://www.alexdln.com/blog/themeizer-part-1",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Part 1. Thematization. History, Reasons, Implementation [article]"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Variable names follow the kebab-case convention. For example --shared-primary-hover. It’s worth noting that this is the expected format at the code level; in Figma, folders are created for this purpose. That is, this variable will be stored at the path shared/primary/hover."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Variable names should reflect their purpose and not contain information about specific shades. For example --button-primary (not --blue-200)"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Styling can be configured using external style sheets, CSS-in-JS, and/or CSS variables."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "In the case of CSS variables:"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "1. Classes containing color variables are created for each theme;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "2. A default theme is selected. A class with this theme is added to the root element;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "3. When the theme is changed, the class on the root element is updated;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "4. All styles are written using CSS variables."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "The theme is stored in cookies or local storage. Upon the next login, the page should be rendered with the correct theme."
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 87,
                    "byteStart": 80
                  },
                  "features": [
                    {
                      "uri": "https://www.alexdln.com/blog/themeizer-part-2",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Part 2. New browser APIs. Theming with SSR. Choosing between SPA, SSR, and SSG [article]"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Theming can be implemented both on the client side and on the server side. Each option has its pros and cons."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "On the server, you can retrieve the user’s theme (including for new users) and render the page immediately with the correct color scheme"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 70,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Additionally, it’s worth paying attention to two more CSS properties"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 12,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "color-scheme"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 183,
                    "byteStart": 122
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "This property tells the browser which color schemes can be used to render an element. It affects user interface elements (colors of fields, buttons, scrollbars, text, background, etc.). Currently, the documentation describes the following possible values:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "normal"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The default value, meaning that the site does not support color schemes. Browser interface elements will be styled using a light color scheme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 5,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "light"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Means that the site supports a light (daytime) theme. Browser interface elements will be styled with a light color scheme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 4,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "dark"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Means that the site supports a dark (night) theme. Browser interface elements will be styled with a dark color scheme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 4,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "only"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 210,
                    "byteStart": 158
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "The user agent may forcefully override an element’s color scheme with the user’s preferred color scheme. This property is used to prevent such overrides (support for this value is implemented only in Safari)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 14,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "<custom-ident>"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 164,
                    "byteStart": 105
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Any other value can also be specified as a property. All values not described above will have no effect (this logic has been added for future backward compatibility)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "In the case of theming, the property must be formatted as follows:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "css",
              "plaintext": ":root {\n  color-scheme: light dark;\n}\n\n.light-theme-example {\n  color-scheme: light;\n}\n\n.dark-theme-example {\n  color-scheme: dark;\n}\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 31,
                    "byteStart": 6
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Where color-scheme: light dark; means that the page supports light and dark schemes, with a preference for the light theme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreidproofnhpn5ro5jm6gqgizk7jyccqm2zh7g64ple6vgjg5pvv4wm"
                },
                "mimeType": "image/png",
                "size": 118917
              },
              "aspectRatio": {
                "width": 1560,
                "height": 395
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 184,
                    "byteStart": 158
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Since the browser needs time to load and apply styles, style flickering may occur when overriding the color scheme. To prevent this situation, you can use the<meta name=\"color-scheme\"> meta tag, which immediately informs the user agent of the supported schemes."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 60,
                    "byteStart": 13
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "For example, <meta name=\"color-scheme\" content=\"dark light\"> indicates that the page supports dark and light themes, with a preference for the dark theme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreigc2fsb6oehu7rtjp5m3zif3fqwujnuqrbja7wjtdhaz3emdonzce"
                },
                "mimeType": "image/png",
                "size": 146083
              },
              "aspectRatio": {
                "width": 1560,
                "height": 394
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 12,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "accent-color"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 67,
                    "byteStart": 50
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "This property is used to change the accent color (the primary color) for interactive elements."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Possible values include:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 4,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 57,
                    "byteStart": 45
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "auto – sets the platform’s accent color (if available);"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 7,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "<color> – a color in any format."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 133,
                    "byteStart": 96
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "The specified color automatically adjusts the contrast of other component parts of the element (text color, background, sliders, etc.). Color variations may also be generated for gradients, etc., to bring the control into compliance with the platform’s conventions regarding the use of accent colors."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The following HTML elements are currently supported:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "<input type=\"checkbox\">"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "<input type=\"radio\">"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "<input type=\"range\">"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "<progress>"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreihag76e57rkrcjwgs7m6kafzf7gk6utjgu4l2tny5sywuicdx7psy"
                },
                "mimeType": "image/png",
                "size": 164242
              },
              "aspectRatio": {
                "width": 1560,
                "height": 431
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 67,
                    "byteStart": 57
                  },
                  "features": [
                    {
                      "uri": "https://twitter.com/josepharhar",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 94,
                    "byteStart": 72
                  },
                  "features": [
                    {
                      "uri": "https://accent-color.glitch.me/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "You can see how this property works on a page created by Joey Arhar – accent-color.glitch.me."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Once you’ve covered the basics, you can move on to the next section—processes and prerequisites."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Steps for implementing theming"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "To fully customize a theme, you need to take 3 steps:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.orderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Design;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Layout using variables;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Configuring the theme switching logic."
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Strange as it may seem, implementing theming starts with design. This is a very important and quite extensive step, and the later new color schemes are added, the longer this step becomes. Overall, this process can be divided into two main tasks:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.orderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Conceiving and creating new color schemes;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Creating layouts for each scheme."
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The second point may seem unnecessary, but after creating color schemes for each theme, they need to be tested. This can only be done after creating layouts for all pages of each theme. It’s a fairly labor-intensive process, but it’s the only way to ensure that the current color palette looks high-quality everywhere and at all times."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreidifr5dzborfzjvoib6afh524ldhzzmgjaelskc7jslj74eewi5ye"
                },
                "mimeType": "image/png",
                "size": 153285
              },
              "aspectRatio": {
                "width": 1560,
                "height": 414
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "All further design changes must also be implemented in the mockups for each theme. High-quality design systems and components solve this problem perfectly, but building the entire design solely on components is difficult and, perhaps, even harmful."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 247,
                    "byteStart": 213
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "It’s clear that developing and maintaining variations for each theme is a monotonous process that takes up too much precious time. As much as I love the dark theme, it’s unfair to complicate the designer’s (and subsequently the developers’) work to such a significant degree, so it would be nice to expand the core functionality and simplify the execution of such tasks."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Design and Figma"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "When reading about design systems, you often come across the term “design tokens.” These typically refer to colors, typography, dimensions, effects, and other values. The standard structure of design tokens is a key-value pair. In the context of Figma, these can be described as objects that define various styles."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Figma Tokens"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 64,
                    "byteStart": 29
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Despite the general concept (all styles are described as objects), Figma tokens do not have a uniform structure. For example, a color object looks like this:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "json",
              "plaintext": "{\n\tdescription: \"\",\n\tid: \"S:8c92367364cb87031fe4e21199c200a3f8c79dd9,\",\n\tkey: \"8c92367364cb87031fe4e21199c200a3f8c79dd9\",\n\tname: \"dark/primary\",\n\tpaints: [\n\t\t{\n\t\t\tblendMode: \"NORMAL\",\n\t\t\tcolor: {r: 0.3999999761581421, g: 0.7120000123977661, b: 1},\n\t\t\topacity: 1,\n\t\t\ttype: \"SOLID\",\n\t\t\tvisible: true\n\t\t}\n\t],\n\tremote: false,\n\ttype: \"PAINT”\n}\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 259,
                    "byteStart": 241
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Most other objects (typography, effects, etc.) will have nothing in common with this object’s structure, except perhaps the name and identifier. To be more precise, these are not objects themselves, but references to them. Thanks to this (or because of this), changing a style in one place will update it everywhere it’s used."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Since the main goal is to make life easier for designers when implementing theming, colors are the most important tokens to focus on. Now that we understand what exactly needs to be modified, we need to figure out “How?”"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Figma for Developers"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 85,
                    "byteStart": 55
                  },
                  "features": [
                    {
                      "uri": "https://www.figma.com/developers",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "To do this, let’s turn to the Figma documentation in the “Figma for developers” section. According to it, the following options are available to developers:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "REST APIs"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Plugins"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Widgets"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Integrations"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Widgets and integrations are of little interest in the context of this task, but the other two options are worth considering."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "REST APIs"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 28,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "uri": "https://www.figma.com/developers/api",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "www.figma.com/developers/api"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 289,
                    "byteStart": 241
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "This interface provides read access and interaction with designs in Figma. Thanks to this API, you can retrieve all design nodes and extract styles and their properties from them. Currently, the Figma APIs allow you to retrieve “files” (objects describing the entire layout’s content) and their versions, images, users, comments, and styles, as well as submit comments on files."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 210,
                    "byteStart": 185
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Despite this impressive list of capabilities, this interface does not provide complete freedom of action, and along with important information, it returns a lot of useless information (in the context of theming)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Things are much better with plugins."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Plugins"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 31,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "uri": "https://www.figma.com/plugin-docs/intro/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "www.figma.com/plugin-docs/intro"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Plugins are web applications that extend Figma’s functionality. They can read and modify node styles, such as color, position, effects, typography, etc."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Plugins are limited to the scope of the current design; that is, they can track the selection of elements, but they cannot track the removal of styles, nor can they access styles from other projects within the current organization."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "After diving into the topic of extending Figma’s functionality, we can return to our original goal: to make life easier for designers and developers."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Optimizing processes during the design phase"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Creating a copy of each page for every color scheme, as mentioned at the beginning of the article, is too labor-intensive a process. Therefore, it’s worth starting the optimization right here."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Several key conditions can be identified that the final solution must meet:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Key requirements:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "the ability to change an element’s color scheme;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "ease of use;"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "speed and accessibility from anywhere."
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The Themeizer Plugin"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "To ensure ease of use, the solution should not create its own API and subsequently impose it; it should merely complement existing functionality. In Figma, style folders are typically used to create color schemes."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreih6f5o4hkinuldsivvnkuhinzgrvguy4bkp5q3nnkzcnc7gcyflky"
                },
                "mimeType": "image/png",
                "size": 142522
              },
              "caption": "Folders in Figma",
              "aspectRatio": {
                "width": 1500,
                "height": 750
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 459,
                    "byteStart": 422
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 580,
                    "byteStart": 559
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Therefore, folder names can be considered theme names, and all styles within them - the colors of the current scheme. Plugins provide access to the values of these styles and allow them to be modified. This means that from the plugin, you can retrieve any theme and change all styles in the mockups to a different theme. Additionally, there are situations where a page needs to display designs in different color schemes (for example, to present a new concept); in such cases, you need to change styles not across the entire design, but in individual frames (such as selected ones)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "This is the first task the plugin solves"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreic24gct5ngvciz7d7pxcyjtchw5x7hhk64pydvhyivwnylxotcj24"
                },
                "mimeType": "image/png",
                "size": 120356
              },
              "aspectRatio": {
                "width": 1560,
                "height": 715
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "However, this comes with some additional requirements. Not every folder in a design is a theme; often there are separate folders for general styles, button elements, or branded elements. Therefore, the plugin needs additional settings where you can select themes."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreiccrvt5b4bfzcvwyhe7cesq3sozvmjnwltf64fm6tqaooemwhhgcq"
                },
                "mimeType": "image/png",
                "size": 55632
              },
              "aspectRatio": {
                "width": 1502,
                "height": 1040
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "All themes that are not selected as light or dark are considered shared, meaning they are available to any theme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The plugin knows all color schemes. This means it has access to the primary source of truth, and if so, this is an opportunity to make this source not just the primary one, but the only one."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Exchange with the client side"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "To make Figma styles the sole source of truth, you need to create a shared repository for both the design team and the client. To do this, all styles must be uploaded to the server."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "As mentioned earlier, the plugin shouldn’t impose anything, so anything can serve as the server. The only rule is that the address must accept a POST request. This address must be specified in the plugin settings, and later, upon publishing, a request containing topics with the following format will be sent to this address:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "tsx",
              "plaintext": "{\n  [theme: string]: {\n    list: [\n      {\n        name: string,\n        value: string,\n        type: \"solid\" | \"linear\" | \"radial\"\n      }\n    ],\n    type: \"light\" | \"dark\" | \"shared\"\n  }\n}\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 205,
                    "byteStart": 174
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 542,
                    "byteStart": 451
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Still, before uploading to the server, it’s worth reviewing all changes and making sure everything is correct. Plugin APIs for such tasks provide access to client storage (an alternative to local storage) and to storage within the current theme. Client storage is definitely not suitable, since several people are working on the project and each of them must have access to the saved data. Storage within the current theme also limits capabilities (for example, data will be lost when the project is copied or when the plugin is reinstalled)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 285,
                    "byteStart": 264
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "There is another location that knows the most recently saved styles - the server to which the plugin is already capable of writing color schemes. Accordingly, changes can also be read from it. To do this, you need to specify an address in the settings where data (using the same scheme) can be retrieved via a GET request."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreigcnekbrqejahbiayyxfdbcr552zdnug5di2psrgijvwcnjpduen4"
                },
                "mimeType": "image/png",
                "size": 82791
              },
              "aspectRatio": {
                "width": 1497,
                "height": 1092
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 94,
                    "byteStart": 87
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Additionally, special headers may be required to execute requests. A separate field -  headers - has been created for them."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "After that, when publishing the next version, you will be able to see all changes."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreibi3nyfa6bnb6mk7t6mrbbbkmyfac26d2lccpnkfsyy7pnnfufuni"
                },
                "mimeType": "image/png",
                "size": 197564
              },
              "caption": "Previewing color changes before publishing to the server",
              "aspectRatio": {
                "width": 1560,
                "height": 694
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Now all theme data is stored on the server and updated as needed. The next step is to configure retrieval on the client."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Optimizing processes during the development phase"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "What should happen on the client:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Retrieving styles"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Converting to CSS variables"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Embedding classes with colors into styles"
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 17,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Retrieving styles"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "To do this, you can use the same URL as for checking changes within the plugin. Accordingly, the client needs to know this URL and the headers required for the request."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "As I wrote in the previous article, theming can be configured on the server side or on the client side. In the case of server-side rendering, styles are expected to always be up to date. In this case, sending a request on every request becomes a resource-intensive operation. If theming is applied on the client, then all styles must be fetched during the build phase."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 10,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Conversion"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 89,
                    "byteStart": 79
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 257,
                    "byteStart": 245
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "The plugin saves all names in a convenient format for creating CSS variables - kebab-case. Thanks to this, all that remains is to retrieve all the names, turn them into variables, and create a class for each theme. Additionally, you need to add color-scheme to the classes so that for dark themes, the browser displays UI elements in a dark interface."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 11,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Integration"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "For client-side theming, the best approach is to embed the themes during the build process. For server-side theming, embedding should occur during page rendering. In both cases, it’s important to optimize the number of requests sent and cache their results."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Themeizer was created to address these challenges."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The “themeizer” package"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The package retrieves the necessary settings from environment variables, namely the server URL, headers, and revalidate. Since there can be a large number of requests, the package supports the revalidate option out of the box, which determines the frequency of request sending."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The package supports two options for embedding styles: during compilation, by replacing the meta tag with a style tag containing classes and styles; and a manual mode, used primarily for SSR."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 89,
                    "byteStart": 50
                  },
                  "features": [
                    {
                      "uri": "https://www.npmjs.com/package/themeizer",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "You can read more about the package on its page - https://www.npmjs.com/package/themeizer"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The “next-themeizer” package"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Additionally, a package was created for Next.js, as one of Next.js’s main goals is to lower the barrier to entry. Minimal hassle, unnecessary logic, and application configuration settings."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "You can add the package to your Next.js configuration as follows:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "jsx",
              "plaintext": "// next.config.js\nconst withThemeizer = require('next-themeizer').default;\nmodule.exports = withThemeizer();\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 44,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "uri": "https://www.npmjs.com/package/next-themeizer",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "https://www.npmjs.com/package/next-themeizer"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Process Optimization During Implementation"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The packages listed above will work perfectly if the site is already built on CSS variables and uses them throughout. However, if the decision to add theming is made after the product has already been created, the process of transitioning to variables can take a significant amount of time.1"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The “themeizer-cli” package"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Another feature of the ecosystem is simplified implementation. For this, you can use the themeizer-cli package. This is a command-line utility that automatically replaces colors in style sheets with variable names from the desired theme."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreihvai25xiqknmjw4qkvxlhrv2ezqs5vtfcltdvo6ngnfox6k3cc3y"
                },
                "mimeType": "image/png",
                "size": 65406
              },
              "caption": "Automatic replacement via utility\n",
              "aspectRatio": {
                "width": 1560,
                "height": 709
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Example of use:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "bash",
              "plaintext": "npm install themeizer-cli -g\nthemeizer-cli –c ./themeizer.config.json\n// or\nthemeizer-cli –u <https://server-url.com/themes> -t light\n"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 43,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "uri": "https://www.npmjs.com/package/themeizer-cli",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "https://www.npmjs.com/package/themeizer-cli"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The utility is just getting started and has many improvements ahead of it. One of its main drawbacks at the moment is the inability to control changes. It simply goes through all styles and changes colors wherever a variable can be used."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Change control and color support are also important components of theming that, of course, could not be overlooked."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "The “stylelint-themeizer” package"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "When working with styles and validating them, the main tool is Stylelint (ESLint for styles), which can detect errors and automatically fix them."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 278,
                    "byteStart": 273
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Stylelint-themeizer is a plugin that adds a new rule: if this rule is enabled, styles will be automatically checked, and if a color appears in published styles but is not defined as a CSS variable, it will display an error and suggest the variable name. When used with the --fix argument, Stylelint will automatically fix all styles."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.image",
              "image": {
                "$type": "blob",
                "ref": {
                  "$link": "bafkreif7oq44oi665ownezcuvyhkk4kfrkjqgwc2rn6bvfwhwnismw322m"
                },
                "mimeType": "image/png",
                "size": 253324
              },
              "caption": "Style hints and console error messages",
              "aspectRatio": {
                "width": 1560,
                "height": 924
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 41,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "uri": "https://www.npmjs.com/package/stylelint-themeizer",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "www.npmjs.com/package/stylelint-themeizer"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 169,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Note: Unfortunately, Stylelint still does not have full Sass support (e.g., the existing postcss-sass parser cannot correctly process styles without the “#” prefix)."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Conclusion"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Themeizer has taken its first steps toward maturity, but there is still a long way to go. Perhaps bringing it into the spotlight at such an early stage is a hasty decision. But without a doubt, it is already capable of much, ready to grow and evolve, and waiting for the opportunity to help all developers make their web worlds more convenient and accessible."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 6,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Links:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "Plugin - https://www.figma.com/community/plugin/1065764293242137356/Themeizer"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "themeizer - https://www.npmjs.com/package/themeizer"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "next-themeizer - https://www.npmjs.com/package/next-themeizer"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "themeizer-cli - https://www.npmjs.com/package/themeizer-cli"
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "stylelint-themeizer - https://www.npmjs.com/package/stylelint-themeizer"
                  }
                }
              ]
            }
          }
        ]
      }
    ]
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreianl5fuuwpkyncutbknwhimxlioexapxpznznudf4ci4c46k3uplq"
    },
    "mimeType": "image/png",
    "size": 135403
  },
  "description": "It’s time to acknowledge that theming isn’t about imposing a soulless black-and-white world or catering to personal whims; it’s a crucial step in ensuring service accessibility and maximizing conversion rates",
  "publishedAt": "2022-02-15T07:19:00.000Z",
  "textContent": "This is already the third article on a topic that doesn’t exist. The first article was written to describe a useful and interesting feature that also produces beautiful results. Now, however, it’s time to acknowledge that theming isn’t about imposing a soulless black-and-white world or catering to personal whims; it’s a crucial step in ensuring service accessibility and maximizing conversion rates.\n\nIf the technical part of the first article focused on the client-side, and the second on the server-side, in this third article I’d like to talk about the difficult journey styles take before reaching the site, and about the companion I created to help them - a friendly guide assisting them at every step, from design to layout. I named it Themeizer, and in this final article of the trilogy, I’d like to introduce you to it, its capabilities, and tell you about how it came to be.\n\nBefore we get to that, it’s worth refreshing your memory and reviewing the key points from the previous articles.\n\nKey Points\n\nPart 1. Thematization. History, Reasons, Implementation [article]\n\nPart 2. New browser APIs. Theming with SSR. Choosing between SPA, SSR, and SSG [article]\n\nAdditionally, it’s worth paying attention to two more CSS properties\n\ncolor-scheme\n\nThis property tells the browser which color schemes can be used to render an element. It affects user interface elements (colors of fields, buttons, scrollbars, text, background, etc.). Currently, the documentation describes the following possible values:\n\nnormal\n\nThe default value, meaning that the site does not support color schemes. Browser interface elements will be styled using a light color scheme.\n\nlight\n\nMeans that the site supports a light (daytime) theme. Browser interface elements will be styled with a light color scheme.\n\ndark\n\nMeans that the site supports a dark (night) theme. Browser interface elements will be styled with a dark color scheme.\n\nonly\n\nThe user agent may forcefully override an element’s color scheme with the user’s preferred color scheme. This property is used to prevent such overrides (support for this value is implemented only in Safari).\n\n<custom-ident>\n\nAny other value can also be specified as a property. All values not described above will have no effect (this logic has been added for future backward compatibility).\n\nIn the case of theming, the property must be formatted as follows:\n\n:root {\n  color-scheme: light dark;\n}\n\n.light-theme-example {\n  color-scheme: light;\n}\n\n.dark-theme-example {\n  color-scheme: dark;\n}\n\n\nWhere color-scheme: light dark; means that the page supports light and dark schemes, with a preference for the light theme.\n\nSince the browser needs time to load and apply styles, style flickering may occur when overriding the color scheme. To prevent this situation, you can use the<meta name=\"color-scheme\"> meta tag, which immediately informs the user agent of the supported schemes.\n\nFor example, <meta name=\"color-scheme\" content=\"dark light\"> indicates that the page supports dark and light themes, with a preference for the dark theme.\n\naccent-color\n\nThis property is used to change the accent color (the primary color) for interactive elements.\n\nPossible values include:\n\nauto – sets the platform’s accent color (if available);\n\n<color> – a color in any format.\n\nThe specified color automatically adjusts the contrast of other component parts of the element (text color, background, sliders, etc.). Color variations may also be generated for gradients, etc., to bring the control into compliance with the platform’s conventions regarding the use of accent colors.\n\nThe following HTML elements are currently supported:\n\nYou can see how this property works on a page created by Joey Arhar – accent-color.glitch.me.\n\nOnce you’ve covered the basics, you can move on to the next section—processes and prerequisites.\n\nSteps for implementing theming\n\nTo fully customize a theme, you need to take 3 steps:\n\nStrange as it may seem, implementing theming starts with design. This is a very important and quite extensive step, and the later new color schemes are added, the longer this step becomes. Overall, this process can be divided into two main tasks:\n\nThe second point may seem unnecessary, but after creating color schemes for each theme, they need to be tested. This can only be done after creating layouts for all pages of each theme. It’s a fairly labor-intensive process, but it’s the only way to ensure that the current color palette looks high-quality everywhere and at all times.\n\nAll further design changes must also be implemented in the mockups for each theme. High-quality design systems and components solve this problem perfectly, but building the entire design solely on components is difficult and, perhaps, even harmful.\n\nIt’s clear that developing and maintaining variations for each theme is a monotonous process that takes up too much precious time. As much as I love the dark theme, it’s unfair to complicate the designer’s (and subsequently the developers’) work to such a significant degree, so it would be nice to expand the core functionality and simplify the execution of such tasks.\n\nDesign and Figma\n\nWhen reading about design systems, you often come across the term “design tokens.” These typically refer to colors, typography, dimensions, effects, and other values. The standard structure of design tokens is a key-value pair. In the context of Figma, these can be described as objects that define various styles.\n\nFigma Tokens\n\nDespite the general concept (all styles are described as objects), Figma tokens do not have a uniform structure. For example, a color object looks like this:\n\n{\n\tdescription: \"\",\n\tid: \"S:8c92367364cb87031fe4e21199c200a3f8c79dd9,\",\n\tkey: \"8c92367364cb87031fe4e21199c200a3f8c79dd9\",\n\tname: \"dark/primary\",\n\tpaints: [\n\t\t{\n\t\t\tblendMode: \"NORMAL\",\n\t\t\tcolor: {r: 0.3999999761581421, g: 0.7120000123977661, b: 1},\n\t\t\topacity: 1,\n\t\t\ttype: \"SOLID\",\n\t\t\tvisible: true\n\t\t}\n\t],\n\tremote: false,\n\ttype: \"PAINT”\n}\n\n\nMost other objects (typography, effects, etc.) will have nothing in common with this object’s structure, except perhaps the name and identifier. To be more precise, these are not objects themselves, but references to them. Thanks to this (or because of this), changing a style in one place will update it everywhere it’s used.\n\nSince the main goal is to make life easier for designers when implementing theming, colors are the most important tokens to focus on. Now that we understand what exactly needs to be modified, we need to figure out “How?”\n\nFigma for Developers\n\nTo do this, let’s turn to the Figma documentation in the “Figma for developers” section. According to it, the following options are available to developers:\n\nWidgets and integrations are of little interest in the context of this task, but the other two options are worth considering.\n\nREST APIs\n\nwww.figma.com/developers/api\n\nThis interface provides read access and interaction with designs in Figma. Thanks to this API, you can retrieve all design nodes and extract styles and their properties from them. Currently, the Figma APIs allow you to retrieve “files” (objects describing the entire layout’s content) and their versions, images, users, comments, and styles, as well as submit comments on files.\n\nDespite this impressive list of capabilities, this interface does not provide complete freedom of action, and along with important information, it returns a lot of useless information (in the context of theming).\n\nThings are much better with plugins.\n\nPlugins\n\nwww.figma.com/plugin-docs/intro\n\nPlugins are web applications that extend Figma’s functionality. They can read and modify node styles, such as color, position, effects, typography, etc.\n\nPlugins are limited to the scope of the current design; that is, they can track the selection of elements, but they cannot track the removal of styles, nor can they access styles from other projects within the current organization.\n\nAfter diving into the topic of extending Figma’s functionality, we can return to our original goal: to make life easier for designers and developers.\n\nOptimizing processes during the design phase\n\nCreating a copy of each page for every color scheme, as mentioned at the beginning of the article, is too labor-intensive a process. Therefore, it’s worth starting the optimization right here.\n\nSeveral key conditions can be identified that the final solution must meet:\n\nKey requirements:\n\nThe Themeizer Plugin\n\nTo ensure ease of use, the solution should not create its own API and subsequently impose it; it should merely complement existing functionality. In Figma, style folders are typically used to create color schemes.\n\nTherefore, folder names can be considered theme names, and all styles within them - the colors of the current scheme. Plugins provide access to the values of these styles and allow them to be modified. This means that from the plugin, you can retrieve any theme and change all styles in the mockups to a different theme. Additionally, there are situations where a page needs to display designs in different color schemes (for example, to present a new concept); in such cases, you need to change styles not across the entire design, but in individual frames (such as selected ones).\n\nThis is the first task the plugin solves\n\nHowever, this comes with some additional requirements. Not every folder in a design is a theme; often there are separate folders for general styles, button elements, or branded elements. Therefore, the plugin needs additional settings where you can select themes.\n\nAll themes that are not selected as light or dark are considered shared, meaning they are available to any theme.\n\nThe plugin knows all color schemes. This means it has access to the primary source of truth, and if so, this is an opportunity to make this source not just the primary one, but the only one.\n\nExchange with the client side\n\nTo make Figma styles the sole source of truth, you need to create a shared repository for both the design team and the client. To do this, all styles must be uploaded to the server.\n\nAs mentioned earlier, the plugin shouldn’t impose anything, so anything can serve as the server. The only rule is that the address must accept a POST request. This address must be specified in the plugin settings, and later, upon publishing, a request containing topics with the following format will be sent to this address:\n\n{\n  [theme: string]: {\n    list: [\n      {\n        name: string,\n        value: string,\n        type: \"solid\" | \"linear\" | \"radial\"\n      }\n    ],\n    type: \"light\" | \"dark\" | \"shared\"\n  }\n}\n\n\nStill, before uploading to the server, it’s worth reviewing all changes and making sure everything is correct. Plugin APIs for such tasks provide access to client storage (an alternative to local storage) and to storage within the current theme. Client storage is definitely not suitable, since several people are working on the project and each of them must have access to the saved data. Storage within the current theme also limits capabilities (for example, data will be lost when the project is copied or when the plugin is reinstalled).\n\nThere is another location that knows the most recently saved styles - the server to which the plugin is already capable of writing color schemes. Accordingly, changes can also be read from it. To do this, you need to specify an address in the settings where data (using the same scheme) can be retrieved via a GET request.\n\nAdditionally, special headers may be required to execute requests. A separate field -  headers - has been created for them.\n\nAfter that, when publishing the next version, you will be able to see all changes.\n\nNow all theme data is stored on the server and updated as needed. The next step is to configure retrieval on the client.\n\nOptimizing processes during the development phase\n\nWhat should happen on the client:\n\nRetrieving styles\n\nTo do this, you can use the same URL as for checking changes within the plugin. Accordingly, the client needs to know this URL and the headers required for the request.\n\nAs I wrote in the previous article, theming can be configured on the server side or on the client side. In the case of server-side rendering, styles are expected to always be up to date. In this case, sending a request on every request becomes a resource-intensive operation. If theming is applied on the client, then all styles must be fetched during the build phase.\n\nConversion\n\nThe plugin saves all names in a convenient format for creating CSS variables - kebab-case. Thanks to this, all that remains is to retrieve all the names, turn them into variables, and create a class for each theme. Additionally, you need to add color-scheme to the classes so that for dark themes, the browser displays UI elements in a dark interface.\n\nIntegration\n\nFor client-side theming, the best approach is to embed the themes during the build process. For server-side theming, embedding should occur during page rendering. In both cases, it’s important to optimize the number of requests sent and cache their results.\n\nThemeizer was created to address these challenges.\n\nThe “themeizer” package\n\nThe package retrieves the necessary settings from environment variables, namely the server URL, headers, and revalidate. Since there can be a large number of requests, the package supports the revalidate option out of the box, which determines the frequency of request sending.\n\nThe package supports two options for embedding styles: during compilation, by replacing the meta tag with a style tag containing classes and styles; and a manual mode, used primarily for SSR.\n\nYou can read more about the package on its page - https://www.npmjs.com/package/themeizer\n\nThe “next-themeizer” package\n\nAdditionally, a package was created for Next.js, as one of Next.js’s main goals is to lower the barrier to entry. Minimal hassle, unnecessary logic, and application configuration settings.\n\nYou can add the package to your Next.js configuration as follows:\n\n// next.config.js\nconst withThemeizer = require('next-themeizer').default;\nmodule.exports = withThemeizer();\n\n\nhttps://www.npmjs.com/package/next-themeizer\n\nProcess Optimization During Implementation\n\nThe packages listed above will work perfectly if the site is already built on CSS variables and uses them throughout. However, if the decision to add theming is made after the product has already been created, the process of transitioning to variables can take a significant amount of time.1\n\nThe “themeizer-cli” package\n\nAnother feature of the ecosystem is simplified implementation. For this, you can use the themeizer-cli package. This is a command-line utility that automatically replaces colors in style sheets with variable names from the desired theme.\n\nExample of use:\n\nnpm install themeizer-cli -g\nthemeizer-cli –c ./themeizer.config.json\n// or\nthemeizer-cli –u <https://server-url.com/themes> -t light\n\n\nhttps://www.npmjs.com/package/themeizer-cli\n\nThe utility is just getting started and has many improvements ahead of it. One of its main drawbacks at the moment is the inability to control changes. It simply goes through all styles and changes colors wherever a variable can be used.\n\nChange control and color support are also important components of theming that, of course, could not be overlooked.\n\nThe “stylelint-themeizer” package\n\nWhen working with styles and validating them, the main tool is Stylelint (ESLint for styles), which can detect errors and automatically fix them.\n\nStylelint-themeizer is a plugin that adds a new rule: if this rule is enabled, styles will be automatically checked, and if a color appears in published styles but is not defined as a CSS variable, it will display an error and suggest the variable name. When used with the --fix argument, Stylelint will automatically fix all styles.\n\nwww.npmjs.com/package/stylelint-themeizer\n\nNote: Unfortunately, Stylelint still does not have full Sass support (e.g., the existing postcss-sass parser cannot correctly process styles without the “#” prefix).\n\nConclusion\n\nThemeizer has taken its first steps toward maturity, but there is still a long way to go. Perhaps bringing it into the spotlight at such an early stage is a hasty decision. But without a doubt, it is already capable of much, ready to grow and evolve, and waiting for the opportunity to help all developers make their web worlds more convenient and accessible.\n\nLinks:"
}