Skip to main content

Creating Custom Patterns #

Following this tutorial you will learn how to build simple accessible UI patterns using Duet’s Design Tokens and basic HTML.

For the sake of simplicity, this tutorial is framework agnostic and imports the different parts of Duet directly from JSDelivr. For production usage we recommend that you install the packages using NPM and host the different parts yourself.

Getting started #

As the first step, you will want to create a basic HTML page that can be used for creating the custom pattern. There are no explicit requirements on what the HTML markup should look like, so let’s just go ahead and create a simple page with the correct lang attribute and a placeholder <title>:

<!doctype html>
<html lang="fi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>LähiTapiola</title>
</head>
<body></body>
</html>

Next, you need to import the necessary packages from Duet Design System in order to utilize them. For this particular custom pattern, we want to use Fonts, CSS Framework, Design Tokens and Components. To import these packages, add the following tags onto your page’s <head> section:

<!-- Fonts -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/fonts@1.3.12/lib/localtapiola.css" integrity="sha384-5JYmtSD7nykpUvSmTW1CHMoBDkBZUpUmG0vuh+NUVtZag3F75Kr7+/JU3J7JV6Wq" crossorigin="anonymous" />

<!-- CSS Framework -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/css@1.4.6/lib/duet.min.css" integrity="sha384-pCjsyofCzuxv5DPuTf+kg3QNWqVN0ZwRTCgvvNWCkI38A42MtkN2EK2I2e28j+wB" crossorigin="anonymous" />

<!-- Design Tokens -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/tokens@2.0.30/lib/tokens.custom-properties.css" integrity="sha384-arb0dRY7eabL9qYMLabGqlrgO9p75pZ9lvaqh3xpLpNUUW6KBtRjKqkSwBv3HyLp" crossorigin="anonymous" />

<!-- Components -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.esm.js" integrity="sha384-ytNH4bICrleu6OXiFr0GctdjoojY5a4B3/H3+Z0wTQOB9eUcbh6i7GbP2QykjM4k" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.js" integrity="sha384-j/SZhqGHRSMeQPpiO0sIu1NANTpsXU23YSk3+nfvGaUM9SvRrOaN15rhsFO4k6L4" crossorigin="anonymous"></script>

Once you’ve added each package shown above, the markup for the HTML page should look about like this:

<!doctype html>
<html lang="fi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>LähiTapiola</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/fonts@1.3.12/lib/localtapiola.css" integrity="sha384-5JYmtSD7nykpUvSmTW1CHMoBDkBZUpUmG0vuh+NUVtZag3F75Kr7+/JU3J7JV6Wq" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/css@1.4.6/lib/duet.min.css" integrity="sha384-pCjsyofCzuxv5DPuTf+kg3QNWqVN0ZwRTCgvvNWCkI38A42MtkN2EK2I2e28j+wB" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/tokens@2.0.30/lib/tokens.custom-properties.css" integrity="sha384-arb0dRY7eabL9qYMLabGqlrgO9p75pZ9lvaqh3xpLpNUUW6KBtRjKqkSwBv3HyLp" crossorigin="anonymous" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.esm.js" integrity="sha384-ytNH4bICrleu6OXiFr0GctdjoojY5a4B3/H3+Z0wTQOB9eUcbh6i7GbP2QykjM4k" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.js" integrity="sha384-j/SZhqGHRSMeQPpiO0sIu1NANTpsXU23YSk3+nfvGaUM9SvRrOaN15rhsFO4k6L4" crossorigin="anonymous"></script>
</head>
<body></body>
</html>

That’s all that’s required to start building custom patterns with Duet. In the next chapter we’ll create a simple link list pattern with a few different variations.

This particular pattern can often be seen in a sidebar of an application, where it lists links that are related to the main content of a view. The finished pattern that we will build in this tutorial will look like this:

Creating custom patterns with Duet

As the first step, you will want to create a container for the above custom pattern. For that purpose, let’s add Duet’s Layout and Card component onto the page inside the <body> tag:

<body>
<duet-layout center>
<div slot="main">
<duet-card padding="small">
<!-- custom pattern will be inserted here -->
</duet-card>
</div>
</duet-layout>
</body>

Now that we have a container ready, we can start creating HTML markup for our custom pattern. For the purposes of this tutorial, we’re going to call it link-list:

<body>
<duet-layout center>
<div slot="main">
<duet-card padding="small">
<div class="link-list">
<a href="#">Tuoteseloste</a>
<a href="#">Ehdot</a>
<a href="#">Avaintietoesite (PDF)</a>
<a href="#">Säännöt</a>
<a href="#">Yhteydenottopyyntö</a>
</div>
</duet-card>
</div>
</duet-layout>
</body>

At this point our custom pattern looks like this:

Creating custom patterns with Duet

While the results might seem very simplistic so far, we don’t necessarily need more markup than this to create our link list pattern. Next, let’s add some styles onto the page to make our list a little prettier:

.link-list a {
border-bottom: 1px solid var(--color-gray-light);
font-weight: var(--font-weight-semi-bold);
padding: var(--space-x-small) 0;
justify-content: space-between;
text-decoration: none;
align-items: center;
display: flex;
width: 100%;
}

With just a few lines of CSS and Design Tokens added above, our list is starting to look closer to finished:

Creating custom patterns with Duet

If you look the above example closely, you may notice we’re missing a border from the first item in the link list. We can add the border with:

.link-list a:first-of-type {
border-top: 1px solid var(--color-gray-light);
}

The full markup for the example so far looks like this:

<style>
.link-list a {
border-bottom: 1px solid var(--color-gray-light);
font-weight: var(--font-weight-semi-bold);
padding: var(--space-x-small) 0;
justify-content: space-between;
text-decoration: none;
align-items: center;
display: flex;
width: 100%;
}
.link-list a:first-of-type {
border-top: 1px solid var(--color-gray-light);
}
</style>

<duet-layout center>
<div slot="main">
<duet-card padding="small">
<div class="link-list">
<a href="#">Tuoteseloste</a>
<a href="#">Ehdot</a>
<a href="#">Avaintietoesite (PDF)</a>
<a href="#">Säännöt</a>
<a href="#">Yhteydenottopyyntö</a>
</div>
</duet-card>
</div>
</duet-layout>
Creating custom patterns with Duet

Adding content components #

To make our example more realistic, let’s add some surrounding content that might be displayed in the sidebar of our app. For this particular example, we’re going to add a heading with description and adjust the spacing a little:

<duet-layout center>
<div slot="main">
<duet-card padding="small">
<duet-heading level="h4">Vakuutuksen kokonaishinta vuodessa</duet-heading>
<duet-caption>Täytä perustiedot nähdäksesi hinta.</duet-caption>
<duet-spacer></duet-spacer>
<div class="link-list">
<a href="#">Tuoteseloste</a>
<a href="#">Ehdot</a>
<a href="#">Avaintietoesite (PDF)</a>
<a href="#">Säännöt</a>
<a href="#">Yhteydenottopyyntö</a>
</div>
<duet-spacer></duet-spacer>
</duet-card>
</div>
</duet-layout>

With these changes, our link list now looks like this:

Creating custom patterns with Duet

Next, we will add the last missing part which is the icons. To find a list of available icons and their names, see Iconography. To add the icons onto the page, we can use Duet’s Icon component:

<duet-layout center>
<div slot="main">
<duet-card padding="small">
<duet-heading level="h4">Vakuutuksen kokonaishinta vuodessa</duet-heading>
<duet-caption>Täytä perustiedot nähdäksesi hinta.</duet-caption>
<duet-spacer></duet-spacer>
<div class="link-list">
<a href="#">
<span>Tuoteseloste</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Ehdot</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Avaintietoesite (PDF)</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Säännöt</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Yhteydenottopyyntö</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
</div>
<duet-spacer></duet-spacer>
</duet-card>
</div>
</duet-layout>

The ready custom link list pattern looks like this:

Creating custom patterns with Duet

Full page markup for the above example:

<!doctype html>
<html lang="fi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>LähiTapiola</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/fonts@1.3.12/lib/localtapiola.css" integrity="sha384-5JYmtSD7nykpUvSmTW1CHMoBDkBZUpUmG0vuh+NUVtZag3F75Kr7+/JU3J7JV6Wq" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/css@1.4.6/lib/duet.min.css" integrity="sha384-pCjsyofCzuxv5DPuTf+kg3QNWqVN0ZwRTCgvvNWCkI38A42MtkN2EK2I2e28j+wB" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/tokens@2.0.30/lib/tokens.custom-properties.css" integrity="sha384-arb0dRY7eabL9qYMLabGqlrgO9p75pZ9lvaqh3xpLpNUUW6KBtRjKqkSwBv3HyLp" crossorigin="anonymous" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.esm.js" integrity="sha384-ytNH4bICrleu6OXiFr0GctdjoojY5a4B3/H3+Z0wTQOB9eUcbh6i7GbP2QykjM4k" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.js" integrity="sha384-j/SZhqGHRSMeQPpiO0sIu1NANTpsXU23YSk3+nfvGaUM9SvRrOaN15rhsFO4k6L4" crossorigin="anonymous"></script>
<style>
.link-list a {
border-bottom: 1px solid var(--color-gray-light);
font-weight: var(--font-weight-semi-bold);
padding: var(--space-x-small) 0;
justify-content: space-between;
text-decoration: none;
align-items: center;
display: flex;
width: 100%;
}
.link-list a:first-of-type {
border-top: 1px solid var(--color-gray-light);
}
</style>
</head>
<body>
<duet-layout center>
<div slot="main">
<duet-card padding="small">
<duet-heading level="h4">Vakuutuksen kokonaishinta vuodessa</duet-heading>
<duet-caption>Täytä perustiedot nähdäksesi hinta.</duet-caption>
<duet-spacer></duet-spacer>
<div class="link-list">
<a href="#">
<span>Tuoteseloste</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Ehdot</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Avaintietoesite (PDF)</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Säännöt</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
<a href="#">
<span>Yhteydenottopyyntö</span>
<duet-icon name="action-arrow-right-small" size="xx-small" margin="none"></duet-icon>
</a>
</div>
<duet-spacer></duet-spacer>
</duet-card>
</div>
</duet-layout>
</body>
</html>

There’s one key difference in the above version compared to the original example, and that’s the fact that our links now open inside the same browser window. What if you need the links to open in a new browser window instead? We need to do two small changes to achieve that and make it accessible.

First, let’s add the correct “new window” icons and make our links open into a blank window by doing the following changes for all our links:

<a href="#" target="_blank">
<span>Tuoteseloste</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
</a>

Second, while we now have a visual indicator that this link is going to open into a new window, we want to have a similar indicator for screen reader users as well. To achieve that, we can use Duet’s Visually Hidden component:

<a href="#" target="_blank">
<span>Tuoteseloste</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>

With these small tweaks the screen reader will now announce to the user that this particular link is going to open into a new browser window. Our finalized link list pattern now looks like this:

Creating custom patterns with Duet

Remember that this newly created custom pattern is fully responsive already so it could be utilized in a narrow sidebar as is.

Ready page markup: #

<!doctype html>
<html lang="fi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>LähiTapiola</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/fonts@1.3.12/lib/localtapiola.css" integrity="sha384-5JYmtSD7nykpUvSmTW1CHMoBDkBZUpUmG0vuh+NUVtZag3F75Kr7+/JU3J7JV6Wq" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/css@1.4.6/lib/duet.min.css" integrity="sha384-pCjsyofCzuxv5DPuTf+kg3QNWqVN0ZwRTCgvvNWCkI38A42MtkN2EK2I2e28j+wB" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@duetds/tokens@2.0.30/lib/tokens.custom-properties.css" integrity="sha384-arb0dRY7eabL9qYMLabGqlrgO9p75pZ9lvaqh3xpLpNUUW6KBtRjKqkSwBv3HyLp" crossorigin="anonymous" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.esm.js" integrity="sha384-ytNH4bICrleu6OXiFr0GctdjoojY5a4B3/H3+Z0wTQOB9eUcbh6i7GbP2QykjM4k" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@duetds/components@4.2.6/lib/duet/duet.js" integrity="sha384-j/SZhqGHRSMeQPpiO0sIu1NANTpsXU23YSk3+nfvGaUM9SvRrOaN15rhsFO4k6L4" crossorigin="anonymous"></script>
<style>
.link-list a {
border-bottom: 1px solid var(--color-gray-light);
font-weight: var(--font-weight-semi-bold);
padding: var(--space-x-small) 0;
justify-content: space-between;
text-decoration: none;
align-items: center;
display: flex;
width: 100%;
}
.link-list a:first-of-type {
border-top: 1px solid var(--color-gray-light);
}
</style>
</head>
<body>
<duet-layout center>
<div slot="main">
<duet-card padding="small">
<duet-heading level="h4">Vakuutuksen kokonaishinta vuodessa</duet-heading>
<duet-caption>Täytä perustiedot nähdäksesi hinta.</duet-caption>
<duet-spacer></duet-spacer>
<div class="link-list">
<a href="#" target="_blank">
<span>Tuoteseloste</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>
<a href="#" target="_blank">
<span>Ehdot</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>
<a href="#" target="_blank">
<span>Avaintietoesite (PDF)</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>
<a href="#" target="_blank">
<span>Säännöt</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>
<a href="#" target="_blank">
<span>Yhteydenottopyyntö</span>
<duet-icon name="action-new-window-small" size="xx-small" margin="none"></duet-icon>
<duet-visually-hidden>Aukeaa uuteen ikkunaan</duet-visually-hidden>
</a>
</div>
</duet-card>
<duet-spacer></duet-spacer>
</div>
</duet-layout>
</body>
</html>