Styling

Styling

okayy ships with sensible defaults that look great out of the box, but every element of the confirmation dialog can be fully customized. You can target elements using CSS data attributes for global styling, or pass className props for one-off adjustments.

Data attribute selectors

Every element in the dialog exposes a data-okayy-* attribute that you can target directly in your CSS. This approach avoids specificity issues since attribute selectors don't compete with class-based utility frameworks.

The full set of available selectors:

  • [data-okayy] -- the portal root wrapper
  • [data-okayy-overlay] -- the backdrop overlay behind the dialog
  • [data-okayy-dialog] -- the dialog container
  • [data-okayy-content] -- the content area (title + description)
  • [data-okayy-header] -- the header row (icon + title)
  • [data-okayy-icon] -- the variant icon wrapper
  • [data-okayy-title] -- the title text
  • [data-okayy-description] -- the description text
  • [data-okayy-keyword] -- the type-to-confirm wrapper
  • [data-okayy-keyword-label] -- the keyword label text
  • [data-okayy-keyword-input] -- the keyword input field
  • [data-okayy-footer] -- the button row
  • [data-okayy-button] -- all buttons (base selector)
  • [data-okayy-cancel] -- the cancel button
  • [data-okayy-action] -- custom action buttons
  • [data-okayy-confirm] -- the confirm button
  • [data-okayy-spinner] -- the loading spinner

State and variant attributes on the dialog:

  • [data-state="initial|open|closed"] -- animation state (on overlay and dialog)
  • [data-variant="default|danger|warning|info|success"] -- visual variant
  • [data-layout="default|centered"] -- layout mode
  • [data-size="sm|md|lg|xl|full"] -- size preset
  • [data-custom] -- present when using confirm.custom()
  • [data-unstyled] -- present when unstyled: true
/* The dialog container */
[data-okayy-dialog] {
  border-radius: 16px;
}
 
/* The overlay backdrop */
[data-okayy-overlay] {
  background: rgba(0, 0, 0, 0.8);
}
 
/* The title */
[data-okayy-title] {
  font-size: 18px;
}
 
/* The description */
[data-okayy-description] {
  font-size: 14px;
}
 
/* The confirm button */
[data-okayy-confirm] {
  font-weight: 600;
}
 
/* The cancel button */
[data-okayy-cancel] {
  opacity: 0.7;
}

className props

For one-off styling that only applies to a specific confirmation, you can pass className and overlayClassName directly in the options object. className applies to the dialog container, while overlayClassName controls the backdrop overlay. These are useful when you need a unique look for a particular confirmation without writing global CSS.

Tailwind CSS

For full Tailwind control, use the unstyled prop on <Confirmer /> to strip all default visual styles, then provide your classes via classNames.

Global Tailwind styling:

<Confirmer
  unstyled
  defaultOptions={{
    classNames: {
      dialog: 'bg-white dark:bg-gray-900 rounded-xl p-6 shadow-xl border border-gray-200 dark:border-gray-800 max-w-sm w-full',
      overlay: 'bg-black/60 backdrop-blur-sm',
      title: 'text-lg font-semibold text-gray-900 dark:text-gray-100',
      description: 'text-sm text-gray-500 dark:text-gray-400 mt-2',
      confirmButton: 'bg-gray-900 dark:bg-white text-white dark:text-gray-900 px-4 py-2 rounded-lg font-medium hover:opacity-90',
      cancelButton: 'border border-gray-200 dark:border-gray-700 px-4 py-2 rounded-lg font-medium hover:bg-gray-50 dark:hover:bg-gray-800',
      footer: 'flex justify-end gap-2 mt-6',
    },
  }}
/>

Per-dialog Tailwind styling:

const ok = await confirm({
  title: 'Delete project?',
  unstyled: true,
  classNames: {
    dialog: 'bg-red-50 border-red-200 rounded-xl p-6 max-w-sm w-full',
    title: 'text-red-900 text-lg font-bold',
    confirmButton: 'bg-red-600 text-white px-4 py-2 rounded-lg',
  },
});

The classNames object supports these keys:

KeyElement
dialogThe dialog container
overlayThe backdrop overlay
titleThe title text
descriptionThe description text
confirmButtonThe confirm button
cancelButtonThe cancel button
actionButtonCustom action buttons
iconThe variant icon wrapper
contentThe content wrapper
footerThe button row

Changing icons

Each confirmation variant (danger, warning, info, success) renders a default icon. You can override these at two levels.

Globally, set the icons prop on the <Confirmer /> component to replace the default icons across your entire app:

<Confirmer
  icons={{
    danger: <TrashIcon />,
    warning: <AlertIcon />,
    info: <InfoIcon />,
    success: <CheckIcon />,
  }}
/>

Per-call, pass an icon prop to override the icon for a single confirmation. Icons can be any valid React element -- an SVG component, an emoji string, or a custom component:

Dark mode

okayy supports three theme modes: light, dark, and system. Set the theme prop on <Confirmer /> to control which mode is used:

// Follow OS preference
<Confirmer theme="system" />
 
// Force dark
<Confirmer theme="dark" />
 
// Force light
<Confirmer theme="light" />

When set to system, okayy automatically detects the .dark class on your <html> element and adjusts accordingly. This works seamlessly with next-themes, tailwind dark mode, or any library that toggles a .dark class -- no additional configuration required.

CSS custom properties

Override these variables on [data-okayy] to customize colors, fonts, and shadows without writing component-level CSS. All variables are set in light mode by default and overridden inside .dark [data-okayy] for dark mode.

VariableDescriptionLight default
--okayy-fontFont family stackGeist Sans, Inter, system-ui
--okayy-overlay-bgOverlay backdrop colorrgba(0, 0, 0, 0.8)
--okayy-dialog-bgDialog background#ffffff
--okayy-dialog-shadowDialog box-shadowMulti-layer shadow
--okayy-dialog-borderDialog border color#e5e5e5
--okayy-text-colorPrimary text color#0a0a0a
--okayy-cancel-bgCancel button backgroundtransparent
--okayy-cancel-borderCancel button border#e5e5e5
--okayy-cancel-textCancel button text color#0a0a0a
--okayy-cancel-hover-bgCancel button hover background#f5f5f5
--okayy-confirm-bgConfirm button background#0a0a0a
--okayy-confirm-borderConfirm button border#0a0a0a
--okayy-confirm-textConfirm button text color#fafafa
--okayy-confirm-hover-bgConfirm button hover background#171717
--okayy-ring-colorFocus ring color#0a0a0a
--okayy-icon-colorVariant icon color#0a0a0a
--okayy-mutedMuted text/accents#737373

Each variant (danger, warning, info, success) overrides --okayy-confirm-*, --okayy-icon-color, and --okayy-ring-color with its own palette.

/* Example: brand-colored confirm button */
[data-okayy] {
  --okayy-confirm-bg: #4f46e5;
  --okayy-confirm-border: #4f46e5;
  --okayy-confirm-hover-bg: #4338ca;
  --okayy-ring-color: #4f46e5;
}

Responsive behavior

On viewports narrower than 640px, the dialog automatically switches to a bottom sheet layout — sliding up from the bottom with a drag handle indicator. Buttons stack vertically with 44px minimum touch targets, and safe-area insets are respected for devices with notches.

No configuration is needed — the responsive layout is handled entirely in CSS via @media (max-width: 639px).

Animations

okayy uses CSS keyframe animations controlled by the data-state attribute:

  • data-state="initial" — dialog is mounted but not yet visible
  • data-state="open" — animating in (scale + fade on desktop, slide up on mobile)
  • data-state="closed" — animating out

When prefers-reduced-motion: reduce is active, scale and slide animations are replaced with a simple fade, respecting the user's accessibility preference.

RTL support

Set dir="rtl" on individual dialogs or globally via the <Confirmer dir="rtl" /> prop. The dialog layout, button order, and text alignment all flip automatically. Use dir="auto" to detect direction from the page's <html> element.