Skip to content

How it works โ€‹

The ZDS development libraries try to adapt to the multiple use cases that we have inside Zurich when it comes to applications development. That requires to have tooling that is flexible, progressive, and that can deal with all the minor issues by hiding the management of the complexity of use and configuration under simplified interfaces that are still giving the control to the developer and the app environment to make the integration as easy as possible.

Design tokens โ€‹

In order to simplify the design, improve consistency, allow customization, and make the whole ZDS more systematic, we use the concept of design token during the design phase (by using Figma variables) and for our development libraries (by using CSS custom properties, AKA CSS variables).

The main difference between this type of variables and the ones used in CSS preprocessors like Sass (SCSS) is that these variables work at runtime and keep the references, they are not simple placeholders substituted in transpilation time.

This is translated in the end into a base NPM package called @zurich/design-tokens that any other web-based components package will be using.

Multi-paradigm โ€‹

We need to give support to multiple platforms, frameworks, and workflows with this design system. That's why we have develop two different approaches to use the designed components:

  • CSS components: based in pure HTML and CSS with no extra libraries required. It's the one implemented in the @zurich/css-components package. Uses custom HTML attributes for the CSS selectors that are not part of the standard ones and also the standard ones of each HTML standard tag.

  • Web components: uses Lit as the only dependency and are JS-based components that have the CSS already included inside the JS files. Each component has also a wrapper to be used as a "native" component for the three main frontend frameworks: Angular, React and Vue. It's the one implemented in the @zurich/web-components package.

  • Native mobile components: we offer also libraries for frameworks that allow the development of native apps like React Native with our @zurich/react-native package.

In the documentation of each component you will find a select in to top-right corner of the page to change between the different explanations of the implementations (for paradigm and target framework). In the cases where the component it's only implemented via WebComponent, the CSS option won't appear.

Local installation vs Remote code โ€‹

Every package can be installed locally, or imported via URL as part of the meta of the HTML document (except for the SCSS tooling of @zurich/dev-utils, since it requires transpilation to CSS in order to be usable).

Each option has pros and cons:

  • Remote code: it's done through <script> and/or <link rel="stylesheet"> tags in the <head> or <body> of the document and are the easiest way of start using the ZDS. For further information about the import process of each package; check the corresponding installation guide. And for consuming assets: check the consumables description.

    This approach doesn't require development dependencies and allow to use the design system directly from the HTML documents. Allows to have a more fine control over the necessary assets to improve performance and doesn't require a bundler.

  • Local installation: it's done via NPM packages and requires further configuration depending on the framework used. One of the advantages of the local installation is that we could be using all the TypeScript and JSDocs features of the components. Check the corresponding installation guide per package.

    This approach offers the best developer Experience (DX) since allows the use of TypeScript and JSDocs to receive hints, errors, autocompletion, and documentation about all the aspects of the ZDS directly in the IDE.

Customization โ€‹

By using the overwrite of some defined CSS variables we can customize the design system if it's required for a certain project. We explicitly expose those variables as part of the documentation so you have no issues with the customization and can make everything work with very easy steps.

There are three customization categories or levels to consider:

  • General: by changing the design token in the :root CSS pseudo-element or under the body selector, we can change variables for the whole site to adapt the design system to our requirements. The last option (with body) even allow us to create different theme based on complementary selectors (ex.: body.my-theme or body[theme="my-theme"]).

    Some examples of this selectors:

    css
    :root {
      --z-button--bg: red;
    }
    css
    body {
      --z-button--bg: red;
    }
    css
    body[theme="scarlet"] {
      --z-button--bg: red;
    }

    In every example, the value of the variable --z-heading--color is changed to red for the entire page.

  • Contextual: by using selector for specific container components (ex.: #my-container) or direct injection in the style attribute the customization can be scoped under that container or under circumstances. This is basically how our z-theme works.

    html
    <section id="my-container">
      <z-button>Click me!</z-button>
    </section>
    css
    #my-container {
      --z-button--bg: red;
    }
  • Instance: as with the contextual customization, the modifications can de applied to a single tag or component instance to make adjustments or fine component customization.

    html
    <z-button style="--z-button--bg: red;">Click me!</z-button>

Custom theme โ€‹

By using the semantic tokens we can create custom theme as variations of the default ZDS look-and-feel in order to customize the UI for specific brands or purposes. These variables can be override on any of the previously mentioned levels.

The previously mentioned "contextual customization" approach allows to have multiple themes at the same time by using different selectors.

Custom components โ€‹

Each component has it's own custom variables exposed with the naming convention --z-<component_name>--<property> as you can check in here, for example. This eases the process of adapting the components without the overhead of having to deal directly with the CSS and in a controlled and tested way.

These variables can be override on any of the previously mentioned levels.

Slots โ€‹

WebComponents have the ability of using slots: placeholders inside a web component that you can fill with your own markup, which lets you create separate DOM trees and present them together.

The tooltip component, for example, uses the inner HTML of the tag to be the "tooltiped" element. This is consider as the default slot.

Hover me

These slots can also be named in order to target an specific one.

HeadBody

Slots in Angular โ€‹

Angular uses something called content projection when it comes to the concept of slots, but the default slot works the same:

html
<za-tooltip text="Information text">
  Hover me
</za-tooltip>

For the use of multiple content projection an attribute with the name of the slot needs to be provided. We will use the *-slot suffix to avoid possible collides.

html
<za-table>
  <thead thead-slot>
    <tr><th>Head</th></tr>
  </thead>

  <tbody tbody-slot>
    <tr><td>Body</td></tr>
  </tbody>
</za-table>

Slots in React โ€‹

In React, the default slot works the same:

html
<ZrTooltip text="Information text">
  Hover me
</ZrTooltip>

But React does not have a proper approach for slots. Instead of that, it can receive other JSX elements as properties. Specific properties are created in the Reacts wrappers when the slot is not an already existing property.

tsx
function THead () {
  return (
    <thead>
      <tr><th>Head</th></tr>
    </thead>
  )
}

function TBody () {
  return (
    <tbody>
      <tr><td>Body</td></tr>
    </tbody>
  )
}

function App () {
  return <ZrTable THead={<THead/>} TBody={<TBody/>}/>
}

Slots in Vue โ€‹

Vue has its own approach for slots too, where the behavior of the default one is like the WebComponent, but the named slots require to use a <template> tag wrapping the content, with the v-slot:<name>attribute of the #<name> shortcut.

html
<zv-table>
  <template #thead>
    <thead>
      <tr><th>Head</th></tr>
    </thead>
  </template>

  <template #tbody>
    <tbody>
      <tr><td>Body</td></tr>
    </tbody>
  </template>
</zv-table>