Loading State with messages Ready
This template shows how to build a simple responsive loading state, which shows messages to the user. Please note that the example below does not represent a real use case. Adjust the style of the messages as required by the design.
Hint: Press F on your keyboard to view both templates and components in fullscreen and ESC to exit the fullscreen mode. You can also open the template in a new browser window.
<!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.duetds.com/api/fonts/3.0.54/lib/localtapiola.css" integrity="sha384-5JYmtSD7nykpUvSmTW1CHMoBDkBZUpUmG0vuh+NUVtZag3F75Kr7+/JU3J7JV6Wq" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.duetds.com/api/css/4.2.2/lib/duet.min.css" integrity="sha384-I9LwsWxJZISYNi8vTILW3uDtX9EoPPz9shzRoJQ1Qz5zM4eEcyZ0LOb+Fk9/2YNU" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.duetds.com/api/tokens/4.1.2/lib/tokens.custom-properties.css" integrity="sha384-/rw1rcr74nmctKEYMFnAfZ/7OWf2ygqzO89C8RJyRvAyixuiQ3FMobxxG9u8l91g" crossorigin="anonymous" />
<script type="module" src="https://cdn.duetds.com/api/components/9.19.1/lib/duet/duet.esm.js" integrity="sha384-0lNqeGTUkq0kaxxD83W+aiywH1mNpztNnY0/WNEcIrbT4jfwqyD5RRStN/XOfAZr" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.duetds.com/api/components/9.19.1/lib/duet/duet.js" integrity="sha384-oxostYE9IRZqkfMdin5Jumsr8xxZZVN/ZLwmnNjerd5oG+Jr7fil86yzQ2B85UM4" crossorigin="anonymous"></script>
</head>
<body>
<duet-layout center middle>
<div slot="main">
<duet-heading level="h1">Page with content that is fetched from a slow service</duet-heading>
<duet-paragrap>To start loading the content, click the start button.</duet-paragrap>
<duet-divider></duet-divider>
<duet-spacer></duet-spacer>
<duet-button variation="primary" id="start-button">Start</duet-button>
<duet-spacer size="xx-large"></duet-spacer>
<div id="dynamic-content" aria-live="polite"></div>
</div>
</duet-layout>
<template id="loader-panel-template">
<duet-panel variation="secondary" class="duet-text-center">
<duet-paragraph weigh="semibold">
<span>Loading content, please wait.</span>
</duet-paragraph>
<div>
<duet-spinner size="large" color="primary"></duet-spinner>
</div>
</duet-panel>
</template>
<template id="dynamic-data-template">
<div class="dynamic-data">
<duet-heading level="h2">Some dynamic data</duet-heading>
<duet-list variation="striped">
<duet-list-item>
<span slot="label">Foo</span>
<span slot="value">€ 1500</span>
</duet-list-item>
<duet-list-item>
<span slot="label">Bar</span>
<span slot="value">50.8 %</span>
</duet-list-item>
<duet-list-item>
<span slot="label">Baz</span>
<span slot="value">Lorem ipsum</span>
</duet-list-item>
</duet-list>
</div>
</template>
<style>
div:has(duet-spinner) {
position: relative;
height: 8rem;
}
</style>
<script>
const contentHolder = document.getElementById("dynamic-content")
const loaderTemplate = document.getElementById("loader-panel-template")
const dataTemplate = document.getElementById("dynamic-data-template")
document.getElementById("start-button").addEventListener("click", () => {
const dataWeAreLoding = document.importNode(dataTemplate.content, true)
const loaderPanel = document.importNode(loaderTemplate.content, true)
// we use span inside the duet-paragraph for changing text to ensure screen readers access it correctly
const textContainer = loaderPanel.querySelector("duet-paragraph span")
// For demo purposes, clear content if it is there
contentHolder.querySelector(".dynamic-data")?.remove()
// First show the loder, start adding messages only after that
contentHolder.appendChild(loaderPanel)
setTimeout(() => {
textContainer.textContent = "Taking a bit longer than expected, please be patient."
}, 5000)
setTimeout(() => {
textContainer.textContent = "Process is still going on, please wait."
}, 10000)
setTimeout(() => {
textContainer.textContent = "Five more seconds and we are ready."
}, 15000)
setTimeout(() => {
contentHolder.querySelector("duet-panel").remove()
contentHolder.appendChild(dataWeAreLoding)
}, 20000)
})
</script>
</body>
</html> <!DOCTYPE html>
<html class="duet-theme-turva" lang="fi">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Turva</title>
<link rel="stylesheet" href="https://cdn.duetds.com/api/fonts/3.0.54/lib/turva.css" integrity="sha384-hHdwZODJ+y2QoCpmMYq9dSnwexFN8FO9B9cVru7Y7iy2l3bXKpf/vNfPASXgfKWU" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.duetds.com/api/css/4.2.2/lib/duet.min.css" integrity="sha384-I9LwsWxJZISYNi8vTILW3uDtX9EoPPz9shzRoJQ1Qz5zM4eEcyZ0LOb+Fk9/2YNU" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdn.duetds.com/api/tokens/4.1.2/lib/tokens.custom-properties.css" integrity="sha384-/rw1rcr74nmctKEYMFnAfZ/7OWf2ygqzO89C8RJyRvAyixuiQ3FMobxxG9u8l91g" crossorigin="anonymous" />
<script type="module" src="https://cdn.duetds.com/api/components/9.19.1/lib/duet/duet.esm.js" integrity="sha384-0lNqeGTUkq0kaxxD83W+aiywH1mNpztNnY0/WNEcIrbT4jfwqyD5RRStN/XOfAZr" crossorigin="anonymous"></script>
<script nomodule src="https://cdn.duetds.com/api/components/9.19.1/lib/duet/duet.js" integrity="sha384-oxostYE9IRZqkfMdin5Jumsr8xxZZVN/ZLwmnNjerd5oG+Jr7fil86yzQ2B85UM4" crossorigin="anonymous"></script>
</head>
<body>
<duet-layout center middle>
<div slot="main">
<duet-heading level="h1">Page with content that is fetched from a slow service</duet-heading>
<duet-paragrap>To start loading the content, click the start button.</duet-paragrap>
<duet-divider></duet-divider>
<duet-spacer></duet-spacer>
<duet-button variation="primary" id="start-button">Start</duet-button>
<duet-spacer size="xx-large"></duet-spacer>
<div id="dynamic-content" aria-live="polite"></div>
</div>
</duet-layout>
<template id="loader-panel-template">
<duet-panel variation="secondary" class="duet-text-center">
<duet-paragraph weigh="semibold">
<span>Loading content, please wait.</span>
</duet-paragraph>
<div>
<duet-spinner size="large" color="primary"></duet-spinner>
</div>
</duet-panel>
</template>
<template id="dynamic-data-template">
<div class="dynamic-data">
<duet-heading level="h2">Some dynamic data</duet-heading>
<duet-list variation="striped">
<duet-list-item>
<span slot="label">Foo</span>
<span slot="value">€ 1500</span>
</duet-list-item>
<duet-list-item>
<span slot="label">Bar</span>
<span slot="value">50.8 %</span>
</duet-list-item>
<duet-list-item>
<span slot="label">Baz</span>
<span slot="value">Lorem ipsum</span>
</duet-list-item>
</duet-list>
</div>
</template>
<style>
div:has(duet-spinner) {
position: relative;
height: 8rem;
}
</style>
<script>
const contentHolder = document.getElementById("dynamic-content")
const loaderTemplate = document.getElementById("loader-panel-template")
const dataTemplate = document.getElementById("dynamic-data-template")
document.getElementById("start-button").addEventListener("click", () => {
const dataWeAreLoding = document.importNode(dataTemplate.content, true)
const loaderPanel = document.importNode(loaderTemplate.content, true)
// we use span inside the duet-paragraph for changing text to ensure screen readers access it correctly
const textContainer = loaderPanel.querySelector("duet-paragraph span")
// For demo purposes, clear content if it is there
contentHolder.querySelector(".dynamic-data")?.remove()
// First show the loder, start adding messages only after that
contentHolder.appendChild(loaderPanel)
setTimeout(() => {
textContainer.textContent = "Taking a bit longer than expected, please be patient."
}, 5000)
setTimeout(() => {
textContainer.textContent = "Process is still going on, please wait."
}, 10000)
setTimeout(() => {
textContainer.textContent = "Five more seconds and we are ready."
}, 15000)
setTimeout(() => {
contentHolder.querySelector("duet-panel").remove()
contentHolder.appendChild(dataWeAreLoding)
}, 20000)
})
</script>
</body>
</html> Integration
To install this template’s dependencies into your project, run:
npm install @duetds/components
npm install @duetds/css
npm install @duetds/fonts For further guidelines, please see each package’s documentation.
Tutorials
Follow these practical tutorials to learn how to build simple page layouts using Duet’s CSS Framework, Web Components and other features:
Building Layouts
TutorialsUsing CLI Tools
TutorialsCreating Custom Patterns
TutorialsServer Side Rendering
TutorialsSharing Prototypes
TutorialsUsage With Markdown
Troubleshooting
If you experience any issues while using a template, please head over to the Support page for more guidelines and help.