Learn Customizing overlays

Overlays ship with minimal styling. When opening an overlay, you can pick a size, assign a CSS class, position popups and drawers, and choose open and close animations. Beyond that, you can override Unpoly's CSS or change the overlay's elements with JavaScript.

Picking a mode

An overlay's default look and behavior come from its mode, which is chosen when the overlay is opened. Every customization below applies on top of a mode, so start with the mode closest to what you need.

Defaults for all overlays live in up.layer.config.overlay. Each mode has its own defaults in up.layer.config.modal, up.layer.config.drawer, up.layer.config.popup and up.layer.config.cover.

Overlay sizes

To give an overlay a width, set an [up-size] attribute on the link that opens it:

<a href="/terms" up-layer="new" up-size="large">Show terms</a> <!-- mark: up-size="large" -->

From JavaScript, pass a { size } option to up.layer.open().

When no size is given, the overlay is medium. A size sets the width of the overlay's box element. The default widths are:

Mode small medium large full grow
modal 350px 650px 1000px 100% grows with content
popup 180px 300px 550px 100% grows with content
drawer 150px 340px 600px 100% grows with content
cover - - - 100% -

Regardless of size, an overlay never grows wider than the screen.

To change the width of a size, override Unpoly's CSS:

up-modal[size=medium] up-modal-box {
  width: 500px;
}

Overlay classes

To make a variant of a mode, like a modal with a warning color, set an [up-class] attribute when opening the overlay:

<a href="/confirm-erase" up-method="delete" up-layer="new" up-class="warning">Erase disk</a> <!-- mark: up-class="warning" -->

From JavaScript, pass a { class } option to up.layer.open().

The class is assigned to the overlay's container element:

<up-modal class="warning"> <!-- mark: class="warning" -->
  ...
</up-modal>

You can now style warning modals in your CSS:

up-modal.warning up-modal-box {
  background-color: #fff3cd;
}

To give every overlay a class, configure up.layer.config.overlay.class.

A popup is anchored to the link or button that opened it (the { origin }). By default it opens below that element, with their left edges aligned. To change this, set [up-position] and [up-align] attributes on the opening link:

<a href="/help" up-layer="new popup" up-position="top" up-align="right">Show help</a> <!-- mark: up-position="top" up-align="right" -->

From JavaScript, pass { position, align } options to up.layer.open().

The following combinations are supported:

{ position } { align } Effect
top left Popup sits above the origin. Left edges align.
top right Popup sits above the origin. Right edges align.
top center Popup sits above the origin. Horizontal centers align.
bottom left Popup sits below the origin. Left edges align.
bottom right Popup sits below the origin. Right edges align.
bottom center Popup sits below the origin. Horizontal centers align.
left top Popup sits left of the origin. Top edges align.
left bottom Popup sits left of the origin. Bottom edges align.
left center Popup sits left of the origin. Vertical centers align.
right top Popup sits right of the origin. Top edges align.
right bottom Popup sits right of the origin. Bottom edges align.
right center Popup sits right of the origin. Vertical centers align.

To change the default for all popups, configure up.layer.config.popup.position and up.layer.config.popup.align.

Drawer position

By default a drawer slides in from the left edge of the screen. To open it on the right edge, set an [up-position="right"] attribute:

<a href="/settings" up-layer="new drawer" up-position="right">Settings</a> <!-- mark: up-position="right" -->

From JavaScript, pass a { position: 'right' } option to up.layer.open(). The drawer's open and close animations follow its position.

To open all drawers on the right, configure up.layer.config.drawer.position.

Open and close animations

Overlays fade in and out by default. Drawers slide in from their screen edge.

To use different animations for one overlay, set [up-animation] and [up-close-animation] attributes on the link that opens it:

<a href="/details" up-layer="new" up-animation="move-from-top" up-close-animation="move-to-top"> <!-- mark: up-animation="move-from-top" up-close-animation="move-to-top" -->
  Open details
</a>

From JavaScript, pass { animation, closeAnimation } options to up.layer.open(). Both directions can also be timed with [up-duration], [up-easing], [up-close-duration] and [up-close-easing] attributes, or the matching up.layer.open() options.

To change the defaults for all overlays, configure up.layer.config.overlay.openAnimation and up.layer.config.overlay.closeAnimation. Each mode can override these in its own config, like up.layer.config.modal.openAnimation.

A single call to up.layer.accept() or up.layer.dismiss() can also pick its own close animation.

Dismiss controls

By default the user can dismiss an overlay by pressing Escape, by clicking the backdrop or by clicking the × button in the top-right corner. Popups have no backdrop and no × button. They close when the user clicks anywhere outside the popup.

To change which of these controls are available, pass a { dismissible } option or [up-dismissible] attribute when opening the overlay. The link below opens an overlay that has a close button (×), but cannot be dismissed by pressing Escape or clicking the backdrop:

<a href="/terms" up-layer="new" up-dismissible="button">Show terms</a> <!-- mark: up-dismissible="button" -->

The following control names are available:

Control name Effect Dismissal value
key Enables dismissing with the Escape key ":key"
outside Enables dismissing by clicking the backdrop ":outside"
button Adds a close button (×) to the layer ":button"

To enable multiple dismiss controls, separate their names with a comma or space:

<a href="/terms" up-layer="new" up-dismissible="button key">Show terms</a> <!-- mark: up-dismissible="button key" -->

Regardless of which dismiss controls are enabled, an overlay can always be dismissed with up.layer.dismiss() or an [up-dismiss] element.

Dismiss icon

Most overlay modes have a button (×) in the top-right corner that dismisses the overlay.

You can change the symbol and accessibility label for that button:

up.layer.config.overlay.dismissLabel // result: '×'
up.layer.config.overlay.dismissARIALabel // result: 'Dismiss dialog'

Styling overlays with CSS

Unpoly ships with some basic CSS for each overlay mode. You can override it in your own stylesheet.

The overlay HTML structure

A modal overlay is built from these elements:

<up-modal size="medium">                                   <!-- container with attributes -->
  <up-modal-backdrop></up-modal-backdrop>                  <!-- dims the page behind the box -->
  <up-modal-viewport>                                      <!-- scrolls the box -->
    <up-modal-box>                                         <!-- white box with padding -->
      <up-modal-content>...</up-modal-content>             <!-- parent of your fragment (unstyled) -->
      <up-modal-dismiss>×</up-modal-dismiss>               <!-- dismiss button -->
    </up-modal-box>
  </up-modal-viewport>
</up-modal>

Drawers have the same structure with <up-drawer>, <up-drawer-box> and so on. Cover overlays use <up-cover> elements and have no backdrop.

Popups are simpler. They have no backdrop, no viewport and no dismiss button, and the container is the box itself:

<up-popup size="medium" position="bottom" align="left">
  <up-popup-content>...</up-popup-content>
</up-popup>

Overriding the default styles

Target these elements in your CSS to restyle a mode:

up-modal-box {
  background-color: #eeeeee;
  padding: 20px;
}

Your settings for size, position and alignment are reflected as attributes on the container element, so you can style a particular variant. The selector below only matches the box of a drawer on the right edge:

up-drawer[position=right] up-drawer-box {
  border-left: 1px solid #ccc;
}

Styling the page while an overlay is open

To change the page behind an open overlay, like blurring it, use a :has() selector on the <body>:

body:has(> :is(up-modal, up-drawer, up-cover)) main {
  filter: blur(2px);
}

Unlike a class that you set from JavaScript, the selector stops matching when the overlay element is removed, no matter how the overlay was closed.

Modals, drawers and covers are appended to the <body>. This lets the selector use the child combinator (>), which is cheap for the browser to check, since it only looks at the body's children.

Popups are attached to the viewport around the element that opened them, so they aren't always direct children of the <body>. To also match open popups, use body:has(up-popup).

Customizing overlay elements

The overlay structure above is fixed and cannot be changed through an option. To add your own elements, listen to up:layer:opened. The event is emitted after the overlay was inserted into the DOM, but before it is painted by the browser:

up.on('up:layer:opened', function(event) {
  if (isChristmas()) {
    up.element.affix(event.layer.element, '.santa-hat', { text: 'Merry Christmas!' })
  }
})

Do not remove any of the existing overlay elements, or the overlay will break.