CSS best practices for fast-growing SaaS startups

I joined Sqreen in June 2016 as a web developer after working as a freelancer and in web development agencies. Since then, I’m in charge of the Sqreen Dashboard integration.

In this article, I will explain the challenges I faced: what I did and what I would have liked to do if I had to do it again. To illustrate the different stages, I’ll use the most common and used component: a button.

I'm a button

The beginning

When I joined Sqreen, there was no designer and the styling was relying only on Bootstrap (Less version) with some extra dependencies thrown in a couple of custom SCSS files. Really messy. My first challenge was to design a completely new style architecture for the entire visual product. We were planning to demo Sqreen at TechCrunch Disrupt, in less than 2 months.

The first thing I did was to swap Bootstrap for Bulma, to clean up the codebase and rely solely on SCSS everywhere. In my opinion, Bulma is the best CSS framework to use to start a new project, since it is well documented, easily customizable and intelligently organized. Thanks to this new stack, I was able to quickly generate huge CSS stylesheets using SASS tools. With such a limited deadline, the goal was to be fast and flexible rather than delivering an organized, maintainable and well-optimized project.

My feedback:

I think it was a good idea to move to Bulma. I could have kept Bootstrap but I was searching for something easier to use. When you don’t have time, always go to the easiest solution, even if you’re not proud of your code quality. At this stage, it didn’t matter. As you can see, the HTML is pretty simple. I’m using only classes on an HTML element. The CSS is simple too. I’ve just overridden some Bulma’s classes but in a bad way. I wanted to go fast.

<button className="button is-primary">
    I'm a young button
</button>
button.is-primary {
  background-color: #3D7ACC;
  padding: 5px 10px;
  &:hover {
    background-color: #5289d1;
  }
  &:active {
    background-color: #326fbd;
  }
}

3 months later: Time to clean up the house

I was pleased to welcome Romain, our current designer in the team. There was no well defined visual identity, and it was really tough for him to design our product. So we tried many things with many small iterations.

Iterations after iterations, we got rid of Bulma in favor of Romain’s design. I kept SCSS as it was the most comfortable solution for me to iterate fast and experiment new things. As we started to split our JS files into components, I started to break my SCSS into smaller files too. It was even easier for me because we used Zeplin as our primary communication platform with Romain. It generates CSS for each element and processes padding, margin and many others. At this stage, we started to have time to consider optimizing the code.

My feedback:

I’ve never been a fan of CSS frameworks. It helps a lot but doesn’t let you do the best part of your project. I’ve always preferred building my own for each project I’ve worked on. Bulma was a really good help but I’ve never planned to keep. Building your own framework helps you to organize your HTML structure as well. It’s not that easy when you’re not used to doing it but it’s a really good exercise. You can create better and faster components when you know each line of your code.

Here, I started to use some React properties. The CSS is pretty simple with some SASS variables and some mixins.

<button
    className={classNames(
        'button',
        this.props.color,
        {
            loading: this.props.loading,
            size: this.props.size,
            outlined: this.pros.outlined,
        }
    )}
    disabled={this.state.disabled}
>
    I'm a teenage button
</button>
.button {
  background-color: $grey-light;
  color: $grey-dark;
  cursor: pointer;
  font-family: $main-font;
  font-size: rem(16px);
  padding: 0.7em 1em;

    &:hover {
      background-color: darken($grey-light, 4%);
    }
    &:active {
      background-color: darken($grey-light, 3%);
    box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2);
  }

    // Colors
    &.primary {
    background-color: $primary;
    border-color: transparent;
    color: $white;
    &:hover {
      background-color: darken($primary, 4%);
    }
  }
  // Sizes
    &.medium {
    font-size: rem($button-medium);
  }
  &.large {
    font-size: rem($button-large);
  }
    etc...
}

1 year later: From an MVP to a Product

After months of design iterations, trying to define our identity, we reached the point where we could say that the design was quite stable. It was the perfect time to start building a UI kit based on components. We tried several libraries and we decided to go with Styleguidist. Really convenient and powerful. We’re three developers in the frontend team, and we enjoyed this tool that allowed us to play with components without dealing with the context of the product. And the designer was able to see how a component visually react when you put different data in it.

My feedback:

The transition between large CSS files and UI components can be really long. But if you start with the most used ones, it’s achievable. Splitting the CSS files into smaller ones also enabled us to move the CSS next to the related React component. It improved the global CSS readability and kicked off the process of getting rid of the global CSS files.

I started to use a React component to reuse the button everywhere on the dashboard. Much easier to use and understandable for us. In the CSS, I used some advanced SASS loops to generate the different colors, sizes, etc from SASS objects.

<Button color="primary" size="medium">
    I'm a dad button
</Button>
.button {
  background-color: $grey-light;
  color: $grey-dark;
  font-family: $main-font;
  font-size: rem(16px);
  padding: 0.7em 1em;
    &:hover {
      background-color: darken($grey-light, 4%);
    }
    &:active {
      background-color: darken($grey-light, 3%);
    box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2);
  }

    // Colors
    @each $name, $pair in $colors-inverted {
    $color: nth($pair, 1);
    $color-invert: nth($pair, 2);
    &.#{$name} {
      background-color: $color;
      border-color: transparent;
      color: $color-invert;
      &:hover {
        background-color: darken($color, 4%);
      }
    }
  }
  // Sizes
    @each $name, $size in $button-sizes {
    &.#{$name} {
      font-size: rem-calc($size);
    }
  }
    etc...
}

The next 3 months: improve the performance

We reached a new milestone: we now have a solid UI kit. Now, SASS is showing its limits and has become a performance bottleneck. We tested Glamorous, a library that eases the CSS usage in React, with Polished, another library that gives a lot of SASS-like tools. And I have to say it’s really good!

My feedback:

It depends on your needs and the complexity of your app but if you are using SASS or SASS like preprocessor, I recommend you to use PostCSS. Lighter and faster than SASS with a large set of extensions, it enables us to split our code, to make use of CSS-in-JS and many other great stuff. It has decreased the time building and removed some heavy packages.

If you don’t want to adapt your complex SASS files when moving to full PostCSS usage, it will be a painful process because you’ll be forced to use many extensions. Each extension has its own syntax.

Today, JS and CSS are continually interacting (colors, contextual styles, dimensions processing, etc). It’s now more interesting to switch to a complete CSS-in-JS approach.

Now my button can be moved everywhere. Almost autonomous, easy to maintained and can be really smart with, for example, fall back when no value determined.

<Button color="primary" size="medium">
    I'm a unicorn
</Button>
import glamorous from 'glamorous';
import { darken, rem, readableColor, rgba } from 'polished';
import { colors, fonts, buttonSizes } from 'assets/style-functions/style-variables';

const ButtonStyle = glamorous.button(props => ({
  fontFamily: fonts['main-font'],
  padding: '0.7em 1em',
  backgroundColor: colors[props.color || 'grey-light'],
  color: readableColor(colors[props.color || 'grey-light']),
  fontSize: rem(buttonSizes[props.size || '16px']),
  ':hover': {
    backgroundColor: darken(0.03, colors[props.color || 'grey-light']),
  },
  ':active': {
    backgroundColor: darken(0.04, colors[props.color || 'grey-light']),
    boxShadow: 'inset 0 1px 2px rgba(10, 10, 10, 0.2)',
  },
}));
const Button = ({...props}) => (
    <ButtonStyle {...props}>
    {children}
  </ButtonStyle>
)

Conclusion

Now our CSS architecture is pretty stable, optimized, maintainable and enabling us to scale our integration work. I had hard times to handle the CSS during this last year and a half. Between fast iterations, the SCSS where I was comfortable and my peers that pushed me to use newer tools like CSS-in-JS, I had to make some trade-offs. Obviously, the decisions I made could have been better. That’s why it’s always good to step back to spot the good and bad things you’ve done. It has given me the best lessons to improve myself for the next projects I’ll have to do.

And you, what was your first startup experience as a web developer?

Interested in learning How to build, test and deploy React Applications? Check out this article from our blog.