Decibel: Acast’s design system

Acast Tech Blog
acast-tech
Published in
9 min readDec 11, 2019

By Antoine Lehurt

Logo: Decibel — Acast’s Design System

As a company grows, so does the number of different products. This introduces a level of fragmentation in both the overall user experience and in visual consistency.

UI components, libraries, guidelines and documentation are usually classed as a ‘nice to have’, aside from the quarter and sprint goals. And, without clear ownership, principles for what works best are kept within teams and might not be communicated to others in the organization who could also draw benefits from it.

We want teams to be able to focus on solving specific problems, and to increase their speed of iteration, without needing to spend time building the same components over and over again. Instead, they should have a library where UI details and accessibility have already been figured out.

Listeners, podcasters, publishers and advertisers use different Acast products owned by different teams, but it’s essential we give them a consistent experience and visual elements.

Our design system, Decibel, is our answer.

“A design system is a collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications.”

https://www.invisionapp.com/inside-design/guide-to-design-systems/

This article focuses on the component library of our design system, including how we work with it and the tools we use.

Why build our own instead of using an existing one?

There are numerous open-source component libraries. To help us decide what would work for us, started with our most important requirements:

  • It needed to reflect our branding, and ours alone. We don’t want to couple our visual identity to another company. We didn’t want to use Material Design, for instance. Its guideline principles have valuable information about interface practices and how to think about motion design, but it’s Google’s design language.
  • It needed to be easy to use in our front-end stack, because our feature teams share the same stack on the front-end.
  • It needed to be extendable. We work on different products for different audiences (audio player, audio editing tools, analytics dashboard, and so on), so it had to be easy to add components that fit our specific needs.
  • It needed to include accessibility support, because we want everyone to be able to use our products.
  • And it needed to have resources for both developers and designers. We wanted to use the same elements to simplify communication and share a common vocabulary.

But, throughout our research, we didn’t find the perfect match. The two main reasons for this were the lack of theming and accessibility support, so we decided to invest in building our own.

Interestingly, Reach UI has since been released, which could have been a good foundation for us as it focuses on accessibility.

Open source, but in-house

While it differs from company to company, the most common workflows are:

  • Centralized: one team owns the design system and builds everything for the “feature” teams.
  • Fragmented: several teams contribute to the design system and share ownership.

We decided to follow the latter — we want gardeners, not gatekeepers. Everyone should have a sense of ownership and feel that they can easily contribute to the component library.

Since more people are involved, it helps to share knowledge around the organization for the type of components that are available or needed, and about front-end and accessibility practices.

Communication

Acast is a remote-first company so, for Decibel, most of our communication happens in GitHub. We mainly use this for project management, requests for new components to the design team, and requests for feedback on APIs.

However, once a week, we have a meeting with the front-end group to discuss all things Decibel. It’s a great forum to give feedback, request help, or discuss issues, and has helped us speed up decision making.

Contribution guidelines

As for open-source projects, we have contribution guidelines accessible in the repository, to give as much information as possible to help new contributors — and to make sure we keep a consistent codebase. It covers:

  • Expectations around code convention, testing, code quality, accessibility, and so on
  • Git commit practices
  • File structure for a new component
  • What we expect in the pull request. To reduce code review time we encourage developers to open draft pull requests, to get early feedback about their ideas and implementation

Our contribution guide isn’t set in stone, however. We keep it updated based on discussions that inevitably arise during pull request reviews.

Tech stack

React

React is a component-based library maintained by Facebook. One of its strengths is that it allows us to build a hierarchy of components by composing different blocks together — so we can follow the Atomic design methodology when creating new parts.

It’s widely used in the front-end community and used in all projects at Acast, so its inclusion was a no-brainer.

Styling

With Decibel, we wanted to build component-oriented styles where we expose all building blocks as React components. We think CSS-in-JS solutions are the right approach for this type of goal, and provide several benefits out of the box:

  • We can ‘tree-shake’ styled components and only include those that are used, which optimizes the bundle size
  • It helps us build modular, reusable styles
  • It hashes the CSS classes on the output, avoiding conflicts between style rules. More importantly, styling rules are isolated, and we decide which styled components we export to the consumer — so it prevents targeting CSS classes on the consumer side.
  • It simplifies maintenance, since we know which style can be removed without breaking the layout on the consumer side or introducing a breaking change

These points are achievable with other solutions, like Sass and BEM.

We wanted to automate as much as possible to reduce time spent on pull request reviews, and to simplify the developer workflow. Therefore, we needed to bring more tools to our stack — for instance a CSS linter to make sure developers follow the BEM convention. But that would require more configuration and more maintenance in the long term.

We evaluated different CSS-in-JS libraries, and Linaria looked promising. Its main benefit was that it didn’t have a runtime, so we could let each team decide what solution they wanted to use for styling in their projects. Unfortunately, when we tried it, we had performance issues while developing components with dynamic styling.

We chose to use Styled Components, as most teams at Acast had started to use it. It has good community support, is part of the fastest engine in benchmarks and has great support for theming. We coupled it with Styled System, which helps us to stay consistent between components’ API for including style props.

Playground

While developing and maintaining components, it’s essential to test them quickly and easily in browsers. Storybook is an excellent tool for this, allowing us to test components in isolation, for different states, and to interact with them.

Storybook page of the Combobox component

Documentation

Documentation is vital to ease adoption, but it gets easily forgotten or out of sync, so it’s essential to keep the documentation close to the code.

We chose to use Docz because it doesn’t rely on the file structure to generate the website. We can collocate the documentation file next to the component, so the component’s folder will contain everything related to it.

For instance, we can find the component, the story (for Storybook mentioned above), tests and the documentation (.mdx) in the Button folder.

Button
├── Button.js
├── Button.stories.js
├── Button.test.js
└── button.mdx

As you might have noticed, it supports MDX, which is a must-have when working with React. It allows developers to mix Markdown, JavaScript, and JSX code in the same file — so we can import the components and render it directly in the documentation.

The consumer will see how the component looks and interact with it. And all of that with zero configuration.

The only downside with Docz is that we often end up copying code from Storybook to the documentation — but Storybook had now added better support for documentation, so we’re planning to migrate to it. The documentation would only focus on the guidelines for using the component, and the playground to interact with it.

Documentation of the variant prop from the Button component

Testing

At Acast, we like to work with React Testing Library. We believe tests should cover whatever users interact with. RTL also helps developers focus on accessibility while writing tests using DOM utils for targeting elements (`getByRole`, `getByAltText`, and so on).

Snapshot testing is convenient to keep the assertion short with only one line of code (`expect(wrapper).toMatchSnapshot()`). But we don’t use them for testing React components. In our experience, snapshot testing a component doesn’t help us with catching bugs or regression.

Having a test that breaks because of HTML structure change brings more noise to the developer than valuable input — and it can bring a false sense of confidence that our components are tested.

Bundling

Decibel is used by all teams on different projects. The bundle must be ‘tree shakable’, so unused components can be removed from the consumer bundle during their building process.

Since Storybook handles the build while developing components, we only need to take care of the build process in the CI when merging on the master branch. Then we can directly use Babel for bundling.

The configuration is rather simple for our use case, and we don’t need to introduce another bundler. Here’s an example of our babel.config.js for the production build:

module.exports = () => {
const presets = [
'@babel/preset-react',
['@babel/preset-env', { corejs: 3, useBuiltIns: 'usage', modules: false }],
];
const plugins = [
['@babel/plugin-transform-runtime', { useESModules: true }]
];
return {
presets,
plugins,
};
};

Then, in the package.json, we have a task executed by the CI:

"scripts": {
"build": "rimraf dist && yarn build:es",
"build:es": "babel src --out-dir dist/es --ignore '**/*.test.js','**/*.stories.js'','**/__snapshots__/**','**/__mocks__/**'"
}

Automation

We aim to automate as much as possible within our processes, to ease contribution and reduce human error.

We use Hygen for code generation for creating new components:

It’s one of many code generators available in the open-source community. We chose it because it’s easy to implement, and it simplifies the component creation process. So far, we only use it for creating the file structure and default boilerplate when we need to create a new component.

We publish a new version automatically when we merge on the master branch:

As for other projects at Acast, we use CI/CD in Decibel. We follow the GitHub Flow for our branching strategy because we want to release a new version as soon as a pull request is approved. To feel confident in this workflow, we run the test suite and linters (ESLint and Commitlint) on the pull request before the developer can merge it.

To simplify version management, we use Semantic Release.

Based on the commit message, and following conventional commit format, Semantic Release automatically determines the version number it needs to bump (major, minor, patch). It also takes care to generate a changelog, which is useful in keeping track of changes.

We use Renovate to update our dependencies automatically:

It’s important to keep dependencies up to date, to benefit from vulnerabilities fixes and other improvements. It can be time-consuming to manage that ourselves, but tools like Renovate do it for us. We limit the pull request creation from Renovate to once a week, to reduce the noise and time we spend on reviewing pull requests.

{
"extends": ["config:base", ":preserveSemverRanges", "group:allNonMajor"],
"lockFileMaintenance": true,
"schedule": ["every weekend"]
}

Next steps

We’re on the right track, but Decibel is still in its infancy.

We’ve built a vast range of components that allow developers to build projects using only our component library. And, in the future, we want to conduct user testing and start to iterate on our building blocks.

We also need to invest more time and resources in establishing a comprehensive system and guidelines to help teams take UX decisions while using Decibel.

--

--