Skip to main content

Modal Ready

Modals are used to display content that temporarily blocks interactions with the main view of an application. Modals should be used sparingly only when necessary.

Modal component is positioned over everything else in the application preventing scrolling of the main document, making only the modal’s content scrollable. Duet only supports one modal dialog at a time as nested modals are often a sign of bad user experience.

Examples #

Open in new window
<duet-modal
size="small"
heading="Onko matkapuhelimesi tai tablettisi rikkoutunut?"
icon="damage-breakage"
class="duet-text-center"
active
>

<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam quis nostrud.
</duet-paragraph>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Jatka </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to the modal component below
var modal = document.querySelector("duet-modal")

// Listen for open events
modal.addEventListener("duetOpen", function () {
console.log("Modal opened")
})

// Listen for close events
modal.addEventListener("duetClose", function () {
console.log("Modal closing start", Date.now())
})

// Listen for closing animation completion
modal.addEventListener("duetCloseComplete", function () {
console.log("Modal closing done ", Date.now())
})

// Listen for beforeClose events
modal.addEventListener("duetBeforeClose", function (e) {
var event = e.detail.originalEvent
if (!confirm("About to dismiss the modal, are you sure you want to continue?")) {
event.preventDefault()
console.log("Modal not closed as user prevented it")
}
})
</script>
Open in new window
<duet-modal
size="small"
heading="Onko matkapuhelimesi tai tablettisi rikkoutunut?"
icon="damage-breakage"
class="duet-text-center"
active
heading-visual-level="h3"
heading-level="h1"
>

<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam quis nostrud.
</duet-paragraph>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Jatka </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to the modal component below
var modal = document.querySelector("duet-modal")

// Listen for open events
modal.addEventListener("duetOpen", function () {
// console.log("Modal opened")
})

// Listen for close events
modal.addEventListener("duetClose", function () {
// console.log("Modal closed")
})

// Listen for beforeClose events
modal.addEventListener("duetBeforeClose", function (e) {
var event = e.detail.originalEvent
if (!confirm("About to dismiss the modal, are you sure you want to continue?")) {
event.preventDefault()
// console.log("Modal not closed as user prevented it")
}
})
</script>
Open in new window
<duet-modal
heading="Oletko varma, että haluat poistua tältä&nbsp;sivulta?"
icon="messaging-alert"
color="warning"
size="small"
variation="dialog"
class="duet-text-center"
active
>

<duet-paragraph> Kaikki täyttämäsi tiedot poistetaan. </duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Kyllä </duet-button>
<duet-button onclick="modal.hide()"> Ei </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
var modal = document.querySelector("duet-modal")
</script>
Open in new window
<duet-modal heading="Tarkista tietosi" icon="profile-personal-info" active>
<form novalidate action="#">
<duet-paragraph class="duet-text-center"> Kaikki kentät ovat pakollisia ellei muuten mainita. </duet-paragraph>
<duet-spacer size="large"></duet-spacer>
<duet-choice-group value="suomi" label="Asiointikieli" direction="horizontal" name="group" responsive>
<duet-choice label="Suomi" type="radio" value="suomi" expand></duet-choice>
<duet-choice label="Svenska" type="radio" value="svenska" expand></duet-choice>
<duet-choice label="English" type="radio" value="english" expand></duet-choice>
</duet-choice-group>
<duet-grid responsive>
<duet-input label="Etunimi" name="firstname" placeholder="Matti" expand></duet-input>
<duet-input label="Sukunimi" name="lastname" placeholder="Meikäläinen" expand></duet-input>
</duet-grid>
<duet-input label="Kansalaisuus" name="citizenship" icon="form-location" value="Suomi" expand></duet-input>
<duet-input label="Katuosoite" name="streetaddress" placeholder="Kotikatu 123" expand></duet-input>
<duet-grid responsive>
<duet-input label="Postinumero" name="zip" placeholder="00100"></duet-input>
<duet-input label="Kaupunki" name="city" placeholder="Helsinki" expand></duet-input>
</duet-grid>
<duet-input label="Maa" name="country" icon="form-location" value="Suomi" expand></duet-input>
<duet-input label="Tilinumero" name="bankaccount" placeholder="FI28 12345 67890" expand></duet-input>
<duet-spacer size="large"></duet-spacer>
<duet-button variation="primary" onclick="modal.hide()">Jatka</duet-button>
<duet-button onclick="modal.hide()">Peruuta</duet-button>
</form>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
var modal = document.querySelector("duet-modal")
</script>
Open in new window
<duet-modal heading="Verkkopalvelun käyttöehdot" icon="messaging-success" active>
<duet-spacer size="large"></duet-spacer>
<duet-heading level="h4">Tervetuloa LähiTapiolan verkkopalveluun</duet-heading>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
<duet-link url="#">ullamco laboris nisi ut aliquip</duet-link> ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate.
</duet-paragraph>
<duet-heading level="h4">Käytön edellytykset</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</duet-paragraph>
<duet-heading level="h4">Verkkopalvelun sisältö</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
</duet-paragraph>
<duet-heading level="h4">Henkilötietojen käsittely</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa.
</duet-paragraph>
<duet-spacer></duet-spacer>
<div class="duet-text-center">
<duet-button variation="primary" onclick="modal.hide()"> Hyväksyn verkkopalvelun käyttöehdot </duet-button>
</div>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to modal component to use in buttons
var modal = document.querySelector("duet-modal")
</script>
Open in new window
<duet-modal size="large" heading="Verkkopalvelun käyttöehdot" icon="messaging-success" active>
<duet-spacer size="large"></duet-spacer>
<duet-heading level="h4">Tervetuloa LähiTapiolan verkkopalveluun</duet-heading>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
<duet-link url="#">ullamco laboris nisi ut aliquip</duet-link> ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate.
</duet-paragraph>
<duet-heading level="h4">Käytön edellytykset</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</duet-paragraph>
<duet-heading level="h4">Verkkopalvelun sisältö</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
</duet-paragraph>
<duet-heading level="h4">Henkilötietojen käsittely</duet-heading>
<duet-paragraph
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa.
</duet-paragraph>
<duet-spacer></duet-spacer>
<div class="duet-text-center">
<duet-button variation="primary" onclick="modal.hide()"> Hyväksyn verkkopalvelun käyttöehdot </duet-button>
</div>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to modal component to use in buttons
var modal = document.querySelector("duet-modal")
</script>
Open in new window
<duet-modal
size="large"
gutter-size="small"
heading="Onko matkapuhelimesi tai tablettisi rikkoutunut?"
heading-level="h1"
class="duet-text-center"
active
>

<img src="https://cdn.duetds.com/api/assets/illustrations/lt-booking.png" slot="top" />
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam quis nostrud.
</duet-paragraph>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Jatka </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to the modal component below
var modal = document.querySelector("duet-modal")

// Listen for open events
modal.addEventListener("duetOpen", function () {
// console.log("Modal opened")
})

// Listen for close events
modal.addEventListener("duetClose", function () {
// console.log("Modal closed")
})

// Listen for beforeClose events
modal.addEventListener("duetBeforeClose", function (e) {
var event = e.detail.originalEvent
if (!confirm("About to dismiss the modal, are you sure you want to continue?")) {
event.preventDefault()
// console.log("Modal not closed as user prevented it")
}
})
</script>
Open in new window
<duet-modal
size="large"
gutter-size="medium"
heading="Onko matkapuhelimesi tai tablettisi rikkoutunut?"
heading-level="h1"
class="duet-text-center"
active
>

<img src="https://cdn.duetds.com/api/assets/illustrations/lt-booking.png" slot="top" />
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam quis nostrud.
</duet-paragraph>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Jatka </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to the modal component below
var modal = document.querySelector("duet-modal")

// Listen for open events
modal.addEventListener("duetOpen", function () {
// console.log("Modal opened")
})

// Listen for close events
modal.addEventListener("duetClose", function () {
// console.log("Modal closed")
})

// Listen for beforeClose events
modal.addEventListener("duetBeforeClose", function (e) {
var event = e.detail.originalEvent
if (!confirm("About to dismiss the modal, are you sure you want to continue?")) {
event.preventDefault()
// console.log("Modal not closed as user prevented it")
}
})
</script>
Open in new window
<duet-modal
size="large"
gutter-size="large"
heading="Onko matkapuhelimesi tai tablettisi rikkoutunut?"
heading-level="h1"
class="duet-text-center"
active
>

<img src="https://cdn.duetds.com/api/assets/illustrations/lt-booking.png" slot="top" />
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam quis nostrud.
</duet-paragraph>
<duet-paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.
</duet-paragraph>
<duet-button variation="primary" onclick="modal.hide()"> Jatka </duet-button>
</duet-modal>

<duet-button variation="primary" onclick="modal.show()"> Show modal </duet-button>

<script>
// Save reference to the modal component below
var modal = document.querySelector("duet-modal")

// Listen for open events
modal.addEventListener("duetOpen", function () {
// console.log("Modal opened")
})

// Listen for close events
modal.addEventListener("duetClose", function () {
// console.log("Modal closed")
})

// Listen for beforeClose events
modal.addEventListener("duetBeforeClose", function (e) {
var event = e.detail.originalEvent
if (!confirm("About to dismiss the modal, are you sure you want to continue?")) {
event.preventDefault()
// console.log("Modal not closed as user prevented it")
}
})
</script>

Properties #

Property Attribute Description Type Default
accessibleCloseLabel accessible-close-label Adds accessible label for the close icon that is only shown for screen readers. This property is always required to create an accessibly interface! Swedish translation for this property is “Stäng fönstret”. string getLocaleString( this.accessibleCloseLabelDefaults, this.language )
accessibleDescribedBy accessible-described-by Indicates the id or a string of space seperated ids of a component(s) that describes the input. string undefined
accessibleDescription accessible-description Aria description the button string undefined
accessibleDetails accessible-details Details of the component string undefined
accessibleLabel accessible-label By default the heading is used aria-label for the modal. If you wish to use something else then you must set this label. string undefined
accessibleLabelledBy accessible-labelled-by Indicates the id or a string of space seperated ids of a component(s) that labels the input. string undefined
accessibleLoaderAnnouncement accessible-loader-announcement When modal used as a lodader, as an audible substitute for the spinner, we use this message string getLocaleString( this.accessibleLoaderAnnouncementDefaults, this.language )
active active Use this property when you need to have the modal dialog initially active. boolean false
closeOnBlur close-on-blur Use this property when you want the modal to close when clicked outside of modal. boolean false
color color Custom color to be used for the icon, as a design token entered in camelCase or kebab-case. Example: "primary". string ""
gutterSize gutter-size Size of the modal window's padding. "large" | "medium" | "none" | "small" "medium"
heading heading Accessible heading displayed in the modal. The modal marks this as the label of the modal when used. This helps screen reader users which is why this is a required property. string ""
headingLevel heading-level Accessible heading size "h1" | "h2" | "h3" | "h4" | "h5" | "h6" "h3"
headingVisualLevel heading-visual-level Makes the visual style mimic a specific heading level. This option allows you to make e.g. h1 visually look like h3, but still keep it h1 in the markup. "h0" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" undefined
icon icon Icon to display above the heading (from Duet’s icons). Example: "form-location" string ""
language language [DEPRECATED] this is now handled via the html lang tag, and is no longer used - kept to avoid breaking changes and ease unit testing

The currently active language. This setting also changes the logo to match the chosen language.
"en" | "fi" | "sv" getLanguage()
size size Size of the modal window. "large" | "medium" | "small" "medium"
theme theme Theme of the modal. "" | "default" | "turva" ""
variation variation Variation of the modal window. Slide-up variation has fixed height so it suits well for situations where the modal content height changes. Loader variation is used when the user initiates an action that takes time to complete - it can be used with addMessage() method to insert accessible message to the modal to inform the user about the progress. "default" | "loader" | "slide-up" "default"

Events #

Event Description Type
duetBeforeClose Emitted before the modal is closed. To prevent the modal from actually closing, use ev.detail.originalEvent.preventDefault(). CustomEvent<{ originalEvent: Event; component: "duet-modal"; }>
duetClose Emitted when the modal is closed. CustomEvent<any>
duetCloseComplete Emitted when the modal closing transition is complete. CustomEvent<any>
duetOpen Emitted when the modal is opened. CustomEvent<any>

Methods #

addMessage(message: string, visible?: boolean) => Promise<void> #

Adds a message to the modal dialog using animation and annoucing it to screen readers.
Primarily for use with the loader variation.

Parameters #

Name Type Description
message string The message to be added to the modal dialog.
visible boolean Set to false if you want to add a non-visual message.

Returns #

Type: Promise<void>

hide() => Promise<void> #

Hides the modal dialog and puts focus back to the original element
that triggered the modal (if we’re still in the same view).

Returns #

Type: Promise<void>

scrollToTop(smooth?: boolean) => Promise<void> #

Scolls the top of the modal's content into view

Parameters #

Name Type Description
smooth boolean Set to false for instant scrolling

Returns #

Type: Promise<void>

show() => Promise<void> #

Shows the modal dialog. Additionally saves the element that triggered
the modal so that focus can be moved back to this specific element when
the modal dialog is closed.

Returns #

Type: Promise<void>

Slots #

Slot Description
"sticky-header" Content will be fixed to the top of the modal when scrolling, typically used with slide-up variation for e.g. a search input
"top" This is a slot that takes any content and will be displayed as the first thing in the hero area (typically an image)

Usage #

This section includes guidelines for designers and developers about the usage of this component in different contexts.

When to use #

  • When you need to display content that temporarily blocks interactions with the main view of an application.
  • When you need to ask a confirmation from a user before proceeding.
  • When the user is required to take an action.
  • For important warnings, as a way to prevent or correct critical errors.

When not to use #

  • For nonessential information that is not related to the current user flow.
  • In the middle of a purchase flow to interrupt it (except as the loader variation when there are lenghty server processes).
  • When the modal requires additional information for decision making that is unavailable in the modal itself.

Variations #

This section describes the different component variations, their purpose, and when to use each variation.

Name Purpose
small Small modal dialog variation is used for confirmation messages and similar.
medium Medium modal dialog should be always used when there’s more than just one paragraph of content.

Accessibility #

This component has been validated to meet the WCAG 2.1 AA accessibility guidelines. You can find additional information regarding accessibility of this component below.

Additional considerations #


Integration

For integration, event and theming guidelines, please see Using Components. This documentation explains how to implement and use Duet’s components across different technologies like Angular, React or Vanilla JavaScript.

Integration guidelines


Tutorials

Follow these practical tutorials to learn how to build simple page layouts using Duet’s CSS Framework, Web Components and other features:

Tutorials

Building Layouts

Tutorials

Using CLI Tools

Tutorials

Creating Custom Patterns

Tutorials

Server Side Rendering

Tutorials

Sharing Prototypes

Tutorials

Usage With Markdown


Troubleshooting

If you experience any issues while using a component, please head over to the Support page for more guidelines and help.