~wuz/wuz.sh

ref: ce48754d8b7eeb3273759c8b78c33c14ceadc13e wuz.sh/_includes/styles/README.md -rw-r--r-- 7.0 KiB
ce48754d — Conlin Durbin Add writing changes and fixes 10 months ago

I've been thinking about how we manage our styles currently and how we might manage them going forward, especially with the Design System and the number of engineers we have right now. I've read a good amount about how other companies handle CSS/SCSS and worked to create something that I feel fits our organization and gives us the ability to create maintainable CSS structures. We aren't here yet, but I think as we move forward with the design system, we have an opportunity to rethink how we handle some of our CSS. Without further ado, I'd like to present SCCSS (success!), a framework for managing CSS at scale at Lessonly.

#A Framework for SCCSS (success)

With a good framework in place, our CSS can be easy to work with and simple to modify. The framework used here is based on Smoothie CSS and is defined as such:

All styles at Lessonly belong to one of three categories: Token, Framework, and Component.

#Tokens

Tokens are the base units of the design system, represented in Yaml and parsed into other file templates (SCSS, JSON, etc.) as needed. Tokens are a shared resource meant to be accessed by engineering and design. Here is an example token file:

# Colors.yml
# All colors used in the Lessonly application
metadata:
    type: color
props:
    brandBlue:
        friendlyName: 'Brand Blue'
        hex: '#4a90e2'
        hsl: '212, 72%, 59%'
        rgb: '74, 144, 226'

At some point in the future, Token files might be autogenerated from a Sketch file, but for now they are just edited by hand.

#Framework

The Framework category of SCCSS is defines a number of global styles for use around our codebase. It is built in a modified version of ITCSS and is focused on making providing a platform to maintain consistency across the application. There are 4 sub-categories of files in this section:

#@ (ats or "application tools and settings")

Ats are the files related to creating tools and settings for use within other CSS files. They shouldn't output CSS of their own and instead should create @mixins, @includes and @imports for other files. This also is where the SCSS token output is imported into the application. It also includes %placeholders, but they should be used sparingly and follow the single responsibility principal. Finally this is where any 3rd-party CSS should be imported.

#Generics

This section includes any generic HTML styles that should appear in your application. This also includes any reset or normalization CSS. All selectors in this section should be basic HTML tags or attribute tags and should be relatively short. Here is where you would define default styles for things like h1s, ps, or buttons.

#Objects

Objects are reusable patterns that pop up around the application. Here we can find commonly used layouts like grids and lists, as well as things like the media object or flag object. All classes that appear here should be prefixed with .o- and should be used in conjunction with their corresponding UI library elements. For example, grid.scss might contain:

.o-Grid {
    display: grid;
    
    .o-Grid--threeCol {
        grid-template-columns: 1fr 1fr 1fr;
    }

    .o-Grid--fourCol {
        grid-template-columns: 1fr 1fr 1fr 1fr;
    }
}

This would be in conjunction with Grid.js, which might contain:

const Grid = ({columns = 'four', children}) => {
    return (
        <div className={`o-Grid o-Grid--${columns}Col`}>
            {children}
        </div>
    )
}
#Utilities

Utilities are simple CSS classes that are used to override other styles. They should follow the Single Reponsibility Principal and often will only contain one line of CSS. They may contain !important tags as they are supposed to override all CSS with the same properties. These classes might be helper classes, hacks or overrides and are often not incredibly elegant in their design. These classes should be prefix with .u-. An example might be a clearfix utility:

.u-clearfix {
        clearfix: both;
    }
#Components

Finally, Components make up the building blocks of the application. These classes are individual, specific elements of the UI that make up the application. Components follow our formatting guidelines and should be locally scoped, affecting only the CSS in thier own file. These files should be commented according the the commenting guidelines below. For example, the Button UI component might be defined as such:

/*
  Button component

  .Button:hover         - Style for a Button that has been hovered
  .Button--default      - Default style for a Button
  .Button--inactive     - Inactive style for a Button
*/
.Button {
    &.Button--default {
        background: $brand-blue;
        color: $white;
    }

    &.Button--inactive {
        background: $brand-gray;
        color: $brand-gray-darker;
    }

    &:hover {
        transform: translateX(-2px);
        box-shadow: $hover-box-shadow;
    }
}
#Putting it all together

All of these piece working together allow us to write easy to read, understandable CSS that we can maintain simply. Here is a quick example of the Button class, using Tokens, the Framework, and a Component file.

/*
  Button component

  .Button:hover         - Style for a Button that has been hovered
  .Button--inactive     - Inactive style for a Button
  .Button--default      - Default style for a Button
  .Button--gradient     - Gradient style for a Button
  .Button-text          - The text content of a Button
*/
.Button {
    // This button will be normalized across browsers from a setting in Framework/Generic
    // That means it is ready to be styled according to our needs

    // This button could be included in a layout established by the Framework/Object section
    // It's text-align or clearfix could be overwritting with a Framework/Utility class

    @extend %reset-button; // <- using a placeholder defined in the Framework/ats section
    @include when-lang('ar') { // <- language override imported from Frameworks/ats
        text-align: right;
    }

    text-align: left;

    &.Button--default {
        background: $brand-blue; // <- color variable imported in Framework/ats and defined in our tokens
        color: $white;
    }

    &.Button--inactive {
        background: $brand-gray;
        color: $brand-gray-darker;
    }

    &.Button--gradient {
        @include gradient-vertical($brand-purple, $brand-purple-dark); // <- Gradient mixin defined in Framework/ats 
        color: $white;
    }

    .Button-text {
        font-size: $font-normal; // <- Font size imported from Tokens
    }

    &:hover {
        transform: translateX(-2px);
        box-shadow: $hover-box-shadow;
    }
}