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

Design tokens are the building blocks of all UI elements. In ZDS, tokens are CSS custom properties that can be used to style or customize components or element at different levels, but also as a way of avoiding hard-coded values with implementations done on top of ZDS.

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

Our 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.

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.

Web Components

Web components are custom HTML elements with encapsulated styles and behavior. They work across many different frameworks (such as Lit, React, Vue, and Svelte) as well as web environments (such as Eleventy, Wordpress, and Ruby on Rails).

Our Web components use 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 (React and Vue) and @zurich/angular-components (Angular).

Native mobile components

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.

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.

It's the approach used in the examples of this documentation.

For example, the imports of using the CSS paradigm:

html
<script type="module" src="../@zurich/css-components/javascript.js"></script>
<link rel="stylesheet" href="../@zurich/design-tokens/HeadingTags.css" />
<link rel="stylesheet" href="../@zurich/design-tokens/Icons.css" />
<link rel="stylesheet" href="../@zurich/css-components/index.css" />

Or using the Web Components paradigm:

html
<link rel="stylesheet" href="../@zurich/web-components/styles.css" />
<link rel="stylesheet" href="../@zurich/design-tokens/HeadingTags.css" />
<script type="module" src="../@zurich/web-components/index.js"></script>

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.

For example, for installing the CSS components in your project, use this command in the root of the project (same level as the package.json):

sh
npm i -D @zurich/css-components

Or for Web Components:

sh
npm i -D @zurich/web-components

Customization

Customization levels

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 or app

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. Check the different options for config in custom components.

Click me!Click me!Click me!

The only one of these three working for the CSS Components would be the first: using style.

INFO

It's not strictly necessary, but we recommend to place the instance tokens override at the same level as the component tag in the case of the CSS Components for consistency and semantics

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.

For both components paradigms, CSS Components and WebComponents, you can use inline styles for the instance customization:

Click me!

For WebComponents, there are also two extra options for customizations:

  • Using the custom attribute, that requires a key-value pair object with the <property> name as key and the corresponding value as string. Make sure you take into account the complex attributes restrictions.
Click me!
  • Using the custom-str attribute, that requires a string with the standard HTML object syntax.
Click me!

Parametrization

All the components, CSS components or Web Components, require to receive some sitting to be configured. In most cases, this is going to be done by adding certain HTML attributes to the HTML tags or their children.

html
<body>
  <z-button config="secondary"></z-button>
</body>

Complex attributes

If the attribute is an array or and object, requires to use a special syntax. Check the explanation about this ion the complex attributes documentation for WebComponents.

The CSS components rely too on the composition of the HTML, and that's why it's important to construct the components according to the documentation of each one.

Using JavaScript

Parametrization can also be done via JavaScript:

html
<body>
  <z-button id="button"></z-button>

  <script type="module">
    const button = document.getElementById('button');
    button.config = 'secondary';
  </script>
</body>

This is really useful for complex attributes:

html
<body>
  <z-select id="select" label="Select"></z-select>

  <script type="module">
    const select = document.getElementById('select');
    select.options = [
      { text: 'Option A', value: 'a'},
      { text: 'Option B', value: 'b'},
      { text: 'Option C', value: 'c'},
    ];
  </script>
</body>

Slotted configuration

When if comes to the Web Components and their framework wrappers; there are some tags that are received inside the default slot of the component that can how as a more HTML-friendly way of configuring the components. An example of that is the use of <option> tags in some components like the Select or in some other cases can even be another component:

html
<body>
  <z-select label="Select">
    <option value="a">Option A</option>
    <option value="b">Option B</option>
    <option value="c">Option C</option>
  </z-select>
</body>

Events

Event handling

For CSS Components:

html
<body>
  <label z-text-input >
    <span>Text</span>
    <input id="input" onchange=""/>
  </label>
</body>
html
<body>
  <label z-text-input >
    <span>Text</span>
    <input id="input" />
  </label>

  <script type="module">
    ...
  </script>
</body>

For WebComponents, they always use a CustomEvent:

html
<body>
  <z-text-input id="input" label="Text"/>

  <script type="module">
    ...
  </script>
</body>

Helpers

bindInputChange

Frameworks and event

...

  • Angular: ...
  • React: ...
  • Vue: ...

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. To place two or more items inside the same slot, you only need to name them with the same slot attribute. Named slots must be used as direct children of the WebComponent, and everything not marked with the slot attribute will fallback to the default one.

Card HeaderTag 1Tag 1Content

Slots in React

In React, the default slot and the named ones will work the same:

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

<ZrCheckbox>
  <span slot="label">Check me!</span>
</ZrCheckbox>

Attention!

Do NOT use the slot attribute in JSX Elements, since in many cases this won't be working. If you want to do it, make sure that the root element/s of the JSX are receiving the slot attribute.

But React has also another way of using generated content for the slots, and it is via JSX Elements in the corresponding properties. Specific properties are created in the Reacts wrappers and the slot injection is managed underneath.

Attention!

Do NOT use the attribute and the corresponding slot at the same time because the content will be duplicated.

jsx
function ArticleCardPreLine () {
  return (
    <ZrTag fill="lime">Tag</ZrTag>
  )
}

function ArticleCardHeader () {
  return (
    <>
      Card <em>header</em>
    </>
  )
}

function App () {
  return (
    <ZrArticleCard 
      header={<ArticleCardHeader/>} 
      pre-line={<ArticleCardPreLine/>}
    >
      Card content
    </ZrArticleCard>
  )
}