@amboss/design-system
TypeScript icon, indicating that this package has built-in type declarations

3.3.3 • Public • Published

amboss-design-system

Latest version Dependencies React Version

📄 Browse the design system (Storyboard documentation)

Quick start

Development Ensure that you are using npm >= 7.0.0 before running npm install

To start the project run npm run start

To generate a new component run npm run component myComponentName

Building the package

To build the package run npm run build

Building storybook

To build storybook run npm run build-storybook

Publishing

  • Add minor, major or patch label to your PR to specify release type.
  • Add skip-release label to skip the release
  • If you want your commit to skip ci, add "[skip ci]" to your commit message.

Generating mobile tokens

  1. Install Node.js and npm by following the official installation guide
  2. Install dependencies: npm install
  3. Generate the tokens:
    npm run tokens:ios
    npm run tokens:android
    

Generating mobile icons

  1. Install Node.js and npm by following the official installation guide
  2. Install dependencies: npm install
  3. Generate the tokens:
    npm run icons:ios
    npm run icons:android
    

Codebase Overview

1. Tech stack
2. Linting
3. Folder structure
4. Build
5. Tokens, styles and theming
6. Assets
7. Media queries

Tech stack

  • Code: typescript
  • Styles: emotion.js
  • Framework: react
  • Bundler: webpack
  • Environment: storybook
  • Tokens: style-dictionary
  • Testing: jest + react testing library

Linting

As for linting and formatting, you can configure your editor to automatically lint and format your code on save. For this purpose, we're using Prettier and ESLint. If you need to do it manually, you can run:

npm run lint

This command is also run as a pre-push hook and in the pipeline.

Folder structure

|-- .storybook
    |-- components
        |-- ColorGrid.tsx
        |-- SizeGrid.tsx
    |-- main.js
    |-- preview.js
|-- assets
    |-- fonts
        |-- Lato.woff
|-- build (autogenerated)
|-- build-icons (autogenerated)
    |-- android
        |-- ic_activity_16.xml
        |-- ic_activity_24.xml
    |-- ios
        |-- activity.imageset
            |-- activity.pdf
            |-- activity16.pdf
            |-- Contents.json
|-- build-tokens (autogenerated)
    |-- android
        |-- design_system_color.xml
    |-- ios
        |-- DesignSystemColorTokens.swift
    |-- scss
        |-- _theming.scss
        |-- _variables.scss
    |-- assets
        |-- icons.json
        |-- icons16.json
    |-- _colors.json
    |-- _sizes.json
    |-- visualConfig.ts
|-- docs
    |-- Design-principles.mdx
|-- static
    |-- image-for-storybook.png
|-- tokens
    |-- global
      |-- themes
          |-- dark.json
          |-- light.json
      |-- assets.json
      |-- colors.json
    |-- web
      |-- component
        |-- dark.json
        |-- light.json
    |-- android
      |-- opacity.json
    |-- ios
      |-- opacity.json
|-- src
    |-- index.ts
    |-- components
        |-- Button
            |-- Button.tsx
            |-- Button.stories.tsx
            |-- Button.test.tsx
    |-- shared
        -- mediaQueries.ts
    |-- types
        |-- index.ts

Build

There are 3 stages of a build.

  1. Generate variables, theme values and assets from tokens
  2. Build all exported components with babel (preset-react, preset-typescript)
  3. Bundle all built components with webpack

Generate variables, theme values and assets from tokens

In order to structure our tokens we use style-dictionary. This tool is used for creation a content of ./build-tokens folder where we keep all the scss variables and mixins.

New variables and mixins gets generated automaticaly:

  • in watch mode while npm run start is running
  • after npm install
  • before build

You can generate tokens manually using npm run tokens:web and npm run tokens:watch

We use tokens to control the most atomic values in styles and themes.

How does it work?

We have a set of .json files that are located in ./tokens folder, and we have a style-dictionary as a tool for converting .json in to various formats.

For example:

We have a file - ./tokens/global/light|dark.json that gets converted into part of ./build-tokens/visualConfig.ts.

visualConfig.ts is used later as a source for emotion.js theme.

The configuration for style-dictionary is inside ./style-dictionary/{platformName}/{platformName}.sd.config.json. In this config we use some custom transforms, filters and formatters, all of them defined in corresponding files of platform folders.

custom/format/emotion-visual-config - a formatter that generates visualConfig.ts that contains all variables and themed values.

custom/filter/emotion-build-tokens - a filter for all token categories that should go to visutal config.

custom/format/json - a formatter for generation .json files per category. This formatter also add isUsed flag to according token.

custom/format/sub-theme-types - a formatter that generates type definitions for sub-themes.

custom/assets/svg - a transformer that adds a path name to dictionary of svg icons.

More about tokens, themes and styles.

Bundle all exported components

Before publishing the design system for NPM, we run npm run build. This runs the script at ``bundles everything that is exported from.tsand`.tsx` files within the source.

For this, we use tsup, which runs esbuild under the hood. We also use a lenient browserslist config to ensure compatibility with older browsers.

For styles, we use emotion.js.

What in the bundle?

  • JS code that is compiled from .tsx files (includes common components and emotion.js styled components)
  • SVG icons that are inlined

fonts that are base64-ed currently taken off the build

Tokens, styles and theming

Currently, we have 4 types of tokens:

  • Atomic values, like colors, sizes, shadows.
  • Font assets, that are basically base64 of font files. (not used in production)
  • Theme values, that are referencing atomic different atomic values
  • Svg icon assets, that are inlined to a .json file

As we use Emotion.js all tokenized values are accessible via theme provider.

Basically during the build phase we generate big objects of variables and themed values for each theme (currently light and dark).

We call this object ambossVisualConfiguration.

Particular themes from ambossVisualConfiguration has to be imported and passed to the theme provider at the consumer side.


Atomic values are straight forward and simply converted to variables.

After build all atomic values are passed to theme.variables by emotion.

Example:

const StyledContainer - styled.div(
  ({ theme }) => ({
    marginTop: theme.variables.size.spacing.s
  })
);

Theme tokens are little more complex. We generate separate VisualConfig for each theme, but together with that we generate type definition that describe any of the themes. This enables developers not care about current theme and how many themes are there.

After build all themes values are passed to theme.values by emotion.

Example:

const StyledContainer - styled.div(
  ({ theme }) => ({
    backgroundColor: theme.values.color.background.primary.default
  })
);

Sub-themes are following the same concept as themes. Developer can fully rely on theme.values object. As long as component in runtime is wrapped in SubThemeProvider and its tokens listed in "tokens/themes/sub-themes/...", all values will be set correctly and automatically.

Note that not all tokens are enabled in sub-themes. Make sure to add missing ones to "tokens/themes/sub-themes/...".

Components that have a "Sub-themed" badge are already in.

Assets

  • assets are defined in tokens/assets.json
  • with the help from style-dictionary we create a json file with tokens (/build-tokens/assets/)
  • those json files supposed to be imported in the ts module (see Icons.tsx)
  • in order to secure d.ts filed in the build we "manually" copy asset jsons on the postbuild phase (see package.json)

Media queries

There is an API for media-query related props: an array of 3 elements [small, medium, large], for respective screen sizes.

For example <Box space=["xs", "xl", "xxl"] /> will set xs for small screens, xl for medium screens and xxl for large.

Basically, if you see a type of MQ<Whatever> you can provide an array of 3 Whatevers.

In order to add a media query support to your components:

import React from "react";

import styled from "@emotion/styled";
import { mq } from "../../shared/mediaQueries";

type TextAlignment = "left" | "center" | "right";

type BoxProps = {
  alignText: TextAlignment | MQ<TextAlignment>;
};

export const Box = styled.div<BoxProps>(({ alignText = "left" }) =>
  mq({
    textAlign: [alignText, { left: "left", right: "right", center: "center" }],
  })
);

Shadow-DOM

In order to use emotion-enabled DS components in the shadow-dom, you need to take some steps to 'link' the styles in the light-dom (these are added by emotion using CSSStyleSheet.insertRule() which is why <style data-emotion> appears empty in the DOM).

emotion-js/emotion#1366 https://emotion.sh/docs/cache-provider https://stackblitz.com/edit/emotion-shadow-dom-example?file=ShadowDomContainer.js https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule

import {
  ThemeProvider,
  light,
  Card,
  H1,
  CacheProvider,
  createCache,
} from "@amboss/design-system";
class SuchShadow extends HTMLElement {
  connectedCallback() {
    const root = document.createElement("div");
    this.attachShadow({ mode: "open" });
    this.emotionCache = createCache({ container: this.shadowRoot });
    render(
      <ThemeProvider theme={light}>
        <CacheProvider value={this.emotionCache}>
          <Card>
            <H1>I love you shadow unicorn!!!</H1>
          </Card>
        </CacheProvider>
      </ThemeProvider>,
      root
    );
  }
}

customElements.define("shadow-element", SuchShadow);

Package Sidebar

Install

npm i @amboss/design-system

Weekly Downloads

2,273

Version

3.3.3

License

ISC

Unpacked Size

6.27 MB

Total Files

612

Last publish

Collaborators

  • bagrat
  • tbo-amboss
  • deiga