Feedback

Retry Toast

An error toast with a way out: the retry action comes first and the technical detail hides behind a disclosure.

  • toast
  • error
  • retry
  • details

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/toast-005?lang=en

Could not save the draft

Соединение оборвалось на середине. Текст остался в браузере.

Подробности
PUT /api/drafts/8412 — 504 Gateway Timeout
17:42:06, попытка 2 из 3
Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "toast-005" (Retry Toast) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/toast-005.json

Registry item: https://vibeui.ru/r/toast-005.json
Installs to: components/vibeui/toast-005.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
An error toast with a way out: the retry action comes first and the technical detail hides behind a disclosure.

An error toast with a retry action and technical detail inside a native <details>. Zero dependencies, one file, no client JS.

## 3. How to use it
import { Toast005 } from "@/components/vibeui/toast-005"

<Toast005
  title="Could not save the draft"
  message="The connection dropped. Your text is still in the browser."
  retryLabel="Retry"
  details="PUT /api/drafts/8412 — 504"
/>

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-toast-005-* palette — do not swap it for your theme tokens
- the order «way out first, detail second»: the retry button must come first
- role="alert" on the card — the failure has to reach a screen reader without any action
- the native <details> instead of your own state: the disclosure works without JS
- the stripped summary marker with its focus ring kept — otherwise the disclosure control disappears
- the card's own light surface: without it the dark text disappears on a dark background

## 6. You may change
- the heading through title and the explanation through message
- the action caption through retryLabel
- the disclosure content through details — status code, URL, timestamp
- the error tone through the --vibeui-toast-005-tone variable

## 7. Rules
- The message text is written for a person: put the stack trace in details, not in the first line.
- An empty details string removes the disclosure entirely and keeps the retry button.
- Wrapping is on inside the <pre>: a long URL will not stretch the card sideways.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/toast-005.json
https://vibeui.ru/r/toast-005.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-toast-005-*. Серверный: состояние раскрытия держит нативный <details>, обработчик не нужен. Маркер summary снят через list-style:none и ::-webkit-details-marker, поэтому строка выглядит ссылкой, но остаётся кнопкой раскрытия. Карточка объявлена role="alert": ошибка читается сразу. Левая грань окрашена тоном ошибки — цвет дублирован значком и словами, не только заливкой.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Toast005Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children" | "title"
> & {
  title?: string
  /** Что делать пользователю. Короткая строка, без стека вызовов. */
  message?: string
  retryLabel?: string
  /** Технические подробности под раскрытием: код, адрес, время. */
  details?: string
  onRetry?: () => void
}

// Идея компонента: уведомление об ошибке, из которого можно выйти. Действие
// «Повторить» стоит первым, а технические подробности спрятаны в <details> —
// они нужны раз в сто ошибок и не должны занимать место в остальных случаях.
const STYLES = `
:where([data-vibeui-block="toast-005"]){
--vibeui-toast-005-bg:oklch(0.99 0.008 25);
--vibeui-toast-005-fg:oklch(0.26 0.03 25);
--vibeui-toast-005-muted:oklch(0.5 0.03 25);
--vibeui-toast-005-border:oklch(0.87 0.04 25);
--vibeui-toast-005-tone:oklch(0.55 0.2 25);
--vibeui-toast-005-code:oklch(0.96 0.012 25);
--vibeui-toast-005-radius:0.875rem;
--vibeui-toast-005-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
--vibeui-toast-005-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
[data-vibeui-block="toast-005"]{
display:flex;gap:0.75rem;align-items:flex-start;
width:100%;max-width:24rem;box-sizing:border-box;
padding:0.875rem;
border:1px solid var(--vibeui-toast-005-border);
border-left:3px solid var(--vibeui-toast-005-tone);
border-radius:var(--vibeui-toast-005-radius);
background:var(--vibeui-toast-005-bg);color:var(--vibeui-toast-005-fg);
font-family:var(--vibeui-toast-005-font);
box-shadow:0 18px 38px -26px oklch(0.3 0.06 25 / 45%);
}
[data-vibeui-block="toast-005"] [data-part="badge"]{
flex:none;display:flex;align-items:center;justify-content:center;
width:1.375rem;height:1.375rem;margin-top:0.0625rem;border-radius:9999px;
background:var(--vibeui-toast-005-tone);color:oklch(0.99 0.01 25);
font-size:0.8125rem;font-weight:800;line-height:1;
}
[data-vibeui-block="toast-005"] [data-part="body"]{display:flex;flex-direction:column;gap:0.375rem;min-width:0;flex:1 1 auto}
[data-vibeui-block="toast-005"] [data-part="title"]{margin:0;font-size:0.875rem;font-weight:650;line-height:1.35}
[data-vibeui-block="toast-005"] [data-part="message"]{margin:0;font-size:0.8125rem;line-height:1.45;color:var(--vibeui-toast-005-muted)}
[data-vibeui-block="toast-005"] [data-part="row"]{display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;margin-top:0.125rem}
[data-vibeui-block="toast-005"] [data-part="retry"]{
appearance:none;cursor:pointer;border:0;
height:1.875rem;padding:0 0.75rem;border-radius:0.5rem;
background:var(--vibeui-toast-005-tone);color:oklch(0.99 0.01 25);
font:inherit;font-size:0.8125rem;font-weight:650;
transition:filter .16s ease;
}
[data-vibeui-block="toast-005"] [data-part="retry"]:hover{filter:brightness(1.08)}
[data-vibeui-block="toast-005"] [data-part="retry"]:focus-visible{outline:2px solid var(--vibeui-toast-005-tone);outline-offset:2px}
/* Подробности под раскрытием: нужны редко, места занимают много. */
[data-vibeui-block="toast-005"] [data-part="more"]{min-width:0}
[data-vibeui-block="toast-005"] [data-part="summary"]{
cursor:pointer;list-style:none;
font-size:0.8125rem;font-weight:600;color:var(--vibeui-toast-005-muted);
text-decoration:underline;text-underline-offset:0.2em;
border-radius:0.25rem;
}
[data-vibeui-block="toast-005"] [data-part="summary"]::-webkit-details-marker{display:none}
[data-vibeui-block="toast-005"] [data-part="summary"]:focus-visible{outline:2px solid var(--vibeui-toast-005-tone);outline-offset:2px}
[data-vibeui-block="toast-005"] [data-part="code"]{
margin:0.5rem 0 0;padding:0.5rem 0.625rem;border-radius:0.5rem;
background:var(--vibeui-toast-005-code);
font-family:var(--vibeui-toast-005-mono);font-size:0.75rem;line-height:1.5;
white-space:pre-wrap;word-break:break-word;color:var(--vibeui-toast-005-muted);
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="toast-005"] *{animation:none!important;transition:none!important}}
`

/**
 * Уведомление об ошибке с повтором и техническими подробностями под раскрытием.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Toast005({
  title = "Не удалось сохранить черновик",
  message = "Соединение оборвалось на середине. Текст остался в браузере.",
  retryLabel = "Повторить",
  details = "PUT /api/drafts/8412 — 504 Gateway Timeout\n17:42:06, попытка 2 из 3",
  onRetry,
  className,
  style,
  ...props
}: Toast005Props) {
  return (
    <>
      <style href="vibeui-toast-005" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="toast-005"
        role="alert"
        className={className}
        style={style as CSSProperties}
      >
        <span data-part="badge" aria-hidden="true">
          !
        </span>
        <div data-part="body">
          <p data-part="title">{title}</p>
          <p data-part="message">{message}</p>
          <div data-part="row">
            <button data-part="retry" type="button" onClick={onRetry}>
              {retryLabel}
            </button>
            {details ? (
              <details data-part="more">
                <summary data-part="summary">Подробности</summary>
                <pre data-part="code">{details}</pre>
              </details>
            ) : null}
          </div>
        </div>
      </div>
    </>
  )
}