Keeping Things Organized
Scaling the Microsoft Teams Component Libraries (TCL) in Figma
The Teams Component Libraries (TCL) are the Figma UI kits that adopt Fluent UI components but are themed and customized for Microsoft Teams. TCL contains four kits, covering every platform type.
This is a prescriptive article and resource that highlights how the TCL — Web & Desktop kit was built, how it is maintained, and the parameters we set up for people to contribute.
We will cover the following topics:
- Structuring pages
- Naming things
- Aligning with code
- Design tokens
- Version control
1. Structuring pages
When deciding how to structure our pages the following priorities were considered:
- We must provide clear component location endpoints.
- We must make it easy to import components.
- We must provide clear health status indicators of components at said endpoints.
The conclusion was that each component should get its own page. This makes it easier for designers to scan a page and quickly copy what they were looking for, rather than trying to visually filter out what they don’t need. The status of a component is at the top level and the designer can see this before even importing. These ideas came from aligning closer to code.
A common file structure practice in React or TypeScript is to group by file type where each component has its own subfolder nested inside a parent folder like this:
src/components/Button/Button.tsx
💡 Note: src means “source.”
Page status indicators
The name of each component page has a status emoji at the end.
- Green (🟢) means all good and the component is healthy and ready to use.
- Yellow (🟡) means that the component can be used but exercise caution as it is being worked on.
- Red (🔴) means do not use under any circumstances; the component is unstable.
2. Naming things
The goal is to have a 1:1 component naming parity with code, but there are a few rare exceptions. This principle helps designers and engineers easily communicate with each other and identify what goes where. There are a few guiding naming conventions that are used and helped inspire our naming structures. Let’s review BEM and PascalCase:
BEM
BEM stands for block, element, modifier.
Breaking this down:
- Block: a standalone entity that is meaningful on its own. Examples:
header
,container
,menu
,checkbox
,input
. - Element: a part of a block that has no standalone meaning and is semantically tied to its block. Examples:
menu item
,list item
,checkbox caption
,header title
- Modifier: a flag on a block or element. Use them to change appearance or behavior. Examples:
disabled
,highlighted
,checked
,fixed
,size big
,color yellow
We don’t use BEM literally, more as an inspiration. If you want to learn more about how BEM works, go to http://getbem.com/.
PascalCase
PascalCase is a naming convention in which the first letter of each word in a compound word is capitalized.
“Software developers often use PascalCase when writing source code to name functions, classes, and other objects. PascalCase is similar to camelCase, except the first letter in PascalCase is always capitalized.”
We use PascalCase when naming components.
Layers and subcomponents
We name layers and subcomponents to communicate semantic meaning specifically to designers and contributors to TCL. Avoid trying to map layers to CSS classes since the web implementation will not always mirror the design.
To explain how we name layers and subcomponents, let’s start with a simple example. Here is the Button
component:
Because we have 240 variants of Button
inside of Figma, we have created a subcomponent.
A subcomponent contains all the necessary content for a component to function properly while allowing for global, sweeping changes across multiple instances.
Since we do not want this subcomponent to be available to the end-user (other designers) we prepend the name with a period and title it .Base
.
If we were to have multiple subcomponents we would enumerate starting at 1 e.g. .Base1
, .Base2
, .Base3
, etc…
“To prevent Figma from publishing your styles and components, you can add a prefix or modifier to the component’s name in the layers panel.
Figma will skip over these styles and components during the publishing process and list them in the Private to this file sections of the Assets panel and library modal.
• Add a period
.
in front of the component layer's name.• Add an underscore
_
to the beginning of the component layer's name.”
If we wanted to make the subcomponent available to users we would not append with a period nor use the .Base
method.
Rather, we would use what is in code and if that name is not available, we’d semantically name the component.
For example, in code, we do not have a subheader component for the DatepickerCalender
but we do have a DatepickerCalenderHeader
component. So, we’d take the lead from that component’s name and call our subheader DatepickerCalenderSubheader
.
Continuing with our Button
example, we start with .Base
nested in Button
…
Let’s drill down again…
The Button
requires min-width: 96px
for the medium-sized variant so we added two fixed-width vectors, one at the top and one at the bottom. The fallback name for anything that does not fit the naming convention is:
"Vector"-[description]
When using auto layout frames we structure the name like this:
[Element]-[element]-[element]-"stack"
💡 Note:
- Use sentence casing; only capitalize the first word in the list of items.
- Limit the number of element names to three. If there are more than three do not write them in the name.
When we look inside the Icons-string-loader-stack
we see a few things:
- The first element is an
IconContainer
which is a custom component built to handle style overrides and wraps theIcon
component. Since there are two of the same component and it technically is justIcon
, we group these and get the first part of the name —Icons
. - The second element is a text layer. We structure the name of text layers like this:
"String"
or if multiple text layers are present, we append the name like:
"String"-[description]
e.g. Sting-title
. We now have the second part of the name — Icons-string
.
- The third and final element in the stack is a frame called
Loader-container
that wraps a component calledLoader
. We will now add this to the end of our stack’s name and calling itIcons-string-loader-stack
.
💡 Note: When using a frame with no auto layout in Figma, structure the name like this:
[Description]-"container"
3. Aligning with code
Aligning our Figma components beyond just their names with our coded components furthers our mission for 1:1 parity with code. These efforts continue to drive shared context between designers and engineers: this is critical for an elevated experience of both parties. By creating a common language with code and developers we reduce the deltas between design and product.
Before we continue, let’s review some key terminology:
API
The term API is an acronym, and it stands for “application programming interface.”
“Think of an API like a menu in a restaurant. The menu provides a list of dishes you can order, along with a description of each dish. When you specify what menu items you want, the restaurant’s kitchen does the work and provides you with some finished dishes. You don’t know exactly how the restaurant prepares that food, and you don’t really need to.”
Props
Props (properties) or variants as Figma calls them are the inputs that each component is equipped with and define the component’s capabilities.
“Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.”
Boolean
A boolean
is a true or false data type.
“A boolean or bool is a data type with two possible values: true or false. It is named after the English mathematician and logician George Boole, whose algebraic and logical systems are used in all modern digital computers.”
Enum
An enum
represents a group of constraints or unchangeable variables.
“In computer programming, an enumerated type (also called enumeration or enum [..]) is a data type consisting of a set of named values called elements, members or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language.”
Now that we’ve covered some key terms, we are ready to dig into an example.
Button example
In the example below we are looking at the Button
component inside of Figma. On the right side of the image, you will see the list of available props that a user can select.
Ideally, our Figma components’ APIs are 1:1 with code, but this is not always the case. Here are a few situations where we deviate:
- Enums vs. booleans — Sometimes it makes sense to group an array of choices versus offering several true or false options. For example, the coded version of
Button
acceptsboolean
values for appearance styles such asprimary={true}
orinverted={true}
, our Figma version ofButton
has a single prop calledAppearance
that can take a value of"Default"
,"Primary"
,"Outline"
,"Subtle"
, or"Inverted"
.
- Prop name vs. propName — We choose to format the name of props slightly differently than our coded counterparts. For example, in code
Button
has a prop callediconPosition
which can take values. But in Figma, we capitalize the first word and use a space. So,Button
in Figma has a prop calledIcon position
that can take values. This makes the component more friendly and easy to parse for designers.
- Sentence casing vs. lowercasing — We always capitalize the first word of a prop or prop value. For example, in code
Button
has anenum
prop calledsize
that can take a value of"small"
or"medium"
. Our Figma version ofButton
has anenum
prop calledSize
that can take a value of"Small"
or"Medium"
. Again, more design-centric and easier to read.
- Showing props vs. not showing props — Props are not always applicable across design and code. For example, in our Figma version of
Button
, we use anenum
prop calledState
that can take a value of"Rest"
,"Hover"
,"Pressed"
,"Active"
,"Focus"
, or"Disabled"
. This logic is not applicable in code, rather the coded version only has twoboolean
props calleddisabled
anddisabledFocusable
. For designers to useButton
in their flows and mocks, they require all available states.
- Most options vs. all options — due to technical constraints in Figma and to limit the payload, we sometimes create additional components instead of a single massive component. For example, in code,
Button
has aboolean
prop callediconOnly
which renders only anIcon
and no text string. In Figma there are two separate components:Button
andIconButton
.
The “position” prop
The position prop defines where the position of a child node is placed in relation to its parent. We structure the name of this prop like:
[Element] "position"
or
"Position"
Either is acceptable. The reason we chose to use Position
rather than Menu position
in Menu
, is because in code the prop that is passed is called position
.
Let’s look at the MenuButton
example in Figma:
The MenuButton
has an enum
prop called Position
that can accept a value of "-"
, "Above"
, "Below"
, "Before"
, or "After"
, this prop defines the placement of the Menu
nested in MenuButton
.
Below is a chart of how we rank and name the options for a prop defining position in such a component. When setting values for an enum
prop in Figma, we usually need to set a default value of "-"
when the prop is not applicable, such as when the Menu
is not open.
- The first option is
"-"
: this meansnull
, or the absence of a value. When theOpen
prop isFalse
meaningMenu
is not opened, this is the fallback value. - The second option is
"Above"
: when true, the node is placed above the component. - The third option is
"Below"
: when true, the node is placed below the component. - The fourth option is
"Before"
: when true, the node is placed before the component. - The fifth option is
"After"
: when true, the node is placed after the component.
The “align” prop
The align prop defines how a child node is aligned to its parent. We structure the name of this prop like:
[Child] "align"
or
"Align"
Either is acceptable. The reason we chose to use Align
rather than Menu align
in MenuButton
is because in code the prop that is passed is called align
.
The aligned prop usually goes hand-in-hand with the position prop.
Below is a chart of how the align prop works. Depending on where a node is placed in relation to another node, we will either use the term “Start” or “End.”
4. Design tokens
Design tokens are essential to iterate quickly, design within defined parameters, and of course, align to code.
“Design tokens are the visual design atoms of the design system — specifically, they are named entities that store visual design attributes. We use them in place of hard-coded values (such as hex values for color or pixel values for spacing) in order to maintain a scalable and consistent visual system for UI development.”
Tokens in code
We strive to stay in sync with our live design tokens. In code, we support three themes: light theme, dark theme, and high-contrast theme.
There are a few types of design color tokens in code, but we will focus on the alias design tokens.
Alias design tokens are context-driven tokens.
Alias color tokens’ names in code are structured like:
[color].[surface][enum]
In design-speak? This just means that we would start with the name of the color, we would then add a period, and then we would add the name of the surface this token is for. Typically the surface is only a foreground or background, although there are some exceptions.
The word “enum” at the end means that if we have more than one token we can enumerate, starting at 0. We don’t actually write “0” though. We would append tokens starting after the first with “1” and count up.
Let’s continue…
When using a token for a state like :hover
it would be structured like this:
[color].[surface].["Hover" | "Pressed" | "Active" | "Focus" | "Disabled"][enum]
💡Note: a pipe (|
) simply means “or.”
“Union types in Typescript allows a variable to have the ability to store a value of several types. We define union types by using a pipe (
|
) to separate each of the possible types. Hence,number | string | boolean
is the type of a variable, which can have the value that can be anumber
, astring
, or aboolean
.”
Tokens in Figma
Two themes are supported in Figma: light theme and dark theme. The tokens nested in either the 🌕 Light theme
or 🌑 Dark theme
folders are 1:1 with each other. Meaning the only difference is the actual HEX or RGBA value. This allows for theme switching with our plugin or manual token swapping.
Color design tokens
Let’s take a look at an example. All of the tokens in Figma mirror the tokens in code. So we start with our doc site.
The two things we need to pay attention to are the palette name, in this case, that is “brand” and the token name, which is “Background.” Let’s transcribe this to Figma.
How tokens are named in Figma:
[Color palette] [Token name]
Notice we capitalized “Brand.”
The same goes for state-based tokens like Brand Background Hover
. We just take the literal name from the doc site…
A high-level overview of our color token organizational structure in Figma:
[Theme] / [State] / [Surface] / [Token]
More specifically…
["🌕 Light theme" | "🌑 Dark theme"] / ["Rest" | "Hover" | "Pressed" | "Active" | "Focus" | "Disabled"] / ["Foreground" | "Background" | "Border"] / [Token Name]
💡 Note: There are two exemptions to this convention though: the “Loading” and “Component styles” subfolders. More on that below.
At the top-most level lives the themes 🌕 Light theme
and 🌑 Dark theme
.
Drilling down under 🌕 Light theme
live the states. These are the six subfolders each named by state:
Rest
: tokens for default color — e.g. thebackground-color
forButton
at its primitive state with no current or pending interaction.Hover
: tokens for hover-context colors — e.g. thebackground-color
forButton
being hovered by a mouse cursor.Pressed
: tokens that for pressed-context colors — e.g thebackground- color
forButton
on mouse click.Active
: tokens for active-context or “selected” colors — e.g thebackground-color
forToggleButton
when selected.Focus
: tokens for focus-context colors — e.g. the focus-ring forButton
when a screenreader is focused on it.Disabled
: tokens for disabled-context colors — e.g thebackground-color
forButton
when disabled.
The bottom two subfolders below the state-based ones are:
Loading
: tokens for loading-specific components that require gradients such asSkeleton
andLoader
. Because of limitations in Figma such not being able to point gradient tokens to multiple variables, these are not 1:1 with code and specific only to our local styles in Figma. The end result is the same but the tokens do not match.Component styles
: tokens used locally to build components such as the shadows inAppBar
andScrollBar
.
💡 Note: the naming conventions in the Loading
and Component styles
folders are not finalized and may be subject to change.
Nested inside each of the state-based subfolders are one of three subfolders contextual to the surface type:
Foreground
: tokens for foreground elements such ascolor
for a textspan
Background
: tokens for background elements suchbackground-color
forButton
Border
: tokens for borders such asborder-color
forCard
Finally, nested in each surface-type subfolder are the alias color tokens. Tokens are sorted as follows:
Default
Silver
Brand
Red
Orange
Yellow
Green
Pink
Onyx
💡 Note: each token group is sorted starting at 0 (remember when 0 no number is appended to the token name). Not all tokens are available in Figma from code as they are added as uses are identified.
Shadow tokens
The shadow or “elevation” story is being revamped for new versions of Fluent UI which TCL will snap to. Currently, we name our shadows like this:
"shadow"-[enum]
and if the shadow is pointing in another direction other than down we name the token like this:
"shadow"-["up" | "left" | "right"]-[enum]
We start at “2” and work up to “32.” Shadows mimic the z-index
, meaning that shadow-2
is a lower z-index
than shadow-16
.
Typography tokens
Again, due to some limitations in Figma, we sort our typestyles a bit differently than code. New ordering is in the works.
Currently, we have six folders, sorted from smallest font size to largest font size, with a local styles folder at the end:
Smaller
Small
Medium
Large
Larger
Component styles
In each folder, styles are named like:
[weight number]
and if it is italic or underlined we name it like this:
[weight number] ["italic" | "underlined"]
They are sorted from the lowest number to the highest number.
5. Version control
Semantic Versioning or SemVer, in short, is a versioning system to incrementally organize published versions of a project. This is most common for engineers or software developers, but we have borrowed some of the principles for our Figma kit. The numbering is structured like this:
[Major].[Minor].[Patch]
- Major: these are massive versions, sweeping and breaking changes. This occurs typically only once a year, such as rebuilding a bunch of new components to align with a new Figma release.
- Minor: these are scheduled to occur on the last Friday of every month, usually a batch of new components or significant non-breaking component updates are grouped and released under this version.
- Patch: these are scheduled every Friday, except for the last Friday of the month. They include mostly bug fixes or small changes.
Communicating versions
There are three ways we communicate new versions to users:
- Release notes in Figma: before we hit that “Publish” button, we type our release notes in the input box inside the Figma publishing window.
- Changelog page in Figma: in Figma, there is a page titled “Changelog”, on that page we post our release notes.
- Design Systems Teams channel: to provide transparency and quick access to components that were added, modified, or moved to the Graveyard we post our release notes in a Teams channel.
Here is how we structure the release notes:
Teams Component Library Update: (TCL [date])-[V#]🎉 What’s New• [Component name with link] component
• [Component name with link] component
• [Component name with link] component🛠 What’s Modified• Bug fix for [Component name with link]
• Thing updated for [Component name with link]⚰️ What’s Moved to Graveyard• Old [Component name with link] component
• Old [Component name with link] component
• Old [Component name with link] component
Deprecating old components
When the time comes to deprecate old components, we do the following:
- Select the component to deprecate a move it to the page called “Graveyard.”
- Change the name to:
[Current name] / "⚠️ DEPRECATED MIGRATE TO NEW VERSION ⚠"
3. When publishing a new version add this component to the “⚰️ Moved to graveyard” section.
Final thoughts
Thank you for taking the time to read this article, many hours were put into making it consumable and digestible for designers. Future versions are being planned. Stay tuned for updates by following this publication on Medium and show your support by clicking 👏 the “clap” button.
Glossary
- API: an acronym that stands for “application programming interface”
- Auto layout: similar to CSS flexbox, auto layout is a property in Figma that you can add to frames and components. It lets you create designs that grow to fill or shrink to fit, and reflow as their contents change.
- BEM: an acronym that stands for “block element modifier”
- Boolean: a true or false data type
- Design tokens: the visual design atoms of the design system — specifically, they are named entities that store visual design attributes.
- Enum: a group of constraints or unchangeable variables.
- Prop: arguments passed into React components.
- Semantic Versioning: semantic and incremental versioning that takes place in the form of
[Major].[Minor].[Patch]
. - Subcomponent: a component that contains all the necessary content for its parent component to function properly while allowing for global, sweeping changes across multiple instances.
Sources
- BEM — Block Element Modifier. (n.d.). Get BEM. http://getbem.com/
- Computer Hope. (2021, February 1). What is a Boolean? https://www.computerhope.com/jargon/b/boolean.htm
- Facebook Inc. (n.d.). Components and Props –. React — A JavaScript Library for Building User Interfaces. https://reactjs.org/docs/components-and-props.html#:%7E:text=Conceptually,%20components%20are%20like%20JavaScript%20functions.%20They%20accept%20arbitrary%20inputs%20(called%20%E2%80%9Cprops%E2%80%9D)%20and%20return%20React%20elements%20describing%20what%20should%20appear%20on%20the%20screen.
- Figma. (n.d.). Create dynamic designs with auto layout. https://help.figma.com/hc/en-us/articles/360040451373-Create-dynamic-designs-with-Auto-Layout
- Hoffman, C. (2021, August 13). What Is an API, and How Do Developers Use Them? How-To Geek. https://www.howtogeek.com/343877/what-is-an-api/
- Tree Hozz. (2020, March 1). What is an enum in Javascript? https://treehozz.com/what-is-an-enum-in-javascript