Web ComponentsNew

Nord makes it effortless to implement and use its components across any framework or no framework at all. We accomplish this by using standardized web platform APIs and Web Components.

Web Components are a set of technologies that provide a standards based component model for the Web. They allow us to create reusable Custom Elements with their functionality encapsulated away from the rest of the code.

We’ve chosen to use Web Components because there is a strong requirement for Nord to be used in many different contexts and with varying technologies — from static HTML pages to server-rendered apps, through to single page applications authored with frameworks such as React and Vue. Web Components work great for Nord, because they:


Browse components

Quick start

There are two necessary parts to using Nord’s components — the Web Components themselves, and the CSS Framework. Everything else is optional. The fastest way to get started is to include the following directly into your page with <script> and <link> tags:

<link rel="stylesheet" href="https://nordcdn.net/ds/css/3.3.1/nord.min.css" integrity="sha384-x2XdCI8Yog7KGRmrrGLegjFrrIYXEhGNxql/xEXdMoW5NkpEhlAkUHdQJxkL1vPg" crossorigin="anonymous" />
<script type="module" src="https://nordcdn.net/ds/components/3.18.2/index.js" integrity="sha384-Hxzh12RVoHTpU2FOGZp+/nJxzuDRDoLKCpOMahxaYjmeImNHJJFhconKw3OjRkDd" crossorigin="anonymous"></script>

With these, you now have access to all of Nord’s components! You can now for example add a button to your page:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Nord Design System</title>
  <link rel="stylesheet" href="https://nordcdn.net/ds/css/3.3.1/nord.min.css" integrity="sha384-x2XdCI8Yog7KGRmrrGLegjFrrIYXEhGNxql/xEXdMoW5NkpEhlAkUHdQJxkL1vPg" crossorigin="anonymous" />
  <script type="module" src="https://nordcdn.net/ds/components/3.18.2/index.js" integrity="sha384-Hxzh12RVoHTpU2FOGZp+/nJxzuDRDoLKCpOMahxaYjmeImNHJJFhconKw3OjRkDd" crossorigin="anonymous"></script>
</head>
<body>
  <nord-button variant="primary">Hello Nordie</nord-button>
</body>
</html>
Edit in CodePen

Whilst this is a fast way to get started, the above method is not recommended for production since it will result in many small HTTP requests. This has a negative effect on page load times. We recommend using a bundler for production, for optimal performance.


Installation

We strive to support as many use-cases as possible for consuming Nord’s components. In its purest form, you can include our components and styles via <script> and <link> tags served from our CDN.

This approach is best for rapid prototyping, for use in static HTML pages, or for integrating into existing apps which do not have a JavaScript build step. For all other cases we recommend installing the packages locally via npm/yarn.

CDN installation

Include the following in the <head> of your web page:

<link rel="stylesheet" href="https://nordcdn.net/ds/css/3.3.1/nord.min.css" integrity="sha384-x2XdCI8Yog7KGRmrrGLegjFrrIYXEhGNxql/xEXdMoW5NkpEhlAkUHdQJxkL1vPg" crossorigin="anonymous" />
<script type="module" src="https://nordcdn.net/ds/components/3.18.2/index.js" integrity="sha384-Hxzh12RVoHTpU2FOGZp+/nJxzuDRDoLKCpOMahxaYjmeImNHJJFhconKw3OjRkDd" crossorigin="anonymous"></script>

If working on a custom themed app, you should also include the theme after the CSS Framework:

<link rel="stylesheet" href="https://nordcdn.net/ds/css/3.3.1/nord.min.css" integrity="sha384-x2XdCI8Yog7KGRmrrGLegjFrrIYXEhGNxql/xEXdMoW5NkpEhlAkUHdQJxkL1vPg" crossorigin="anonymous" />
<link rel="stylesheet" href="https://nordcdn.net/ds/themes/8.1.1/nord-dark.css" integrity="sha384-4mfQkitA1YUssjHukrfVhopnhPw9eM2tX8Z05rZ/5NJRmDJN1fQp2gGfwydx2SzL" crossorigin="anonymous" />
<script type="module" src="https://nordcdn.net/ds/components/3.18.2/index.js" integrity="sha384-Hxzh12RVoHTpU2FOGZp+/nJxzuDRDoLKCpOMahxaYjmeImNHJJFhconKw3OjRkDd" crossorigin="anonymous"></script>

Local installation

Before moving further with the installation, please make sure you have Node.js installed on your machine. You can install the latest version through their website.

If you’re planning on using Nord’s Web Components in a project that doesn’t yet use Node Package Manager, you’ll have to first create a package.json file. To do this, run npm init and follow the steps provided. Once finished, you can install Nord’s Web Components by following the instructions below.

Run in your project or website root directory (where package.json lives):

# With NPM
npm install @nordhealth/css @nordhealth/components --save

# With Yarn
yarn add @nordhealth/css @nordhealth/components

Required styles

It’s a requirement that you use our CSS framework alongside the Web Components. The CSS framework includes all our design tokens, a default theme for the components, as well as a number of helpful utility classes.

Outside of the components themselves, we recommend leveraging the included design tokens and utility classes as much as possible to achieve a consistent look and feel.

If working in Veterinary or Healthcare space, you will also need a specific theme from our themes package in your application:

# With NPM
npm install @nordhealth/themes --save

# With Yarn
yarn add @nordhealth/themes

Bundling

Bundling and transpiling are considered application concerns. We cannot anticipate all requirements, loading strategies, and so on. So we publish our components as ES modules, written for modern browsers. These can then be bundled, tree shaken, and optimized by individual applications.

To import all components:

import '@nordhealth/components'

To import specific components, which allows for optimal tree shaking:

import '@nordhealth/components/lib/Badge'
import '@nordhealth/components/lib/Button'
import '@nordhealth/components/lib/Input'

These imports automatically register the Web Components (side effects).

Browser support

Nord Design System is tested in the latest two versions of the following browsers. Our team addresses critical bug fixes in earlier versions based on their severity and impact. If you need to support IE11 or pre-Chromium Edge, this library isn’t for you.


Chrome Safari Chrome Chrome Chrome

Usage

Properties

In browsers, elements have attributes in HTML and properties in JavaScript. Our attributes are always in dash-case, and the properties are in camelCase.

All attributes have corresponding underlying properties, but not all properties have corresponding attributes. Since HTML only has two types of values, strings and booleans, any property which is not a string or a boolean cannot exist as an attribute. For instance, a function cannot be passed via an attribute, so it must be passed via JavaScript:

<nord-calendar></nord-calendar>

<script type="module">
  const calendar = document.querySelector("nord-calendar")

  // this could not be set via an attribute
  calendar.isDateDisabled = function isWeekend(date) {
    return date.getDay() === 0 || date.getDay() === 6
  }
</script>

Boolean attributes are considered false by omission, and true by inclusion. For instance, to set the hideLabel property of an input to true, you include the hide-label attribute without a value:

<!-- hideLabel property is set to true -->
<nord-input label="Name" hide-label></nord-input>

It’s recommended, as much as possible, to leverage attributes in HTML rather than properties in JavaScript.


Events

Components emit custom DOM events, which can be listened to via addEventListener or whatever mechanism your framework of choice offers. It’s worth noting that all our events bubble.

Where our components are drop-in replacements for native elements we follow native event names. For instance, our Input and other form components, emit change, input, blur, focus events.

<nord-input id="events-example" label="Name"></nord-input>

<script type="module">
  document.querySelector("#events-example").addEventListener("focus", e => {
    console.log("focus", e.target)
  })
</script>
Edit in CodePen

Where there is no equivalent native event, we prefix our event names with nord-. For instance, the Command Menu emits a nord-select event when a command is chosen from the menu.

It’s best to consult the documentation for each component to be sure of event names.


Methods

Some components offer methods which you can call to invoke behavior. Interactive components like Input, Button etc. offer focus(), blur(), and click() methods, which behave like the native equivalents. Some components offer methods for which there is no native equivalent. For instance, Date Picker offers a show() and hide() method:

Open date picker
<nord-stack>
  <nord-button id="methods-example-button">Open date picker</nord-button>
  <nord-date-picker id="methods-example-picker" label="Date of birth"></nord-date-picker>
</nord-stack>

<script type="module">
  const button = document.querySelector("#methods-example-button")
  const picker = document.querySelector("#methods-example-picker")

  button.addEventListener("click", () => {
    picker.show()
  })
</script>
Edit in CodePen

If you are using a framework like React or Vue, you will need to get a reference to the DOM element to call the methods on. Please refer to the Vue or React docs for further information.

It’s best to consult the documentation for each component to get a full description of methods and their parameters.


Slots

Web Components offer a feature called slots. As with events, properties and methods, slots form part of the public API of a component. Slots allow web components to receive content as children, and place the content into their internal DOM.

The most common slot is the default slot, which captures any content placed as a child of a component that does not have a slot attribute. For instance, the Button component has a default slot:

Log in
<nord-button>Log in</nord-button>
Edit in CodePen

In the above example the text “Log in” is placed into the Button’s default slot. You can also place elements inside the default slot, as can be seen here with the Card component:

Hello world

<nord-card>
  <p>Hello world</p>
</nord-card>
Edit in CodePen

Some components also offer named slots. Slot names are decided by the components themselves. Named slots are used to differentiate certain types of content, giving components control over style and position. Here we use the Button component’s start slot to position an icon. Notice the slot="start" attribute:

Delete
<nord-button variant="danger">
  <nord-icon slot="start" name="interface-delete"></nord-icon>
  Delete
</nord-button>
Edit in CodePen

Slotted elements can be placed anywhere inside a component. The component will always place it in the correct place internally. Therefore, the below example will produce the exact same result as above, despite the order being changed:

Delete
<nord-button variant="danger">
  Delete
  <nord-icon slot="start" name="interface-delete"></nord-icon>
</nord-button>
Edit in CodePen

Refer to a component’s documentation for a complete list of available slots.


CSS Properties

Our Web Components offer CSS Custom Properties to allow for fine grained control over their presentation. These aren’t offered as an alternative to properties, you should use properties whenever possible, but more as an “escape hatch” for when a component needs to meet an edge case.

Component custom properties are prefixed with --n- followed by the name of the component and the property in question. For example consider this Banner component that we want to add a box-shadow to:

Default banner presentationBanner with box-shadow added
<nord-stack>
  <nord-banner variant="warning">
    Default banner presentation
  </nord-banner>

  <nord-banner variant="warning" style="--n-banner-box-shadow: var(--n-box-shadow)">
    Banner with box-shadow added
  </nord-banner>
</nord-stack>
Edit in CodePen

Custom properties are inheritable as well which means they can be applied to any parent, or even the :root element, and their respective component will inherit the value(s).

Example bannerExample bannerExample banner
<nord-stack class="custom-property-banners">
  <nord-banner variant="info">
    Example banner
  </nord-banner>
  <nord-banner variant="warning">
    Example banner
  </nord-banner>
  <nord-banner variant="danger">
    Example banner
  </nord-banner>
</nord-stack>

<style>
  .custom-property-banners {
    --n-banner-box-shadow: var(--n-box-shadow);
  }
</style>
Edit in CodePen

You can see all the available custom properties for the Banner component on its respective docs page. Custom properties differ from component to component, refer to the component’s documentation for a complete list of available custom properties.

Always use a component’s properties over a CSS custom property wherever possible. CSS Custom properties are built with a counterpart “private” property, prefixed with --_n-. Please refrain from using private properties as they are always subject to change and intended for internal component use only.


Themes

We offer light and dark themes for components in addition to high contrast variants. Themes operate at a global level, rather than per component. The CSS Framework includes the light theme by default. For theme specific documentation, please see themes documentation. Make sure to try theme builder as well to get familiar with theming and how to customize it.

# Install themes using NPM
npm install @nordhealth/themes --save

# Install themes using Yarn
yarn add @nordhealth/themes
Nord ThemesThemes documentation

Waiting for components to load

Web Components are entirely self-contained (both styles and behavior), and can be loaded/defined asynchronously. Therefore a component may not be ready for use immediately.

When a component is loading, it should be hidden from view to avoid a Flash Of Un-styled Component (see FOUC). Our CSS framework will do this for you, as long as it is loaded before the components.

If you set a property/attribute on a custom element before it’s loaded, it will be applied correctly and will use the values once it’s loaded. However, you cannot call a method on a component before the component is loaded.

Most of the time this is not an issue, as you will be calling methods in event handlers etc, when the component has already loaded. In cases where you want to call a method as soon as possible, for example on page load, you need to wait for the component to be defined, using customElements.whenDefined:

<nord-date-picker id="loading-example" label="Enter date"></nord-date-picker>

<script type="module">
  const picker = document.querySelector("#loading-example")

  // It's fine to set properties while components are still loading
  picker.isDateDisabled = function isWeekend(date) {
    return date.getDay() === 0 || date.getDay() === 6
  }

  // but if you want to immediately call a method,
  // you should wait for the component to be defined
  await customElements.whenDefined("nord-date-picker")
  picker.focus({ preventScroll: true })
</script>
Edit in CodePen

Code completion for VS Code

We generate TypeScript type definitions for all our Web Components, including the React wrappers. This will allow VS Code, and other editors, to offer code completion and type information for properties and methods, whether using TypeScript or not.


Form controls

All our input components raise change events, and where it makes sense input events. These are useful for client-side validation, or for gathering values in javascript-based apps like with Vue or React.

Submit
<form id="form-events-example">
  <nord-stack>
    <nord-input label="First name" name="firstName"></nord-input>
    <nord-input label="Last name" name="lastName"></nord-input>
    <nord-button variant="primary" type="submit">Submit</nord-button>
    <output></output>
  </nord-stack>
</form>

<script type="module">
  const data = {}
  const form = document.querySelector("#form-events-example")
  const output = form.querySelector("output")

  // Input events bubbling up from input components
  form.addEventListener("input", e => {
    data[e.target.name] = e.target.value
  })

  form.addEventListener("submit", e=> {
    e.preventDefault()
    output.innerHTML = `<code>${JSON.stringify(data)}</code>`
  })
</script>
Edit in CodePen

For server-rendered apps, our components hook into the browser’s formdata event, which means that our components’ values will be submitted as part of POST/GET traditional form submissions, like native HTML elements:

Submit
<form id="form-submission-example">
  <nord-stack>
    <nord-input label="First name" name="firstName"></nord-input>
    <nord-input label="Last name" name="lastName"></nord-input>
    <nord-button variant="primary" type="submit">Submit</nord-button>
    <output></output>
  </nord-stack>
</form>

<script type="module">
  const form = document.querySelector("#form-submission-example")
  const output = form.querySelector("output")

  const url = new URL(location.href)

  if (url.searchParams) {
    output.innerHTML = `<code>${url.searchParams}</code>`
  }
</script>
Edit in CodePen

The browser’s built-in FormData object can also be used to get all values from a form via JavaScript:

Submit
<form id="form-data-example">
  <nord-stack>
    <nord-input label="First name" name="firstName"></nord-input>
    <nord-input label="Last name" name="lastName"></nord-input>
    <nord-button variant="primary" type="submit">Submit</nord-button>
    <output></output>
  </nord-stack>

</form>

<script type="module">
  const form = document.querySelector("#form-data-example")
  const output = form.querySelector("output")

  form.addEventListener("submit", e => {
    e.preventDefault()
    const data = new FormData(form)

    // This is an example, refer to MDN for more info about FormData.
    output.innerHTML = `<code>${JSON.stringify(Object.fromEntries(data))}</code>`
  })
</script>
Edit in CodePen

Localization

Nord provides a lightweight solution for localizing its components. Not all components need localizing, as for the most part snippets of text are set per instance. For example, the label on an Input will likely be changed every time you use it. Those cases are an app-level concern.

However, some components have text which has no reason to change per instance. For example, the usage instructions in the footer of the Command menu, or the accessible labels for next/previous month buttons of the Calendar.

For the latter cases, we provide a localization mechanism. It is not possible or feasible for Nord to anticipate every locale in which the components might be consumed, so we rely on individual teams and applications to translate and configure localization. To ensure Nord components work out of the box, we bundle translations for en-US.

View localization documentation

React

Whilst React supports Web Components, they are very awkward to use as-is. For this reason, we provide React-specific wrapper components in the package @nordhealth/react. This will allow you to use the Nord components as you would any other React component.

A planned future release of React will greatly improve support for Web Components, which will make the wrapper components unnecessary. But until then, we recommend their use.

View example React project

Installation

Install with npm:

npm install @nordhealth/react @nordhealth/css --save

or yarn:

yarn add @nordhealth/react @nordhealth/css

Usage

You must import @nordhealth/css once in your main entry point, then import components from @nordhealth/react wherever they are needed:

import { useState } from "react"
import { createRoot } from "react-dom/client";
import "@nordhealth/css"
import { Button } from "@nordhealth/react"

function App() {
  const [count, setCount] = useState(0)

  return (
    <Button
      variant="primary"
      onClick={() => setCount(count + 1)}
    >
      Clicks: {count}
    </Button>
  )
}

const root = createRoot(document.querySelector("#app"));
root.render(<App />);

The react wrapper components forward their refs, so that you can get access to the underlying web component instance with useRef. The @nordhealth/react package also exports types for all the web components for convenience.

The following example shows how to use refs in a typescript application:

import { useRef } from "react"
import { createRoot } from "react-dom/client";
import "@nordhealth/css"
import { Button, Input } from "@nordhealth/react"
import type { InputWC } from "@nordhealth/react"

function App() {
  const inputRef = useRef<InputWC>(null)

  function handleClick() {
    inputRef.current?.focus()
  }

  return (
    <Button variant="primary" onClick={handleClick}>
      Focus input
    </Button>
    <Input label="Name" ref={ref}>
  )
}

const root = createRoot(document.querySelector("#app"));
root.render(<App />);

Usage with Next.js

Currently there is some friction when using our components in a Next.js app. It is possible to overcome these issues yourself, but to make the process easier we offer specialized integration for Next.js.

Instead of importing components as above, you should import components from our Next.js specific integration:

// instead of:
import { Button } from "@nordhealth/react"

// do this:
import { Button } from "@nordhealth/react/lib/next.js"

Vue

Vue has excellent support for Web Components out of the box, it only requires a little configuration.

View example Vue project

Installation

Install with npm:

npm install @nordhealth/components @nordhealth/css --save

or yarn:

yarn add @nordhealth/components @nordhealth/css

Configuration

Vue needs to be configured to recognize Web Components. If you’re using Vite, it can be done like this:

// vite.config.js
import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // treat all tags with a dash as custom elements
          isCustomElement: (tag) => tag.includes('-')
        }
      }
    })
  ]
}

For more information, and how to configure for other setups (e.g. Vue CLI), please see the Vue docs.

Usage

Import the @nordhealth/components and @nordhealth/css packages in your main entry point:

// main.js
import '@nordhealth/css'
import '@nordhealth/components'

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

Then you can use the Nord’s components throughout your app. For instance, here is an example using a single-file component:

<script setup>
import { ref } from 'vue'

const count = ref(0)
</script>

<template>
  <nord-button variant="primary" @click="count++">
    Count is: {{ count }}
  </nord-button>
</template>

For optimal tree shaking, don’t import the @nordhealth/components package in the main entry point. Instead, import the specific Nord components you're using in each component. For example:

<script setup>
import { ref } from 'vue'
import '@nordhealth/components/lib/Button'

const count = ref(0)
</script>

<template>
  <nord-button variant="primary" @click="count++">
    Count is: {{ count }}
  </nord-button>
</template>

Our components also support the v-model directive for two-way data binding:

<script setup>
import { ref } from 'vue'

const name = ref("")
</script>

<template>
  <nord-input v-model="name" label="Name"></nord-input>
  <output>{{ name }}</output>
</template>

However, for our Checkbox, Toggle, and Tag components you need to add type="checkbox" to get v-model working. Otherwise, Vue has no way of knowing that it should bind to the checked property instead of the value property. For example:

<script setup>
import { ref } from 'vue'

const selected = ref(false)
</script>

<template>
  <nord-checkbox v-model="selected" type="checkbox" label="Label"></nord-checkbox>
  <output>{{ selected }}</output>
</template>

Note: when this Vue PR gets merged, this can be configured globally.

Alternatively, you can always use the more verbose equivalent to v-model:

<script setup>
import { ref } from 'vue'

const selected = ref(false)
</script>

<template>
  <nord-checkbox :checked="selected" label="Label" @change="selected = $event.target.checked"></nord-checkbox>
  <output>{{ selected }}</output>
</template>

Sometimes, you may need to pass complex data such as objects, arrays, and functions. These must be passed to a Web Component as properties rather than attributes. For the most part, Vue does the correct thing, and will handle this without any particular treatment.

However, there may be rare cases where you need to be more explicit that you’re supplying a property. For this, Vue has the syntax .propName="value", where propName is the name of the property.

For instance, here is an example of passing a function to a component:

<script setup>
const isWeekend = (date) => date.getDay() === 0 || date.getDay() === 6
</script>

<template>
  <nord-date-picker label="Date of birth" .isDateDisabled="isWeekend"></nord-date-picker>
</template>

You can integrate Nord components with RouterLink components from Vue Router like this:

<router-link to="/home" custom v-slot="{ navigate, href, route }">
  <nord-button :href="href" @click="navigate">{{ route.fullPath }}</nord-button>
</router-link>

Check out the Vue Router documentation for more information.

Styling

Because our Web Components come with their styles built in, and our CSS Framework comes with utility helpers, you should very rarely need to add CSS to Vue components. However, if you need to add custom styles, please use our tokens instead of fixed values and add scoped CSS within Single-File Components:

<template>
  <nord-badge>Status</nord-badge>
</template>

<style scoped>
nord-badge {
  text-transform: capitalize;
}
</style>

Using the scoped attribute will ensure the styles don’t leak out of your component and affect other components.

Check out the official Vue documentation for more information.

Types and editor integration

Nord's components work out of the box with Vue, but they don't provide any TypeScript support or editor integration. To improve the developer experience, we offer the @nordhealth/vue package. This package only contains types and no code.

To integrate these into your project, install @nordhealth/components as you normally would, along with @nordhealth/vue. Then add the following to your tsconfig.json in a TypeScript project, or jsconfig.json in a JavaScript project:

{
  "compilerOptions": {
    "types": ["@nordhealth/vue"]
  }
}

Now you will get full type checking and auto-completion in your Vue templates.

Note: to use components you should still import from the @nordhealth/components package.


Can I use this in my own project?

Nord Design System is solely meant for building digital products and experiences for Nordhealth. Please see the terms of use for full license details.


Support

Need help with our Web Components? Please head over to the Support page for more guidelines and ways to contact us.


Attribution

Special thanks to the following projects that help make Nord possible.


Was this page helpful?

YesNo
Send feedback

We use this feedback to improve our documentation.

 
Edit page