Tag: Theming

Color Theming with CSS Custom Properties and Tailwind

Custom properties not only enable us to make our code more efficient, but allow us to work some real magic with CSS too. One area where they have huge potential is theming. At Atomic Smash we use Tailwind CSS, a utility class framework, for writing our styles. In this article, we’ll look at how custom properties can be used for theming, and how we can integrate them with Tailwind to maximize the reusability of our code. We won’t cover getting up and running with Tailwind — check out the official documentation for that — but even if you’re new to it you might find some of these tips useful.

Theming overview

Let’s say we have a “Call To Action” (CTA) component with a heading, body copy, and button.

A box with a light red heading that reads join our mailing list above a dark red body that reads be the first to hear about our new offerings right before a red signup button.

Writing regular (non-Tailwind) CSS for this color scheme would look something like this:

.cta {   background-color: #742a2a; // dark red   color: #ffffff; //white }      .cta__heading {   background-color: #e53e3e; // medium red   color: #742a2a; } 
 .cta__button {   background-color: #e53e3e; }

Using Tailwind, we would apply these colors as utility classes in our HTML:

<div class="bg-red-900 text-white">   <h3 class="bg-red-600 text-red-900">Join our mailing list</h3>   <div>     <p>Be the first to hear about our new offerings</p>     <button class="bg-red-600" type="button">Sign up</button>   </div> </div>

I’ve deliberately left out classes relating to anything other than the basic color scheme, but you can see the example in its entirety in this demo:

Now, if we wanted to apply a different color scheme to our component, we would need to override the color values of our original component. Without Tailwind, a common way to do that would be to append a theme class to the component itself, and redefine the color values lower down in the cascade. So for a component with a modifier class of .cta--blue (using the BEM convention) we’ll apply the CSS values for a blue color scheme:

.cta--blue {   background-color: #2a4365; // dark blue } 
 .cta--blue .cta__heading {   background-color: #3182ce; // medium blue   color: #2a4365; } 
 .cta--blue .cta__button {   background-color: #3182ce; }
A box with a light blue heading that reads join our mailing list above a dark bluebody that reads be the first to hear about our new offerings right before a blue signup button.

If we’re using Sass or another preprocessor, it’s likely we’ll make life easier for ourselves by using variables for those color names, and we might nest the .cta__heading and .cta__body selectors. It doesn’t exactly make our code more concise, but it does make it more manageable by having a single place to update those values.

Now, suppose we have 10 different color schemes, as was my experience on a recent project. Our code starts to get longer, as we’re basically duplicating the above example 10 times in order to change those color values. Now imagine every component in our design system needs 10 color schemes, and many of those components are far more complex than our simple CTA. Maybe our themes need different fonts too. Suddenly we have a lot of CSS to write.

Theming with Tailwind

If we’re using Tailwind, on the other hand, we’d need to change multiple classes in the HTML itself. Even if we’re using a JavaScript framework, like React or Vue, this is not exactly a trivial task. In order to ensure unused styles are removed in a production build, Tailwind discourages the use of string concatenation for class names (at the time of writing). So building our themes means potentially piling a lot of logic into our components.

Theming with Custom Properties

By using custom properties for our color themes, we can drastically reduce the amount of code we need to write, and alleviate the maintenance burden. Let’s first take a look at how we can do this in regular CSS.

We define our custom properties as variables on the :root selector, making them global variables. (The body selector would serve us just as well.) Then we can use those variables in a selector, in place of our color property values:

:root {   --primary: #742a2a; // dark red;   --secondary: #e53e3e; // medium red } 
 .cta {   background-color: var(--primary);   color: white; } 
 .cta__heading {   background-color: var(--secondary);   color: var(--primary); } 
 .cta__button {   background-color: var(--secondary); }

This is where the real magic happens: now the code for creating each of our themes becomes a case of only updating those custom property values. The new values will be inherited wherever we apply our theme class:

.th-blue {   --primary: #2a4365; // dark blue   --secondary: #3182ce; // medium blue }

If we want a blue color scheme, we can apply that .th-blue class to the component, or even use it on the <body> tag to apply to apply a page-wide theme, which can be overridden on individual components as desired. Using a utility class potentially saves us writing even more code compared to a component-specific class (such as .cta--blue in the original code), as it could be applied anywhere in our codebase.

Handling older browsers

Like many agencies, plenty of our clients at Atomic Smash still require us to support Internet Explorer 11. While I’m okay with a progressive enhancement approach in most cases (by providing simpler fallback layouts for browsers that don’t support CSS Grid, for instance), I find theming is one area that often doesn’t allow for easy compromise. Clients want their brand colors and fonts seen, even on older browsers. Providing fallbacks using feature queries would entail a lot of extra work that would negate the benefits of using custom properties in the first place. To overcome this, we need a polyfill.

There are a couple of options for polyfilling custom properties in IE 11.

postcss-custom-properties

The first is using a PostCSS plugin called postcss-custom-properties. If you’re already using PostCSS in your workflow, this is fairly simple to add. It works by processing your CSS and outputting the result of the variable as the property value. So if you have the following CSS:

:root {   --color: red; } 
 h1 {   color: var(--color); }

The processed result will be:

h1 {   color: red;   color: var(--color); }

Browsers that don’t support custom properties will ignore the second rule and fall back to the regular property value. There is also an option to remove the rules with the custom properties in the output, so the file size will be smaller. This means that no browsers will get the custom property — which is an issue if you’re updating variables dynamically — but you’ll be able to use them for static values in your code with no ill effects.

Unfortunately this polyfill has some limitations:

  1. You need to specify the file (or files) in your config where you’re defining the custom properties.
  2. Custom properties can only be defined on the :root selector.

The first limitation is relatively trivial, but the second unfortunately renders this polyfill entirely useless for our theming use case. It means we can’t redefine variables on a selector to create our themes.

ie11CustomProperties

This polyfill option involves serving a client-side script, rather than preprocessing the CSS. We can add the following script to our head to ensure the polyfill will only be loaded in IE 11:

<script>window.MSInputMethodContext && document.documentMode && document.write('<script src="https://cdn.jsdelivr.net/gh/nuxodin/ie11CustomProperties@4.1.0/ie11CustomProperties.min.js"></script>');</script>

This permits us to enjoy the full benefits of custom properties as in the examples here, so it’s the solution I decided to go with. It has a limitation where custom properties set in style attributes aren’t polyfilled. But I’ve tested it for the theming example above and it works just fine.

But what does this have to do with Tailwind? 

As we’ve already seen, utility classes — single-purpose classes that can be applied anywhere in our HTML — can make our code more reusable. That’s the main selling point of Tailwind and other utility class frameworks — the size of the CSS file you ship should end up smaller as a result. Tailwind makes multiple color classes available: .bg-red-medium would give us a red background-color property value, .text-red-medium for color and so on for border, box-shadow, or any place you can think of that you might need a color value. 

Colors can be defined in a config file:

module.exports = {   theme: {     colors: {       red: {         medium: '#e53e3e',         dark: '#742a2a'       },       blue: {         medium: '#3182ce',         dark: '#2a4365'       }     }   } }

If we want to use custom property values for our Tailwind classes, we can specify them in the config:

module.exports = {   theme: {     colors: {       'th-primary': 'var(--primary)',       'th-secondary': 'var(--secondary)'     }   } }

I’m prefixing my colors and theme-related class names with th- so that it’s obvious they’re specifically related to theming, but feel free to use whatever convention suits you.

Now those classes will be available to us through Tailwind. Using .bg-th-primary gives us the equivalent of writing:

.some-element {   background-color: var(--primary); }

In our CSS we can define our custom properties for our themes as before:

:root {   --primary: #742a2a;   --secondary: #742a2a; } 
 .th-blue {   --primary: #2a4365;   --secondary: #3182ce; }

Let’s apply those classes to our HTML. The first example gives us a component with our default theme (the variables defined on the :root). The second has our blue theme. The only difference is the addition of the .th-blue class on the component. (Once again, I’ve omitted the classes unrelated to the theme, for brevity and clarity.)

<!--Component with default (red) theme--> <div class="bg-th-primary">   <h3 class="bg-th-secondary text-th-primary">Join our mailing list</h3>   <div>     <p>Be the first to hear about our new offerings</p>     <button class="bg-th-secondary" type="button">Sign up</button>   </div> </div> 
 <!--Component with blue theme--> <div class="th-blue bg-th-primary">   <h3 class="bg-th-secondary text-th-primary">Join our mailing list</h3>   <div>     <p>Be the first to hear about our new offerings</p>     <button class="bg-th-secondary" type="button">Sign up</button>   </div> </div>

Using the config as a style guide

Tailwind encourages you to define all variables in the config, and personally I agree that it’s a better approach. It means that the config file can be a single source of truth rather than (potentially) ending up with multiple places to define your colors and other theme values. Luckily, we can also use values from the Tailwind config file for our custom properties. We’ll need to first define all of our colors in the config (assuming we’re not using the default color palette included with Tailwind):

module.exports = {   theme: {     colors: {       red: {         medium: '#e53e3e',         dark: '#742a2a'       },       blue: {         medium: '#3182ce',         dark: '#2a4365'       },       'th-primary': 'var(--primary)',       'th-secondary': 'var(--secondary)'     }   } }

Then we can access the theme object in the CSS:

:root {   --th-primary: theme('colors.red.dark');   --th-secondary: theme('colors.red.medium'); } 
 .th-blue {   --th-primary: theme('colors.blue.dark');   --th-secondary: theme('colors.blue.medium'); }

Wrapping up

I’m really excited about the benefits of being able to use custom properties without having to worry about browser support, even more so to be able to integrate them smoothly with our existing workflow. It’s hard to overstate the amount of time they will save us for theming. I hope that even if you’re not a Tailwind user, this article might encourage you to give custom properties a go for this use case.


The post Color Theming with CSS Custom Properties and Tailwind appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,

Material Theming: Making Material Your Own!

The web is a beautiful, expressive medium that’s evolved over time as trends and technology have changed. Moments of delight and flair are what set companies apart from one another. At the same time, today’s top products rely on scalable, component-based design systems to efficiently develop a coherent brand. And it’s more important than ever to have accessibility and a solid user experience baked in from the start! But these two worlds — creative web design and systematic web design — don’t need to be at arms. The beautiful gooey center of the web design world is where we can find a way to meld creative web design in with our systems, and luckily, you can have both!

One of those design systems is called Material Design (the team I just joined at Google!). Material is built on years of research and best practices to give designers and developers access to creating beautiful, accessible UIs with the least amount of work possible. But in its initial launch, one thing was missing from the equation: the ability to express a brand’s personality in an easy-to-implement way. The team heard two key pieces of feedback:

  1. Material wasn’t stylistically flexible enough to meet the needs of all products, brands, and users.
  2. It wasn’t easy enough to apply and build brand experiences systematically across products.

And as of last year (specifically, I/O 2018), Material announced new theming capabilities! Theming allows developers and designers to benefit from all the parts of Material that make it a world-class design system — and make it their own! In other words: You can customize the look and feel of Material Components by applying global changes to your product’s color, shape, and typography.

Material Components are built for multiple systems like Android, iOS, and Flutter, and we’re going to focus on the web for this post (this is CSS-Tricks after all). Let’s start off with a base login template using Material Components for the web to see how easy implementing a theme atop this will be:

About theming

The current theming implementations allow for color, typography, and shape adjustments that trickle down to every component in your product. These three subsystems may not sound like many options, but together they bring a big impact to the design, and are a great springboard to jump off of with more design changes on a more granular level.

Color

The first themable option is color. There’s a great color picker tool on Material.io for you to be able to see contrast and aid your color selection process. Since this is CSS-Tricks, let’s write some CSS (okay, more like SCSS), to get started with an example. In a few lines of code in our my-theme.scss file, we can entirely change the look and feel of this login screen:

We’ve set:

$ mdc-theme-primary: #26418f; $ mdc-theme-secondary: #d1c4e9; $ mdc-theme-background: #fdf6f9;

Even though we have not specified a $ mdc-theme-on-primary, the system knows to make this white now (using a Sass contrast function) to help us ensure accessible color contrast. We can also override the text color on the primary and secondary themes with $ mdc-theme-on-primary and $ mdc-theme-on-secondary. However, if no value is explicitly set, these will either be black or white based on the background. See the color picker for more information.

Typography

Material sets up a base typography scale, which can be customized with theming. You can adjust the typography for each individual headline level and apply it throughout your product in two ways:

  1. Using CSS classes to apply the styles. I.e. <h1 class="mdc-typography--headline1">
  2. Using mixins to extend the styles from a header component onto another (i.e. h1 {@include mdc-typography(headline1);}). You can see an example of this in our starter kit example on Glitch.

A quick way to change the typeface is by using Google Fonts — a really nice directory of free web fonts. We’re going to make this easy for ourselves and pick out a Google font called Josefin Sans. In order to use this in our project, we’ll need to import it, and then can set the base typography to use it:

@import url('https://fonts.googleapis.com/css?family=Josefin+Sans'); $ mdc-typography-font-family: unquote("Josefin Sans, sans-serif");

We use unquote here because the typography in Material is set in a map. We can specify more styles like so:

$ mdc-typography-styles-button: (  font-size: 14px,  font-weight: 600,  letter-spacing: 0.05em, );

Now our login page looks like this:

Shape

Shape is another way to be expressive with your code! What do I mean by shape? In Material, shape affects the corner radius of components, such as buttons, cards, and sheets. In product design, shape helps to define your brand. Is it angular and machine-like, or is it more rounded and organic? Changing shape trickles down through the rest of the system. Here are some examples of shape in Material:

The other cool thing about shape in Material Components is that you don’t have to have the same shape on each corner, or even the same component at different sizes. There are small, medium, and large components in Material, and you can apply different styles for these different component types, and you can mix it up and specify each corner, one-by-one, in a space-separated list of values just as you would with border-radius.

MDC Web does not currently support cut corners like the other platforms do since it is a non-trivial operation with current web standards. This is another reason I personally love Houdini and see a lot of impact for it in future CSS development!

In the example we’re working with, we have both large and small components. The large components are the text field inputs, and the small component is the “login” button. We can apply different shapes to these with:

$ mdc-shape-small-component-radius: 12px 4px; $ mdc-shape-large-component-radius: 8px;

And voilà! We have transformed our application in 12 lines of code.

Themes, themes, everywhere!

Now here’s the part where you get creative. There are so many ways to mix and match the elements of color, typography, and shape to really make your brand stand out. Here are four very distinct example apps that use color theory, typography, and shape to differentiate their products, each based on the baseline Material Components:

Themes IRL

Google itself customizes and extends upon the baseline Material Components with its own products. This is called the Google Material Theme, and it was defined by a number of product teams, including Gmail, Google News, Google Play, and Google Home:

But they are not the only company to use Material Design and its principles. A few companies have also extended Material Components in their own large-scale products.

Lyft, a ridesharing service, is just one example. They used an extended FAB component to highlight key actions, and designed it with a gradient to give it a unique feel that users were still familiar with. Lyft also leveraged Material’s elevation system in their own product.

Anchor, a free app that helps people record, distribute, and host podcasts, is another great example of making Material your own. They showcase their bold color palette, and integrate it with choice chips to delineate display options, a list of cards for content selection, and an extended FAB anchored to the bottom to remain persistently available for user actions.

Get started

Now you have everything you need to make own Material Theme with Material Components for the web. It’s easy to start building your web project with our Material Starter Kit, and exploring theming with the Build a Material theme Glitch project we’ve just released. Change around the variables in my-theme.scss to explore how they can customize the individual components. Then, you can open any template, paste those variables into the my-theme.scss, and start building.

TL;DR: If you thought that Material Design wasn’t for you in the past, it’s a great time to start exploring. Happy theming!

The post Material Theming: Making Material Your Own! appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]