Best Practices for Building Your Design System in Svelte

Best Practices for Building Your Design System in Svelte

Best practices for building your design system in Svelte to prototype faster and maximize team efficiency


The engineering team at Nylas built our updated Nylas Dashboard using the latest JavaScript framework, Svelte. No one on the team had used Svelte before, and we accrued some technical debt while learning the framework and the preferences of our teammates. We needed to address this technical debt in Q4 2020 when the team took on two big projects with tight deadlines. 

With our year fast approaching the start of Q1 2021, we decided to start building a design system to help us build faster and keep our design more consistent. We hoped to achieve the following:

  • Reduce lines of code to implement new pages and features
  • Speed up dev work with code components that designers can use
  • Expedite design work with pre-built Figma components
  • Better UI design standards in the Dashboard 

In this post, we’ll share the Svelte best practices we’ve built into our design system that helps us prototype faster and maximize team efficiency. Read on for great tips on building your own Svelte component library.

Svelte Component Best Practices

Best Practice Themes

Simplicity and Flexibility

  • Adding complexity to elements makes them inherently difficult to understand and use. So instead, we prioritized techniques that helped the elements remain simple. 
  • To maintain simplicity in elements, we prioritized flexibility. Thus, we used techniques that allow the parent to configure the element, avoiding logic inside the element.

Naming Conventions

For example, you can see these conventions in Colours where we chose to use comparative endings to avoid adding too many colors. 

$color-green-darker: #167b6a;
$color-green-dark: #00a88c;
$color-green: #00e5bf;
$color-green-light: #25f4d1;
$color-green-lighter: #e5fffb;
$color-green-lightest: #f5fffd;

In other cases, we prioritized developer experience, showing the pixel number in the name so that developers can easily see the values in Figma.

$spacing-4: 0.25rem; // 4px
$spacing-8: 0.5rem; // 8px
$spacing-16: 1rem; // 16px

TypeScript and mixins

By defining props in TypeScript, you can simplify element architecture and your developers get code completion when using the element. When elements define many prop options, styles become very large. Mixins can help to break up our stylesheets, making them more consumable to read and edit. 

Define props (added to the element via classes):

export let size: "medium" | "small" = "medium";
export let colour: "green" | "blue" | "red" | "black" = "green";
export let style: "fill" | "outline" | "transparent" = "fill";
export let fill: "natural" | "full" | "mobile-full" | "desktop-full" = "natural";

Include mixins:

button {
  @include button-size;
  @include button-colour;
  @include button-style;
  @include button-fill;

In a separate scss file, define mixins:

@mixin button-size() {
  &.small {...}
  &.medium {...}
}

$$restProps 

Svelte’s $$restProps is a list of all the undefined properties associated with the element. Using $$restProps means we don’t have to define every single prop that can be used, thereby improving simplicity and flexibility.

<button {id} {name} {type} {...$$restProps} class={classes} on:click>
  ...
</button>

slots

Svelte’s slots are extremely versatile, allowing for fallbacks, props and even slot management using $$slots. Slots help make elements flexible and robust and are quickly becoming DS’ most used design pattern.

Let component define layout and logic, slot can define details:

{#each components as component, index}
  <AccordionList item={component} {index}>
    <div slot="header">{component.title}</div>
    <div slot="content">{component.details}</div>
  </AccordionList>
{/each}

Define a default empty state in a named slot:

<tr class="loaded">
  <td colspan={headers.length} class="no-data">
    <slot name="empty-state">
      <GenericEmptyState />
    </slot>
  </td>
</tr>

That can be overwritten:

<DataTable ... >
  <div slot="empty-state">
    <UniqueEmptyState />
  </div>
</DataTable> 

Pass data to your slot:

<slot currentOption={option} currentIndex={index}>
  <option value={option}>{option}</option>
</slot>

So you can customize presentation:

<Element let:currentOption>
  <option value={currentOption}>{currentOption.display_name}</option>
</Element>

Results

Since implementing a design system, we’ve noticed remarkable improvements in developer experience and speed:

  • We’ve greatly increased speed to prototype new features, as seen in our hackathon projects becoming more ambitious over time.
  • Working on new projects has improved and become more efficient since designers and developers both work with the same blocks. 

You May Also Like

Working with the Nylas CLI
Nylas Node.js SDK v6
How to Send Emails with Nylas Node SDK
How to Manage Calendar Events with the Nylas Ruby SDK