By Sonny Yu on April 25, 2017
I was honored to work on the Test Tooling Team at Hootsuite since January, and one of the main projects that I actively participated in is the Arbiter Tests Management System, a project that provides a user-friendly way for QAs and Developers to manage the tests to be run on different build pipelines in real time. By allowing the users to directly toggle tests from the UI, Arbiter has significantly reduced the resolution time to tests failures in the organization.
As the program aims to optimize developer’s experience, we focused not only on the practical convenience of the program, but also on the design of the user interface that could create a pleasant visual effect for the developers. With this in mind, we have designed 5 different themes ranging all over the spectrum: dark, light, oreo, tiramisu, and azure.
While React has been the de-facto standard for front-end development at Hootsuite, we have also chosen React as our front-end language to effectively build the visual effects we intended to achieve.
In February, I completed the process of refactoring CSS Stylesheets into LESS Stylesheets to match the organization standards at Hootsuite. This transition results in a much more efficient integration with React. The stylesheets are now loaded directly into the components they style with less-loader so that we don’t have to work with stylesheets in raw CSS.
BUT, alas, every rose comes with its thorn! Now that stylesheet loading is handled by less-loader, we can no longer explicitly manipulate the stylesheet links to switch themes 🙁
Component StructureTo make things simple, I have separated all the theme-loading related functions into its own React component named ReactThemeLoader. The overall structure of this component involves initially loading all the available themes, disabling all the themes, and re-enabling the current theme.
The mechanism of this system relies the fact that stylesheets loaded onto DOM can be toggled on and off by setting the boolean property “disabled”.This property gives us the flexibility to load multiple themes onto the page at once by enabling the current theme and disabling others, without having them interfering one another.
In componentWillMount, all the themes are loaded and disabled initially. The index of the theme stylesheets are tracked here and recorded as key-value pairs into a dictionary called themes in state.
After the components are mounted, in componentDidMount, the program reads the theme name stored in the cookie and and sets the theme by calling setTheme(). In case the cookie is not present, the theme will be set to default theme (in this case dark), which is handled by the setTheme() method.
In the setTheme method, the stylesheet indexes of the old theme and new theme are simply retrieved from the dictionary themes and toggled by setting the “disabled” property of the stylesheets as shown above.
The UML Diagram for the theme-loading process is shown below:
NPM PackageTo make this React Component more reusable, I have published an NPM package: react-theme-loader (http://ow.ly/U9Lt30asuTX) as a ready-to-use open-source React package for you. Below is where the tutorial actually starts
Install the NPM PackageUnder your project directory, run:
This command will install this node package to your project and add it to the dependencies in package.json
Import the React ComponentIn your top-level React components where you want the theme styles to take effect, import the package and render it as a component
When rendering the ThemeLoader, there are certain properties we need to attach to the element; the above is just an example, please read the instructions below on filling in the props and do NOT copy the above exactly.
Required“ref”: pass in “themeLoader” exactly so that you have access to the functions provided by the component.
“supportedThemes”: pass in an array of strings that represent the names of the theme files (without “.less”).
“themeDirectory”: pass in the relative path of the directory of the theme files in “supportedThemes”
Optional“fonts”: pass in the relative path of the fonts file. This file should contain ALL fonts used in the themes, and do NOT import any fonts in the theme files since they mess up the indexes of loaded stylesheets. If “fonts” is not specified, no fonts file will be loaded.
“defaultTheme”: pass in the name(string) of the default theme, which will be loaded initially when no theme cookie is stored. The first theme in “supportedThemes” will be the default if not specified.
“themeCookie”: pass in the name(string) of the cookie where you wish to store the current theme name, so that the browser will remember the selected theme. “CURRENT_THEME” will be used if not specified.
Switching ThemesIn the React component where ThemeLoader is rendered, simply use
to switch themes, where theme is the name(string) of the theme you wish to switch to.
When a theme is loaded, the ThemeLoader will render the following to HTML:
CookieCurrently the theme information in our system is completely relying on un-encrypted browser cookie, named “CURRENT_THEME” by default. The package gives you an option to pass in the cookie name you wish to have.
Although theme information is not sensitive, it still brings inconvenience and leaves holes for security issues. We are currently looking into other ways of improving this features.
React-Theme-Loader 2.0 !!!Currently, the react-theme-loader package is still in its rudimentary stage and a lot of customized features can be added to the component. The ones listed below are in progress, please feel free to leave any ideas below in the comments
- Customized way to store current theme information
- More robust import handling
ConclusionCompleting the transition from CSS Stylesheets to LESS Stylesheets is one of the first refactoring process I have worked on during my time at Hootsuite. While I have been through a lot of frustrations and desperation throughout this process, I was forced into discovering more hidden features in the world of programming (leading me to make my first npm pacakge!) Looking back at the process, I would consider it is a tremendously helpful experience in the long run with major improvements made to the system.
About the Author
Sonny Yu was a co-op student on the Test Tooling team for the spring term of 2017. He studies Computer Science and Business Administration at University of Waterloo. Connect with him on LinkedIn!