Creating Angular 2+ components with the ability to switch themes

Hello.

So, let's say we are writing a site on which we need to realize the ability to dynamically switch the appearance settings, or, more simply, the theme. The theme (theme) will be called a set of properties that determine the appearance of the components (and indeed the whole site).
Suppose we have a one-page application on Angular, and let it have so many components, and one of them is ButtonComponent (we will connect the styles from button.component.css to the component), by the example of which we will consider the whole mechanism. And you need to realize the ability to switch between two themes: “dark” and “light”, which will differ only in colors (in the general case, you can put anything into the theme, sizes there, fonts, background pictures, etc. - that's all what can be controlled from css).

The original light styles looked like this:

.my-button {
  padding: 10px;
  font-size: 14px;
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
  cursor: pointer;
}
my-button:disabled {
  pointer-events: none;
  cursor: default;
  color: grey;
  border: 1px solid grey;
}

And for "dark" like this:

.my-button {
  padding: 10px;
  font-size: 14px;
  color: lightblue;
  border: 1px solid lightblue;
  background: midnightblue;
  cursor: pointer;
}
.my-button:disabled {
  pointer-events: none;
  cursor: default;
  background: grey;
  color: lightgrey;
  border: 1px solid lightgrey;
}

Select the common part and save it in a separate file. Let it be there, in the folder with our component, and called button-core.component.css. We also create one file for each theme, call them button-light.component.css and button-dark.component.css, respectively. It’s already clear what will be in them, but for completeness I’ll give you a code (whoever doesn’t want to see it, feel free to scroll):

button-core.css:

.my-button {
  padding: 10px;
  font-size: 14px;
  cursor: pointer;
}
.my-button:disabled {
  pointer-events: none;
  cursor: default;
}

button-light.css:

.my-button {
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
}
my-button:disabled {
  color: grey;
  border: 1px solid grey;
}

button-dark.css:

.my-button {
  color: lightblue;
  border: 1px solid lightblue;
  background: midnightblue;
}
.my-button:disabled {
  background: grey;
  color: lightgrey;
  border: 1px solid lightgrey;
}

And in the end, button.component.css itself will look like this:

@import 'button-core.css'
@import 'button-light.css'
@import 'button-dark.css'

Note that first you should connect the general settings for all topics, then the file with the default theme, and then all the rest.

Now let's see how we can switch from one topic to another, including from the code.

To do this, we need a selector: host-context (). What is he doing? This selector searches for a specific css class, running through all the parents of the element, to the root. And if he meets this class, then he applies the described changes. And it’s better to digress a little from our button and consider its operation as an example. Let us have two nested components: parent-component and child-component. Their styles are isolated from each other, thanks to Angular, and this is good, but we sometimes want to change the appearance of the child-component depending on, say, whether the cursor is on the parent-component (hover event). One could, of course, remove encapsulation using ViewEncapsulation: none, or add an input variable in the child-component for these purposes, but it’s much simpler and more correct to do this:

:host-context(.parent-component:hover) .child-component {
  background: lightcoral;
}

Let's get back to our little book. We can put some style (: host-context (.some-style)) as a marker for the host context, which we need to do. Now the light theme files will look like this:

button-light.css:

:host-context(.light-theme) .my-button {
  color: midnightblue;
  border: 1px solid mdnightblue;
  background: white;
}
:host-context(.light-theme) my-button:disabled {
  color: grey;
  border: 1px solid grey;
}

For dark-theme is similar.

What did we get? Now, if we have only one of the parent elements of our button set the light-theme or dark-theme class, it will change its appearance by connecting the corresponding classes from our files. Most often, the theme is applied immediately to the entire application, which means that you switch the theme on the root element, for example, on the external div from the app-component (well, or the component that you write in the bootstrap of your application). You can enter the theme flag in it and hang it on the external element.

[ngClass]="{'light-theme': theme === 'light', 'dark-theme': theme === 'dark'}".

Well, what can I tell you later, everything seems to be clear.

Here is such a simple way to create themes without using css - preprocessors. The method is imperfect, because involves a lot of copy paste. With sass preprocessors, a more concise creation of themes is possible. However, this method also has the right to life.

Thank you all for your attention, I hope someone will find it useful. Based on the article , all the ideas, in general, from there.

Also popular now: