Skip to content
Devix Open Source

Customize

Styling and theming

Four levels, pick the one that fits:

  1. CSS variables — colours, sizes, radii, the stack's spacing.
  2. classNames — your own classes on every part, per toaster or per toast.
  3. icons and strings — your own SVG and your own words.
  4. render — draw the whole toast yourself; timing, stacking and swiping still apply.

Stylesheets

import '@devix-labs/toast/styles.css';        // plain
import '@devix-labs/toast/styles.layer.css';  // the same, inside @layer devix

The layered build loses to any unlayered rule of yours, so app styles win without !important.

Variables

.dxt {
  --dxt-width: 356px;
  --dxt-gap: 10px;            /* between toasts when the stack is open */
  --dxt-inset: 16px;          /* from the edge of the screen */
  --dxt-radius: 10px;
  --dxt-font-size: 13.5px;
  --dxt-title-weight: 500;
  --dxt-padding: 12px 14px;
  --dxt-collapse: 14px;       /* how far a tucked-away toast peeks out */
  --dxt-collapse-scale: 0.045;

  --dxt-bg: #fff;
  --dxt-text: #18181b;
  --dxt-muted: #71717a;
  --dxt-border: #e4e4e7;
  --dxt-shadow: 0 10px 30px rgb(17 17 28 / .12);
  --dxt-success: #16a34a;
  --dxt-error: #dc2626;
  --dxt-warning: #d97706;
  --dxt-info: #2563eb;
  --dxt-action-bg: #18181b;
  --dxt-action-text: #fff;
  --dxt-progress: currentColor;
}

Dark mode follows the page (.dark, data-theme, data-bs-theme, or the real background colour) and re-reads it when a theme toggle flips. Force it with theme: 'dark', or write .dxt[data-theme='dark'] { … }.

Coloured toasts, the "rich colours" look

.dxt-toast[data-type='success'] { background: #f0fdf4; border-color: #bbf7d0; color: #14532d; }
.dxt-toast[data-type='error'] { background: #fef2f2; border-color: #fecaca; color: #7f1d1d; }
.dxt[data-theme='dark'] .dxt-toast[data-type='success'] { background: #052e1a; border-color: #14532d; color: #dcfce7; }

Tailwind on every part

configure({
  classNames: {
    toast: 'rounded-xl border-zinc-200 shadow-lg',
    title: 'text-sm font-medium',
    description: 'text-xs text-zinc-500',
    action: 'bg-indigo-600 text-white rounded-md',
  },
});

toast('One-off', { classNames: { toast: 'ring-2 ring-indigo-500' } });

Your own markup

toast.custom((node, { dismiss }) => {
  const avatar = Object.assign(document.createElement('img'), { src: user.avatar, className: 'size-8 rounded-full' });
  const text = Object.assign(document.createElement('p'), { textContent: `${user.name} joined` });
  const button = Object.assign(document.createElement('button'), { textContent: 'Say hi' });
  button.onclick = () => { greet(user); dismiss(); };
  node.append(avatar, text, button);
}, { duration: 8000 });

The node you fill is the toast itself, so it keeps the stack position, the swipe, the pause-on-hover and the dismiss timing.

Positions and stacks

Six positions, set per toaster or per toast. For two independent stacks — say, app messages bottom-right and upload progress top-left — make a second toaster:

import { createToaster } from '@devix-labs/toast';
const uploads = createToaster({ position: 'top-left', max: 5 });
uploads.loading('Uploading 3 files…');
Updated 15 Sep 2026