Transcript
Rambacher: We’re going to balance flexibility and consistency in our design systems, engage your community, share your expertise. We’re going to make it easy. I am the Technical Product Owner and a contributor to the PatternFly design system at Red Hat. I’ve been at Red Hat for 8 years. I mostly write CSS. I’m what Brad Frost calls a front of the frontend developer. I think I’m also a magician. I became the technical product owner about a year ago. Before that, I worked for many years on a corporate intranet and its various enterprise applications, time reporting and meeting room scheduling and a corporate phone book that, oddly enough, became the repository for all the technical expertise in the company.
History of PatternFly
I’m going to give you a little bit of background on PatternFly, our design system. It’s an open-source design system created for Red Hat’s enterprise applications. Here’s how it looked 8 years ago, when I started at Red Hat, was on version 3. Was using Less and CSS layered over Bootstrap. It had components implemented in Angular, and a little bit of the beginning of a React component library. About 6 years ago, we did a complete redesign. We rewrote all the components in React. We switched to Sass. The HTML structure diverged from bootstraps. An important addition at this point was that we added CSS variables as the preferred way for consumers to customize the components.
A year ago, we put out version 5 that brought in React 18 support and the dark theme, a few other enhancements. It was also supposed to include a big visual refresh besides dark theme. This visual refresh, we codenamed Penta. In the end, we pushed that visual refresh to version 6, and therefore we learned our first lesson, don’t use a name meaning five, in case it doesn’t make it into version 5. I’ll talk about how this release ended up being a big deal and some more lessons. Right now, we’re on to version 6. We’re currently working on it. Beta should be out if I get back to work and finish up a few things. It definitely does include a complete visual refresh. It has design tokens. I’m going to talk a lot more about those later.
Balance Flexibility and Consistency
A design system is a library of solved problems. This is a quote from Josh Clark. I really like it, because that’s why we have a design system. Rather than saying it’s this technology or it’s that, it’s focused on design, or it’s focused on providing implementations that you can take and use. It’s there so that we don’t have to solve the same problems over again. Probably the number one job of the design system is to promote consistency, but this is also 100% a balancing act, because as a design system, we would love to tie everything down, make everything perfectly the same, make all our components plug and play. It would be easier for us to develop the design system. It would be easier to package it up for the consumers to use the components.
In reality, all the applications using the design system all have their own unique requirements, and there’s just no way that one size fits all. We have to build in some degree of flexibility, but it’s always balance of the more flexibility there is, the less consistency, and the more work it is to implement it. We’re still finding that balance. Over time, we made the components more composable so they’re less prescriptive. We let the consumer build them with some flexibility as to what goes in.
For example, our select component started out providing a lot of functionality. Consumers asked for a lot of variations: item descriptions, favorites, a footer, a badge counting the number of items selected, filtering, type ahead. We had 28 different examples with various options. We rewrote the select component to be more composable. The downside of that was that now it required the consumer to do a little more wiring up to make things work. Our consumers flagged this as a potential blocker, because some of what they were getting for free now would require some work to implement. We tried to find a balance. We looked at it, and now we’re introducing templates which pre-build, not all, but some of the variations.
Engage the Community
If we hadn’t talked to consumers, we would probably have some very unhappy teams. Which brings me to engage the community. We’ve done a lot of work to increase adoption of PatternFly across products. We hold regular community meetings and office hours because we want to stay connected. We actively partner with products teams to test releases. That’s where we found out that creating the more flexible, composable components would delay or decrease adoption of the new version. We’re an open-source design system, and we welcome contributions from the community.
In reality, most of our consumers are pretty busy with their own projects. It’s super important for us to stay connected and to make sure that what we see as good for the design system is also good for the community. If you’re on the side of the product team, the consumer, I challenge you to stay connected with your design system team, because we want that connection. We need that connection.
Provide Centralized Expertise
We don’t like to think that we’re experts in everything, but of course, we are not. A design system should leverage its centralized team to provide expertise out to the consumers of the design system. Take accessibility. Can you rattle off the formula for color contrast to achieve double A rating? What’s happening with the contrast calculation in WCAG 3? How do you make a menu accessible? There’s a lot to it. Accessibility can be an afterthought for product teams. They’re focused on building the thing, and the level of expertise can vary widely, team to team. By making sure that the design system team has experience like that, then that knowledge can be built into the design system, and therefore all those consumer apps are going to be that much closer to having great accessibility.
Accessibility, of course, is more than just color contrast. It’s keyboard interaction. It’s semantic structure. It’s ARIA labeling to ensure screen readers can make sense of the page. There’s a lot to know, and the more that we could build into the components, the better. What we couldn’t build in, we documented, both at the system level and within every component. When we created a scorecard to check that we are doing things well, we also made that available to products so they can reuse that expertise and make sure that they’re doing a good job too.
Make it Easy
It’s easy to get a little lost in building the perfect set of components on our own timeline. Consumers of the design system want improvements and enhancements, but they don’t want disruption. We need to remember that they’re on their own path, and they’re bringing our design system along. One of the most useful things we’ve done to help product teams save time is to create a seed project. It’s a jump start, providing a scaffolding for the web app, and it also serves as a reference for configuration. All the essentials are there. Consumers just need to clone the repo and install it. Just these few lines of code, and they’re started with a new project.
Just in case, we also provide information about some common problems you could encounter and how to solve them. That gets them started. Then we want to make sure that they’re keeping current with the latest PatternFly, which means we need to make it easy. Again, we have a balance between making lots of great updates and breaking changes. They’re going to take work for them to consume. I mentioned that version 5 turned into a big breaking change. As we were nearing the v5 release of PatternFly, we realized that we had a problem. We had several ecosystems of applications consuming PatternFly that use the model of a chroming layer, and tenant apps within the content area. Maybe you call this an application wrapper. We have separate apps living on the same page, so the CSS of one affects the CSS of the other.
There was just no way that all these applications could move in sync to version 5. There were too many, with different teams, different roadmaps. With the chroming application upgraded to version 5, that meant that we were going to break all those tenant apps that were still on version 4. Very late in the schedule, we pivoted to introduce a version namespace in all our CSS variables and classes, which sounds like a search and replace, but you’d be surprised at how many little details there were to take care of.
Here we go. We are ready to do another set of breaking changes for version 6. We just revved the version number. That would have been great. That would eliminate all the conflicts in our CSS. Should have been easy, except that, as I mentioned, we’re doing a completely new visual refresh. Some of the tenant apps were still actually on version 4. I can tell you that designers really don’t like seeing old and new looks mixed up together on the same page, so we’re trying to be proactive. We’ve engaged early with the owners of the chroming apps.
Ahead of release, we’re getting the upgrade onto their roadmap, and hopefully onto the roadmaps of the tenant apps. The new look and feel is a little bit of a carrot for tenant apps to update. To ease the burden on the products, we’re creating codemods, and we’re sending some of our developers to those teams to help them out in any heavy lifting that needs to be done. If necessary, we’re going to do a bit of temporary theming to bring them a little closer together. Luckily, that’s possible, because this release also introduces design tokens. Yes, they’re sparkly.
Design Tokens
At its simplest, a design token is a name and value pairing. Remember I said, I’m a front of the frontend developer. I’m going to talk in terms of CSS, but, of course, it also applies to native mobile apps or whatever you’re working in. I think CSS is awesome. I think CSS with design tokens is even better. Salesforce Lightning Design System calls them visual design atoms of the design system. Material says they’re small, repeated design decisions that make up a design system’s visual style.
My favorite definition I heard recently in a talk by Steven Jencso from Stormdog Design. He said, a design token is a documented design decision. I love this because it makes clear that tokens are intentional. Each one is a decision. I love the word documented, because, as you will see, that’s a very important part of making the design tokens work and making everyone’s life easier. In PatternFly, we’ve written the definition of a design token as a name and value pairing that represents a small, repeatable design decision. A token can be a color, font, style, border width, border radius, a unit of space, or even a micro-animation designed for a specific need. A single token can have multiple values that change depending on theme. Let’s see what they look like in action.
Who sees color hex codes hardcoded into their CSS properties? I bet a lot of us do. Maybe we now have some variables defining our palette colors. If you’ve been writing any CSS lately, you probably recognize CSS variables, but if not, all those names starting with — are CSS variables. You’re going to see a lot of them in the upcoming slides. You have some variables for your colors, but what exactly is gray-200? Is that on the light end, the dark end? I can never remember. You want to make the text color change on hover, what would be a good choice? Will going down 100 be enough? Going down 200, or were you supposed to go up? I can never remember.
If we have meaningful variables, and these are our tokens, the documented design decisions, when I’m coding the button, I barely have to think about what color to use. It’s right there in the name. I trust that my designers have worked out what colors work with each other. Say I have a button that I want to look like a primary button. What color do I choose for the background? How about the text? I don’t have to worry about what the color is, I just use the token for the primary background color. I use the text color that goes on primary. I’m shortening the actual names here, for simplicity. I probably need a hover state for my button. Again, I can use the tokens that are specified for the hover states of my primary background color and the text color that goes on top of that. I don’t care what the hex code is. I don’t even care if it’s blue-100 or orange-50. I apply tokens that have meaning. Tokens are more than just colors. They can be any design decision, spacing, font attributes, borders, box-shadows, z-index, even parameters for animations.
Naming is hard. Figuring out how to name our tokens was probably one of the toughest parts of implementing them, and one of the most important, because that’s how everyone is going to know what each token really means. I didn’t know who Phil Karlton was. I had to look him up. I found his original web page from 1997. Not one bit of CSS on that page, but I’m glad we have it now. Back to design tokens and naming things. Nathan Curtis wrote a really excellent article going into depth about naming and categorizing and grouping and layers. I recommend reading the article. He talks about many of these. There are lots of ways to organize tokens, and there are few diagrams illustrating various schemas. You’ll notice they’re all separated into layers of some sort, into a taxonomy.
We started out calling our schema the 7-layer dip, although it’s evolved a bit, and I think perhaps we lost the guac. We’ve got our namespace, pf for PatternFly, t for token. We’ll have a version number v. Then we have our schema layers. Scope, we actually just have two, global and our charts package. Component refers to what the token relates to, like icon, background, or text. The property of a component is something like color, size, width, or radius. Now we have something like global text color or global border width. Concept relates to higher level concepts like status, primary or action. Concepts go with variants like danger, warning, or success, or might be a variant of a component like plane or compact.
Finally, there’s state, which for us is mainly default, hover, or clicked. Sometimes we have all the pieces. Sometimes not, depending on the particular token. Always plan for evolution. You’ll find as you build it out, something doesn’t fit. You’re missing something. You want to call something different. This is how you select what token to apply. You can think of it as the API to your design. I highly recommend finding yourself a good fuzzy matching autocomplete plugin for your editor, because there will be a lot of tokens. If you know that you’re trying to find the right background color for a primary button, you start typing BG color primary, it’s going to pop up all those choices. It’s so much easier than looking at a mockup with hex code, 0066CC. You look that up to find that it’s blue-50. Then when you go to do hover, there’s no hover specified. This is much easier. You just use what the tokens tell you to use. That’s one aspect, the classification of meaningful tokens across your system.
There’s another distinction between tokens that’s really important. I’m going to switch to a cake metaphor here. Although it’s more like this. We have a 3-level structure within our design tokens. This distinction divides the design tokens into layers, and the first is the palette layer. I think of this as the universe. It’s every color that’s possible in our interface. In our system, and I’m going to leave off the namespace, so I’m not saying pft all the time, they have names in our taxonomy, like color red-50. Here’s our palette, just to show you what I’m talking about.
On top of that we have the base layer. These tokens start to assign meaning to the palette tokens. I think of it more as all the possible valid values or properties in the design system. Here we introduce concepts like the range of primary colors or status colors. Now we’ll start to see things like global–text–color–100, global–text–color–200. All the base color tokens are going to point down to that palette color layer. Then we also bring in more than just colors. There are base dimensions, like icon size or border width. We even have some base motion tokens, like the delays and durations and easing functions that we’re going to use for animation.
Then the third layer, we have semantic tokens. These are the ones with real meaning that can be tied to the interface, and you can see it in the code. In fact, we don’t want anyone to use the base and palette tokens themselves, only the semantic tokens should be used. The base and palette are the building blocks in our system. Now we can see real meaning in the name, global–background–color–brand–default, global–background–color–brand–hover. Semantic tokens take base tokens as their values, and sometimes other semantic tokens as their value as well, if it’s meaningful. For example, we have a global–text–color–subtle, but our global–text–color placeholder uses that subtle text color as its value. It’s probably a little confusing, so here’s a picture to illustrate it. On the top, we have a semantic token for global–color–brand–default. It’s our default brand color. It’s in turn using a base token called color–brand–200, which in turn is using the palette color blue-50, and that’s where the actual hex value is defined.
Why so many layers? Dividing the tokens into layers means we can easily accomplish theming and customization. We can change values at a particular layer and affect the outcome in different ways. You can do so relatively safely, because you know what’s going to affect what. Let’s look at some examples. Say the brand team decides to adjust the reds in the official palette. Design will update the fundamental colors in the palette layer. Because tokens in the palette layer are used by the higher layers, that change is going to affect everything that is based on that fundamental color. The change will spread up through the base and semantic tokens to affect the entire interface.
The base layer assigns a fundamental concept like danger color or primary color, or fundamental spacers. Changing the base level tokens will affect just the elements that rely on that concept. The semantic layer is really tied to much more specific concepts, so changing a semantic token will have a much more targeted effect. Here’s an example using one of our wizard pages. If I change the palette color, it’s going to affect everything that’s based on it. Here it’s the wizard step number, the primary button background, the secondary button border, and the link color. All of those change. If I were to change a semantic token that’s related to the step number, it’s going to be much more targeted. You’ll see that the step number highlight changes, but the button and link colors stay the same. Dark theme just becomes an adjustment of the base and semantic tokens.
Without tokens, we had custom styles on almost every element, reassigning the component styles to dark values. It was brittle. It was error prone. Had to be done individually everywhere. Now we keep our palette layer the same. Remember, that’s our universe of colors for our design system. The base tokens are reassigned to swap light values for dark, and vice versa. In some cases, semantic tokens are tweaked to make adjustments to which base colors are used. When we wrap those reassignments in a class, and when that class is applied to the body, the entire interface switches automatically.
Now design can actually tweak colors and spacing and all the things that your tokens control. We’re bridging the gap between mockups and code. Here’s our process. Our designers work in Figma and have implemented tokens as Figma variables. They’ve built an entire library of Figma components based on token variables. Those palette base and semantic layers make their life much easier as well. All the designers use that kit and get all those components for free. If the core design team decides to change something, that flows out through the mockups that they’re doing as well. We build a custom exporter to get those variables out as JSON. We continue to refine this export.
For example, started out exporting just a couple of files, so we just had a quick and dirty click on each one. Save it. It’s grown, now we have 11 files. We invested some time to make it one-click, making it easier, reducing some friction. We’re using Amazon Style Dictionary to transform the tokens into CSS variables. We need to consume CSS variables, but you might export Sass variables or TypeScript definitions or Android resources. Style dictionary provides formatters and transforms to do the translation, and you can write your own as well. We’re currently doing some refinements here too.
For one thing, whenever we added tokens, the order of the generated CSS file changed slightly. We’re just addressing that with some sorting. It’s not absolutely necessary, but again, it’s making our life easier just by reducing one more thing to worry about. We’re also working on bringing in descriptions of each token’s intended use from Figma through our documentation framework. This was always planned, but on the back burner, until we started having others begin to use the PatternFly tokens in their products. They need that documentation to feel confident that they’re using the right tokens. We’re also writing up specific documentation to help consumers understand our 7-layer dip and our 3-layer cake. We’re cautious about removing every human step in automating the Figma to code pathway. We’re talking a lot about what constitutes a breaking change in tokens.
In some sense, every change to tokens could be considered a breaking change because it’s going to ripple through as a visual difference. For now, we’re being a little bit intentional about pulling the tokens all the way through to PatternFly code.
As we prepare to roll out the tokens to our consumers, we’re learning that we do need to do some outreach to make sure everyone understands how the design token system works. The tokens are pretty much self-documenting, but it is really a mind shift. Product developers have been used to looking for that particular color of red that’s on the mockup, searching for that hex code, finding that it’s red-400 or whatever. It’s really a shift for them to understand that they don’t need to do that, instead they need to look at, I’m setting the background color of a danger button, so I’m going to use the danger background color.
Summary
Balance flexibility and consistency. Engage your community. Listen to them. Let them know your plans. Work with them. Share your expertise. Design systems are a shared resource, so your impact is multiplied by every consumer of your design system. Look for where the friction is, both for your own design system team and for product teams. Make everyone’s life easier. Don’t forget, design tokens are awesome.
Questions and Answers
Participant 1: Do you have plans to publish themes so that other people can pre-customize based on your taste that is so good?
Rambacher: It’s possible that we’ll start with a classic theme, because, as I said, we may need to do a little bit of easing into the backward compatibility. That may be the first one. We may have a need for working with teams to theme, maybe to look like other design systems that they need to live with. Then there’s also the possibility of other themes, like a high contrast theme, may be a good thing for accessibility as well.
Participant 2: I’m curious about your journey through the 28 variants of the drop-down component. We are running into a similar issue, where the designers want their creativity, but they haven’t come up with all the variants at once. Now we are going through our development cycle, and there is like this conflict between the designers and the devs. Because once they build something, hand it over to the content authors, and then the designers come up with another variant. How did you manage through those cycles in the design system process?
Rambacher: There is a process for bringing in new variants to make sure that it’s something that it’s more than just a one-off. Assuming that it is useful to more than one team, it’s a balance between whether we bring it in and implement it as one of our components, or if we make it an extension or part of this template. Look at the use and how different it is. If it’s different enough, it might be a whole different component.
See more presentations with transcripts