Breadcrumb

Trail With Actions

Строка пути с кнопками справа: на узкой ширине они уходят под путь и остаются подписанными словами, а не сжимаются в иконки.

  • breadcrumb
  • toolbar
  • actions
  • responsive

Превью

1440pxHost theme

Использовать с AI

  1. 1. Скопируйте ссылку.
  2. 2. Напишите агенту своими словами и вставьте её в предложение.
  3. 3. Агент откроет ссылку и поставит компонент из registry.

размести это в шапке: https://vibeui.ru/c/breadcrumb-007

Показать полную инструкцию

То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.

# Установка и размещение «breadcrumb-007» (Trail With Actions) из VibeUI

## 1. Сначала установка — не пропускай, не пересоздавай
Выполни эту команду до того, как писать код:
npx shadcn@latest add https://vibeui.ru/r/breadcrumb-007.json

Registry item: https://vibeui.ru/r/breadcrumb-007.json
Ставится в: components/vibeui/breadcrumb-007.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.

Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.

## 2. Что это
Строка пути с кнопками справа: на узкой ширине они уходят под путь и остаются подписанными словами, а не сжимаются в иконки.

Панель с путём слева и действиями справа: в узкой колонке кнопки переезжают под путь и остаются со словами. Ноль зависимостей, один файл, клиентского JS нет.

## 3. Как использовать
import { Breadcrumb007 } from "@/components/vibeui/breadcrumb-007"

<Breadcrumb007 items={items} actionLabel="Опубликовать" secondaryLabel="Настройки" />

Полный список пропсов — в установленном файле.

## 4. Куда поставить
Это мелкий inline-компонент. Поставь его ровно туда, куда просил
пользователь, внутрь существующей разметки. Не создавай ради него
новую страницу, секцию или обёртку. Если на этом месте уже стоит похожий
контрол — замени его, а не добавляй второй.

Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)

## 5. Сохранить как установлено
- локальную палитру --vibeui-breadcrumb-007-* и не подменять её токенами вашей темы
- раскладку на внутренней панели: контейнерный запрос не применяется к самому контейнеру
- подписи на кнопках в узкой ширине — иконка без слова угадывается неверно
- одно главное действие: две одинаково заметные кнопки заставляют выбирать
- обрезку текущего уровня многоточием, чтобы кнопки не уезжали
- aria-label на nav — панель содержит не только крошки

## 6. Можно менять
- массив items
- actionLabel и secondaryLabel
- акцент через проп accent — цвет главной кнопки
- порог переноса в контейнерном запросе

## 7. Правила
- Кнопки ничего не делают: обработчики навешивает вызывающий код.
- Больше двух действий в строку не ставьте: остальные — в меню.
- Опасные действия (удалить, отозвать) в этой панели неуместны: их место — рядом с подтверждением.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.

## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.

Для разработчика

npx shadcn@latest add https://vibeui.ru/r/breadcrumb-007.json
https://vibeui.ru/r/breadcrumb-007.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-breadcrumb-007-*. Серверный: хуков нет. Корень объявлен контейнером, а раскладка живёт на внутренней панели: контейнерный запрос действует на потомков, но не на сам контейнер. От 26rem собственной ширины кнопки растягиваются на всю строку под путём. Текущий уровень режется многоточием.

Исходник компонента

Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Breadcrumb007Item = {
  label: string
  href?: string
}

export type Breadcrumb007Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children"
> & {
  items?: Breadcrumb007Item[]
  actionLabel?: string
  secondaryLabel?: string
  accent?: string
}

// Идея компонента: строка пути с действиями справа. Путь и кнопки живут в
// одной строке, потому что обе отвечают на вопрос «что я могу сделать здесь»;
// на узкой ширине строка переносится, и кнопки уходят под путь, а не
// сжимаются до нечитаемых иконок.
const STYLES = `
:where([data-vibeui-block="breadcrumb-007"]){
--vibeui-breadcrumb-007-fg:oklch(0.26 0.016 265);
--vibeui-breadcrumb-007-muted:oklch(0.56 0.014 265);
--vibeui-breadcrumb-007-border:oklch(0.9 0.006 265);
--vibeui-breadcrumb-007-bg:oklch(1 0 0);
--vibeui-breadcrumb-007-accent:oklch(0.55 0.17 265);
--vibeui-breadcrumb-007-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
container-type:inline-size;
}
[data-vibeui-block="breadcrumb-007"]{
display:block;width:100%;box-sizing:border-box;
font-family:var(--vibeui-breadcrumb-007-font);color:var(--vibeui-breadcrumb-007-fg);
}
[data-vibeui-block="breadcrumb-007"] [data-part="bar"]{
display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem;
box-sizing:border-box;padding:0.625rem 0.75rem;
border:1px solid var(--vibeui-breadcrumb-007-border);border-radius:0.75rem;
background:var(--vibeui-breadcrumb-007-bg);
}
[data-vibeui-block="breadcrumb-007"] ol{
display:flex;flex-wrap:wrap;align-items:center;gap:0.375rem;flex:1;min-width:0;
margin:0;padding:0;list-style:none;
font-size:0.8125rem;line-height:1.4;color:var(--vibeui-breadcrumb-007-muted);
}
[data-vibeui-block="breadcrumb-007"] li{display:inline-flex;align-items:center;gap:0.375rem;min-width:0}
[data-vibeui-block="breadcrumb-007"] li + li::before{content:"/";color:oklch(0.78 0.01 265)}
[data-vibeui-block="breadcrumb-007"] a{color:inherit;text-decoration:none;border-radius:0.25rem}
[data-vibeui-block="breadcrumb-007"] a:hover{color:var(--vibeui-breadcrumb-007-fg);text-decoration:underline;text-underline-offset:3px}
[data-vibeui-block="breadcrumb-007"] a:focus-visible{outline:2px solid var(--vibeui-breadcrumb-007-accent);outline-offset:2px}
[data-vibeui-block="breadcrumb-007"] [aria-current="page"]{
color:var(--vibeui-breadcrumb-007-fg);font-weight:600;
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
[data-vibeui-block="breadcrumb-007"] [data-part="actions"]{display:flex;align-items:center;gap:0.375rem;flex:none}
[data-vibeui-block="breadcrumb-007"] button{
appearance:none;cursor:pointer;
display:inline-flex;align-items:center;height:2rem;padding:0 0.75rem;
border:1px solid var(--vibeui-breadcrumb-007-border);border-radius:0.5rem;
background:transparent;color:inherit;font:inherit;font-size:0.8125rem;font-weight:500;
}
[data-vibeui-block="breadcrumb-007"] button[data-primary="true"]{
border-color:transparent;background:var(--vibeui-breadcrumb-007-accent);
color:oklch(0.99 0.01 265);font-weight:600;
}
[data-vibeui-block="breadcrumb-007"] button:focus-visible{outline:2px solid var(--vibeui-breadcrumb-007-accent);outline-offset:2px}
/* На узкой ширине кнопки уходят под путь и остаются подписанными словами. */
@container (max-width: 26rem){
[data-vibeui-block="breadcrumb-007"] [data-part="actions"]{width:100%}
[data-vibeui-block="breadcrumb-007"] [data-part="actions"] button{flex:1;justify-content:center}
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="breadcrumb-007"] *{animation:none!important;transition:none!important}}
`

const DEFAULT_ITEMS: Breadcrumb007Item[] = [
  { label: "Проекты", href: "#" },
  { label: "VibeUI", href: "#" },
  { label: "Компонент breadcrumb-007" },
]

/**
 * Строка пути с действиями справа: на узкой ширине кнопки уходят вниз.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Breadcrumb007({
  items = DEFAULT_ITEMS,
  actionLabel = "Опубликовать",
  secondaryLabel = "Настройки",
  accent,
  className,
  style,
  ...props
}: Breadcrumb007Props) {
  const palette = {
    ...(accent ? { "--vibeui-breadcrumb-007-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-breadcrumb-007" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="breadcrumb-007"
        className={className}
        style={palette}
      >
        <div data-part="bar">
          <nav aria-label="Хлебные крошки">
            <ol>
              {items.map((item, index) => {
                const last = index === items.length - 1

                return (
                  <li key={item.label}>
                    {item.href && !last ? (
                      <a href={item.href}>{item.label}</a>
                    ) : (
                      <span aria-current={last ? "page" : undefined}>
                        {item.label}
                      </span>
                    )}
                  </li>
                )
              })}
            </ol>
          </nav>
          <div data-part="actions">
            {secondaryLabel ? (
              <button type="button">{secondaryLabel}</button>
            ) : null}
            {actionLabel ? (
              <button type="button" data-primary="true">
                {actionLabel}
              </button>
            ) : null}
          </div>
        </div>
      </div>
    </>
  )
}