Sakuga Back to Sakuga
Theme
Sound
Jump to a component

Core · Containers

Dialog

Dialog is a desktop window over the page: pickers (references, opening frame, video plan), confirmations (story conflicts) and last checks (clip options).

@anicanon/sakuga/uiv1.0Used onWeb

Live, from @anicanon/sakuga 2.0.0

Sakuga for iOS and Android is coming

iOS

Web only

Dialog is made for the web, and isn’t part of Sakuga for iOS yet.

Android

Web only

Dialog is made for the web, and isn’t part of Sakuga for Android yet.

Use it for
A task or a check that needs the page's whole attention on desktop: choosing references, a story conflict, the last check before something is spent or deleted.
Not for
  • The same task on a phoneSheet
  • A menu or a hover card anchored to what opened itPopover
  • Choosing from a searchable list under a fieldPicker
  • A confirmation that needs no answerToast

Guidelines

  • Title says the task; description says what happens and what it costs, if anything.
  • The main action sits right in the footer, labelled with what it does ("Add 2 references", "Generate clip").
  • It settles in on motion-slow with ease-enter and leaves on motion-base with ease-exit.
  • Its scrim fades in over motion-slow and out over motion-base, both linear, so the fade reads as one.
  • With sound on, it plays the open sound as it arrives and the close sound as it leaves (README · Sound).
  • Phones use Sheet instead.
  • The footer's buttons are footer, or children in slot="footer".
DialogExample.jsxReact
import { useState } from 'react';
import { Button, Dialog } from '@anicanon/sakuga/ui';

export default function DialogExample() {
  const [open, setOpen] = useState(true);
  const close = () => setOpen(false);
  return (
    <>
      <Button variant="secondary" onClick={() => setOpen(true)}>
        Choose a video plan
      </Button>
      <Dialog
        open={open}
        onClose={close}
        title="Choose a video plan"
        description="Plans differ in motion polish and what they need from you."
        width={520}
        footer={[
          <Button key="a" variant="ghost" onClick={close}>
            Cancel
          </Button>,
          <Button key="b" variant="primary" onClick={close}>
            Use Regular
          </Button>,
        ]}
      >
        <p style={{ margin: 0, color: 'var(--text-secondary)' }}>
          Plan cards go here.
        </p>
      </Dialog>
    </>
  );
}

PropsDialogProps

openrequired
boolean
titlerequired
string
description
string
footer
ReactNode
Or children in slot="footer".
width
number | string
onClose
() => void
children
ReactNode
className
string
DialogExample.swiftiOS · coming soon
DialogExample.ktAndroid · coming soon

Each token reads a role, and the role lands on a value in each theme. Change the role and every component that reads it follows.

Colour

dialog-bgDialog: panel.
bg-surfaceneutral-0#ffffff
bg-surfaceneutral-900#111117
dialog-borderDialog: panel edge.
border-dividershade-a12rgba(11,10,16,0.12)
border-dividerwhite-a12rgba(255,255,255,0.12)
dialog-scrimDialog: dim layer behind it.
bg-overlayshade-a40rgba(11,10,16,0.4)
bg-overlayblack-a50rgba(0,0,0,0.5)
dialog-titleDialog: title.
text-primaryneutral-925#0b0a10
text-primaryneutral-0#ffffff
dialog-descriptionDialog: line under the title.
text-mutedneutral-600#5c5b67
text-mutedneutral-400#a3a3a7

Space and shape

dialog-width
its own value
720px
dialog-radius
radius-xl
24px
dialog-inset-ring-width
line-hairline
1px
dialog-head-gap
gap-lg
12px
dialog-head-padding-top
inset-lg
16px
dialog-head-padding-x
inset-xl
24px
dialog-head-padding-bottom
inset-lg
16px
dialog-desc-margin-top
gap-xs
4px
dialog-body-padding-x
inset-xl
24px
dialog-body-padding-bottom
inset-lg
16px
dialog-foot-gap
gap-md
8px
dialog-foot-padding-y
inset-lg
16px
dialog-foot-padding-x
inset-xl
24px
dialog-foot-border-top-width
line-hairline
1px
dialog-forced-edge-width
line-hairline
1px

Type

dialog-font-family
font-sans
Funnel Sans
dialog-title-font
text-title-3
700 20px/26px "Funnel Display", "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif
dialog-desc-font
text-body
400 14px/20px "Funnel Sans", system-ui, -apple-system, "Segoe UI", sans-serif

Motion

dialog-scrim-fade-in-animation
motion-scrim-in
sk-fade-in 360ms linear
dialog-in-animation
its own value
sk-emerge 360ms cubic-bezier(0, 0, 0, 1)
dialog-scrim-leaving-fade-out-animation
motion-scrim-out
sk-fade-out 240ms linear
dialog-scrim-leaving-out-animation
its own value
sk-recede 240ms cubic-bezier(0.3, 0, 1, 1)
dialog-scrim-fade-in-animation
motion-scrim-in
sk-fade-in 360ms linear
dialog-scrim-leaving-fade-out-animation
motion-scrim-out
sk-fade-out 240ms linear

Checked automatically with axe-core 4.11.4 against WCAG 2.2 A and AA, on the live preview above, in light and dark, on 26 October 2026.

Result
No issues found, in light or in dark.
By design
A modal dialog named by title and described by description: focus moves to the first control in its body (else its footer, else "Close"), Tab and Shift+Tab stay inside it, a press on its words keeps focus inside, and Escape, "Close" or a click on the scrim run onClose. When it closes, focus goes back to what opened it.
Not tested
The automated check doesn't test keyboard use, focus order or what a screen reader says. The lines above are the design system's own account of them.