Fizz's CSS framework is designed to allow you to compose just about any design without having to add many additional styles on top of it (if at all). Fizz has three pillars of styles, inspired by Andy Bell's CUBE CSS:
- Component Styles
Individual UI components are styled to be used in any context.
- Utility Styles
These single-purpose classes allow you to compose multiple components in any number of ways without having to re-declare styles in different contexts.
- Layout Styles
Rather than using a generic 12-column grid, Fizz provides a number of different pre-defined layouts (using CSS grid) that can be used based on the content.
Let's call this the COOL (CUL) methodology. 😎
Aside from some style resets and basic formatting, almost all of Fizz's styles are scoped with a
fizz- prefix to prevent clashing with other classes.
As we transition from existing styles to solely make use of Fizz styles, we've further scoped all of Fizz's styles inside the class
.fizz-styles, which should be applied to the
<body> or as high as possible in the DOM.
The Fizz CSS framework is built in SCSS and is roughly organized using the SMACSS methodology, where individual files are divided into sections and compiled into a single fizz.css file using SCSS
@import. Here's how it breaks down:
Design Tokens serve as the single source of truth for visual style decisions in Fizz. They're organized into three categories:
- Global Tokens include all of the possible values in the system. The color palette, type scale, spacing values, etc. If we want to change the value of our primary brand color, we can do that here and it will change everywhere that token is used. What once took us several weeks can now be done in a few seconds.
- Contextual Tokens describe how Global Tokens are used. They include common values that are used across multiple components, such as
$color-background-brand. Contextual Tokens allow us to change from one Global Token value to another across multiple components.
- Component Tokens are values specific to a single component and can point to Contextual Tokens, Global Tokens, or even one-off values. Component Tokens allow us to make changes to a single component — changing the background color of the CTA button from blue to purple, for example — without having to edit the app styles.
The tokens are included at the top of fizz.scss so that subsequent files can make use of them. They can also be included into page-specific styles (non-Fizz styles) via the npm module to take advantage of them outside of Fizz.
For more on Design Tokens, check out the Design Tokens page.
The base folder includes a simple style reset based on Andy Bell's A Modern CSS Reset. Among other things, it resets box-sizing to border-box, removes some default padding and margins, and removes animations and transitions for users that prefer reduced motion.
The base folder also includes some additional resets and default typographical styles. Most notably, styling for all headings is set to inherit size, line-height and font-weight from the
<body>. This helps force a separation between the semantic meaning of heading elements from their visual style. If a developer wants a particular type style for a heading, they can do so by applying a class despite whichever heading level element is used.
This section is the meat of the styles — the C in COOL. Each individual file represents a single component or group of similar components (roughly) and their variants. This makes it easier to find styles related to a particular component.
Component styles are named as vaguely as possible, to maximize their reuse potential. We try to avoid naming components specific to the context or the feature in which they are used.
Components only include the styles within the components. (That is, there are no external styles like margins applied.) To arrange multiple components into a larger one, we can use Utility classes, as detailed below.
Utility classes, popularized by frameworks like Tailwind and Atomic CSS, are single-purpose classes that can be combined to create almost any design using the system.
Traditionally, if we had a new feature, we might use a methodology such as BEM to apply a bunch of classes specific to that feature to apply things like margins and padding within a component, and name each child element accordingly:
<img src="/img/products/1234.jpg" alt="A widget" class="product-card--image">
<h3 class="product-card--title">A widget</h3>
<p class="product-card--description">This handy, dandy widget can do just about anything.</p>
<p class="product-card--price"><s class="product-card--strikethrough">$49</s> <span class="product-card--sale-price">$39</span></p>
While this might offer some degree of encapsulation, it also makes it nearly impossible to use any of these styles outside the context of a product card.
Instead, we can use a combination of Component and Utility styles to acheive the same result with more reusable classes, greatly lowering the overall size of our CSS and allowing for almost any combination within the system without having to add feature specific styles:
<img src="/img/products/1234.jpg" alt="A widget" class="fizz-stack-4">
<h3 class="fizz-heading-3">A widget</h3>
<p class="fizz-text-small fizz-text-subdued">WidgetCo.</p>
<p class="fizz-stack-16">This handy, dandy widget can do just about anything.</p>
<p class="fizz-text-price fizz-text-align-right"><s>$49</s> $39</p>
A few things are happening here:
Each class is describing a specific generic component or it's relationship to adjacent components.
.fizz-card, for example, is only describing the visual properties of the card itself: its border, border radius, padding, shadows, etc.
We don't need to specify the relationship of each child to its parent with class names. We can do that in the CSS, if necessary.
The class names are more verbose than you'd see in something like Tailwind, but they give us a pretty good idea of what this card will look like, even without knowing anything about the visual styles of the system. We also need to apply fewer classes than a pure utility-based framework because a lot of the styles are baked into the individual components themselves and we're just using utilities to specify their relationship to each other.
In addition to providing utilities for things like margins (
.fizz-stack-*), Fizz has utility classes for arranging components using flexbox, and/or applying consistent margins to all direct children of an element.
For more information, check out the Utilities page.
The ubiquitous 12-column grid, popularized by frameworks such as Bootstrap, tends to be overkill for most sites. It's built to make any number of combinations of columns but, in reality, we're usually only dividing the available space in half or thirds or quarters, or distributing items equally across the available space.
Developers tend to struggle with implementing these grids, particularly when they need to be nested. And designers rarely adhere to the grid or use it sparingly.
Instead, Fizz provides a number of different page layouts using CSS grid. As new requirements emerge and existing layouts don't suffice, we can add new layouts as needed.
The result is a much easier method for applying layout to a page, which requires a lot less code and mental effort to implement.
This folder contains styles for any third-party styles required by the system — things like a syntax highlighting or date picker library. Rather than pull these in as a separate file, we can include them in Fizz to reduce the amount of requests.