Patterns

Highlight dialog

The dialog that announces one new thing: a feature, a section, a new layout. A square media slot, a short eyebrow, a title, one or two sentences, and two actions: one dismisses it, the other takes the person to what is new.

Updated Oct 8, 2026 by Juan Pablo Turina

Anatomy

A Dialog with its own layout, built on the Dialog, AspectRatio and Button atoms from the design system's Figma (Highlight / Dialog). Open it to see the parts.

  1. 1
    Media

    A square slot (1:1). Whatever you pass is stretched to cover it: an image, a video, an animation. From lg it fills the left half edge to edge; below lg it sits inside the padding with a 10px radius. Ships as a dashed placeholder.

  2. 2
    Close

    The Dialog's own X, top right. Below lg it sits in a 64px header bar; from lg it sits over the text column.

  3. 3
    Eyebrow

    text-xs, bold, in the primary text colour, drawn in capitals. Says what kind of news this is: New feature. Write it in sentence case, the component capitalises it. Optional.

  4. 4
    Title

    text-lg, semibold. The name of the thing, or what changed.

  5. 5
    Description

    text-sm, muted. One or two sentences on what it is for. Optional.

  6. 6
    Actions

    A bordered footer with two buttons: outline to dismiss (Got it!) and Primary to go to the feature (Check it out). Side by side on the right from sm; stacked and full width under sm, Primary first.

Responsive

One component, three layouts, chosen by the viewport. From lg (1024px): 800px wide, media on the left half and text on the right. From sm (640px): 552px wide, a header bar, then media over text. Under sm: the whole screen, same stack, the two buttons full width. The toggle below shows the last two; for the side-by-side one, open the dialog above on a window at least 1024px wide.

Highlight dialog
A declared exception
From lg this dialog is 800px wide and its footer is 84px tall. A kit Dialog stops at 672px and its footer is 64px. This one is allowed to differ because it is for another use: an announcement with media, not a dialog you work in. It holds no fields and no list, and the media needs the width. The exception belongs to this component only; do not copy its width or its footer to another Dialog.

Recipes

From a What's new entry

The person asks for it: a button or a menu item opens the dialog. Pass that element as trigger and the component handles open and close.

<HighlightDialog
  trigger={<Button>What's new</Button>}
  eyebrow="New feature"
  title="Custom checkouts"
  description="Build a separate checkout for each market or campaign, each with its own payment methods."
  media={<img src="/whats-new/custom-checkouts.png" alt="" />}
  primaryLabel="Check it out"
  onPrimary={() => router.push("/checkout/custom")}
/>
Once, on arrival

The dialog opens by itself the first time someone reaches the section and never again. Drive open yourself, and treat every way out (the X, Got it!, Esc, the primary action) as seen. Press the button to simulate a first visit.

Seen: it will not open again.
// `seen` comes from wherever the product remembers it (the user's settings).
const [seen, setSeen] = React.useState(false);

<HighlightDialog
  open={!seen}
  onOpenChange={(open) => { if (!open) setSeen(true); }}
  eyebrow="New feature"
  title="Custom checkouts"
  description="Build a separate checkout for each market or campaign, each with its own payment methods."
  primaryLabel="Check it out"
  onPrimary={() => { setSeen(true); router.push("/checkout/custom"); }}
/>

Import

Copy this import line at the top of the file where you compose this molecule.

A molecule: Dialog + AspectRatio + Button. Everything it draws comes from those atoms.
import { HighlightDialog } from "@/components/patterns/highlight-dialog";

Props

PropTypeDefaultDescription
titlestring (required)—The dialog title, and its accessible name.
primaryLabelstring (required)—Label of the Primary button, the one that takes the person to the feature.
onPrimary() => void—Called when the Primary button is pressed. Closing the dialog here is up to you.
secondaryLabelstring"Got it!"Label of the outline button. It always closes the dialog.
onSecondary() => void—Called when the outline button is pressed, before the dialog closes.
eyebrowstring—Short label over the title, drawn in capitals. Leave it out to hide it.
descriptionReact.ReactNode—One or two sentences under the title.
mediaReact.ReactNodedashed placeholderWhat fills the square slot. It is stretched to cover it (object-cover).
triggerReact.ReactNode—The element that opens the dialog. Leave it out when you drive open yourself.
open / defaultOpenboolean—Controlled or uncontrolled open state.
onOpenChange(open: boolean) => void—Fires on every open and close, whatever caused it.
classNamestring—Extra classes on the dialog surface.

When to use

  • To announce one new feature, section or layout, with something to look at.
  • As the door to a guided tour: the Primary button starts it.
  • From a What's new entry the person opens on their own.

When not to use

  • To ask for a confirmation: use Alert dialog.
  • For a form or a decision with fields: use Dialog.
  • For a small tip on one control: use Popover.
  • For an error or a warning: use Alert.

Usage

Do
  • One announcement per dialog.
  • Keep the description to one or two sentences.
  • Make the Primary button lead somewhere: the feature itself, or its tour.
  • When it opens by itself, show it once.
Don't
  • Don't chain several highlights one after another.
  • Don't add a third action.
  • Don't put text inside the media; it will not be translated or read aloud.
  • Don't open it by itself over a task the person is in the middle of.

Related

Cross-links to the atoms this molecule composes and sibling patterns.

  • DialogThe atom this molecule is built on.
  • Aspect ratioKeeps the media slot square.
  • PopoverFeature tutorials and tips anchored to one element.
  • Guide cardThe other piece with a media slot, for a grid on a page rather than an announcement.