Feedback

Announcement Banner

A full-width announcement bar: tag, message, link and dismiss. Dark and calm — it does not pretend to be an error.

  • banner
  • announcement
  • top bar
  • marketing

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/banner-001?lang=en

NewThe catalog now has form components — fields, selects and switches.Take a look
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 "banner-001" (Announcement Banner) 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/banner-001.json

Registry item: https://vibeui.ru/r/banner-001.json
Installs to: components/vibeui/banner-001.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
A full-width announcement bar: tag, message, link and dismiss. Dark and calm — it does not pretend to be an error.

A dark full-width announcement bar: a tag such as "New", a message, a link action and a close button. The glow on the left is a gradient. In a narrow column the message moves to its own line instead of collapsing word by word. Zero dependencies, one file, its own palette.

## 3. How to use it
import { Banner001 } from "@/components/vibeui/banner-001"

<Banner001
  tag="New"
  message="The catalog now has form components."
  actionLabel="Take a look"
  actionHref="/components"
  onDismiss={hideBanner}
/>

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-banner-001-* palette — the bar is deliberately dark and does not follow the page theme
- the calm tone: an announcement must not look like an error, errors have the alert component
- the @container query that moves the message to its own line: without it a narrow screen squeezes the words into a column
- showing the close button only when onDismiss is provided — a cross that does nothing is worse than no cross
- role="region" with a label: the bar has to be findable and skippable from the keyboard
- underlining the link on hover through border-bottom rather than text-decoration: that keeps the line from jumping
- the <style> block inside the component — it holds the palette, the gradient and the layout

## 6. You may change
- tag — the short label on the left: "New", "Beta", a date
- message and children — the announcement copy
- actionLabel and actionHref — the action's label and destination
- onDismiss — the close handler; without it there is no close button
- the accent through the accent prop — it colours the tag, the link and the glow
- placement through className: sticking it to the top is the caller's job

## 7. Rules
- The bar does not remember being dismissed. The "already seen" flag belongs to the calling code, otherwise the announcement returns on the next page.
- Do not keep two bars on screen at once: the second one stops being noticed.
- Do not put an important system status message here — people dismiss the bar without reading. Use an inline alert.
- 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/banner-001.json
https://vibeui.ru/r/banner-001.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-banner-001-*. Подсветка слева — радиальный градиент от акцента, а не отдельный элемент. Кнопка закрытия появляется только когда передан onDismiss: полосе, которую нечем закрыть, крестик не нужен. На узкой ширине текст уходит на отдельную строку через @container, а метка и действие остаются в одну линию.

Component source

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

import type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from "react"

export type Banner001Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children"
> & {
  message?: string
  /** Короткая метка слева: «Новое», «Бета», дата. */
  tag?: string
  actionLabel?: string
  actionHref?: string
  /** Кнопка закрытия. Без обработчика полосу закрывать нечем. */
  onDismiss?: () => void
  children?: ReactNode
  accent?: string
}

// Идея компонента: объявление на всю ширину, которое не притворяется
// уведомлением об ошибке. Полоса тёмная и спокойная, действие — обычная
// ссылка, а на узкой ширине она уходит на вторую строку, а не сжимает текст.
const STYLES = `
:where([data-vibeui-block="banner-001"]){
--vibeui-banner-001-fg:oklch(0.96 0.003 265);
--vibeui-banner-001-muted:oklch(0.78 0.01 265);
--vibeui-banner-001-bg:oklch(0.24 0.016 265);
--vibeui-banner-001-accent:oklch(0.72 0.15 200);
--vibeui-banner-001-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
container-type:inline-size;
}
[data-vibeui-block="banner-001"]{
/* flex-wrap живёт здесь, а не в @container: контейнерный запрос применяется
   к потомкам контейнера, но не к нему самому. На широкой раскладке перенос
   ни на что не влияет — всё умещается в строку. */
display:flex;flex-wrap:wrap;align-items:center;gap:0.375rem 0.75rem;
width:100%;box-sizing:border-box;
padding:0.625rem 0.875rem;
background:
radial-gradient(120% 180% at 0% 50%,color-mix(in oklab,var(--vibeui-banner-001-accent) 22%,transparent),transparent 55%),
var(--vibeui-banner-001-bg);
color:var(--vibeui-banner-001-fg);
font-family:var(--vibeui-banner-001-font);font-size:0.8125rem;line-height:1.4;
}
[data-vibeui-block="banner-001"] [data-part="tag"]{
flex:none;padding:0.1875rem 0.4375rem;border-radius:0.3125rem;
background:color-mix(in oklab,var(--vibeui-banner-001-accent) 25%,transparent);
color:var(--vibeui-banner-001-accent);
font-size:0.6875rem;font-weight:600;letter-spacing:0.02em;
}
[data-vibeui-block="banner-001"] [data-part="message"]{flex:1 1 auto;min-width:0}
[data-vibeui-block="banner-001"] [data-part="action"]{
flex:none;color:var(--vibeui-banner-001-accent);font-weight:600;
text-decoration:none;border-bottom:1px solid transparent;
transition:border-color .16s ease;
}
[data-vibeui-block="banner-001"] [data-part="action"]:hover{border-bottom-color:currentColor}
[data-vibeui-block="banner-001"] [data-part="action"]:focus-visible{outline:2px solid var(--vibeui-banner-001-accent);outline-offset:3px}
[data-vibeui-block="banner-001"] [data-part="close"]{
appearance:none;border:0;background:transparent;cursor:pointer;flex:none;
display:flex;align-items:center;justify-content:center;
width:1.5rem;height:1.5rem;border-radius:0.375rem;
color:var(--vibeui-banner-001-muted);font:inherit;font-size:1rem;line-height:1;
transition:background-color .16s ease,color .16s ease;
}
[data-vibeui-block="banner-001"] [data-part="close"]:hover{background:oklch(1 0 0 / 10%);color:var(--vibeui-banner-001-fg)}
[data-vibeui-block="banner-001"] [data-part="close"]:focus-visible{outline:2px solid var(--vibeui-banner-001-accent);outline-offset:2px}
/* Узкая полоса: действие переносится под текст, а не сжимает его в столбик. */
@container (max-width: 30rem){
[data-vibeui-block="banner-001"] [data-part="message"]{flex:1 1 100%;order:2}
[data-vibeui-block="banner-001"] [data-part="action"]{order:3}
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="banner-001"] *{animation:none!important;transition:none!important}}
`

/**
 * Полоса-объявление во всю ширину: метка, текст, действие, закрытие.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Banner001({
  message = "Каталог пополнился компонентами форм — поля, списки и переключатели.",
  tag = "Новое",
  actionLabel = "Посмотреть",
  actionHref = "#",
  onDismiss,
  children,
  accent,
  className,
  style,
  ...props
}: Banner001Props) {
  const palette = {
    ...(accent ? { "--vibeui-banner-001-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-banner-001" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="banner-001"
        role="region"
        aria-label={tag || "Объявление"}
        className={className}
        style={palette}
      >
        {tag ? <span data-part="tag">{tag}</span> : null}
        <span data-part="message">{children ?? message}</span>
        {actionLabel ? (
          <a data-part="action" href={actionHref}>
            {actionLabel}
          </a>
        ) : null}
        {onDismiss ? (
          <button
            data-part="close"
            type="button"
            onClick={onDismiss}
            aria-label="Скрыть объявление"
          >
            ×
          </button>
        ) : null}
      </div>
    </>
  )
}