Badge

Marker Dot

A marker dot over someone else's element: the wrapper places it in the corner, its ring cuts it out of the background, and the meaning goes in text.

  • badge
  • dot
  • notification
  • wrapper

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/badge-004?lang=en

unread notifications
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 "badge-004" (Marker Dot) 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/badge-004.json

Registry item: https://vibeui.ru/r/badge-004.json
Installs to: components/vibeui/badge-004.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 marker dot over someone else's element: the wrapper places it in the corner, its ring cuts it out of the background, and the meaning goes in text.

A wrapper that puts a marker dot in the corner of another element: a ring in the surface colour separates it from the icon and the meaning goes to screen readers as text. Zero dependencies, one file, no client JS.

## 3. How to use it
import { Badge004 } from "@/components/vibeui/badge-004"

<Badge004 label="unread messages">
  <button type="button">Notifications</button>
</Badge004>

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-badge-004-* palette — do not swap it for your theme tokens
- the ring in the surface colour: on a busy icon the dot is otherwise lost
- the hidden text with role="status": a dot is not read aloud
- the wrapper instead of a standalone badge — the marker must follow its element
- --vibeui-badge-004-cut matching the background behind the element
- the corner as an attribute rather than separate components

## 6. You may change
- the children: a button, icon or avatar
- label — what happened
- tone — accent, danger or success
- placement — top-right or top-left

## 7. Rules
- A dot says "there is something", not how much: for a number use badge-003.
- Do not mark an element with no action: people look for somewhere to click.
- The demo bell renders only when no children are passed — pass your own element in a project.
- 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/badge-004.json
https://vibeui.ru/r/badge-004.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-badge-004-*. Серверный: хуков нет. Это обёртка: внутрь кладут кнопку или иконку, точка позиционируется абсолютно в угол и вырезает под собой кружок через box-shadow цветом подложки. Смысл маркера уходит скрытой строкой с role="status". Без детей рисуется демонстрационная кнопка с колокольчиком.

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 Badge004Props = Omit<
  ComponentPropsWithoutRef<"span">,
  "children"
> & {
  children?: ReactNode
  /** Что именно случилось: текст уходит скринридеру, точка — глазу. */
  label?: string
  show?: boolean
  tone?: "accent" | "danger" | "success"
  placement?: "top-right" | "top-left"
}

// Идея компонента: точка-маркер поверх чужого элемента. Она обёртка, а не
// плашка: внутрь кладут кнопку или иконку, и маркер садится в угол. Обводка
// цветом подложки вырезает под точкой кружок — на любой иконке она остаётся
// отделённой. Смысл маркера идёт текстом: точка вслух не читается.
const STYLES = `
:where([data-vibeui-block="badge-004"]){
--vibeui-badge-004-dot:oklch(0.58 0.2 25);
--vibeui-badge-004-cut:oklch(1 0 0);
--vibeui-badge-004-size:0.5rem;
}
[data-vibeui-block="badge-004"]{position:relative;display:inline-flex;vertical-align:middle}
[data-vibeui-block="badge-004"][data-tone="accent"]{--vibeui-badge-004-dot:oklch(0.58 0.16 265)}
[data-vibeui-block="badge-004"][data-tone="success"]{--vibeui-badge-004-dot:oklch(0.63 0.17 152)}
/* Обводка цветом подложки: точка остаётся отделённой на любой иконке. */
[data-vibeui-block="badge-004"] [data-part="dot"]{
position:absolute;
width:var(--vibeui-badge-004-size);height:var(--vibeui-badge-004-size);
border-radius:9999px;background:var(--vibeui-badge-004-dot);
box-shadow:0 0 0 2px var(--vibeui-badge-004-cut);
}
[data-vibeui-block="badge-004"][data-placement="top-right"] [data-part="dot"]{right:-0.125rem;top:-0.125rem}
[data-vibeui-block="badge-004"][data-placement="top-left"] [data-part="dot"]{left:-0.125rem;top:-0.125rem}
[data-vibeui-block="badge-004"] [data-part="text"]{
position:absolute;width:1px;height:1px;overflow:hidden;
clip-path:inset(50%);white-space:nowrap;
}
/* Демонстрационная кнопка внутри обёртки: свою вы передаёте детьми. */
[data-vibeui-block="badge-004"] [data-part="sample"]{
display:inline-flex;align-items:center;justify-content:center;
width:2.5rem;height:2.5rem;border-radius:0.625rem;
border:1px solid oklch(0.9 0.006 265);background:oklch(0.985 0.002 265);
}
[data-vibeui-block="badge-004"] [data-part="bell"]{
position:relative;width:0.875rem;height:0.75rem;
border:1.5px solid oklch(0.42 0.014 265);border-radius:0.4375rem 0.4375rem 0.125rem 0.125rem;
border-bottom-width:0;
}
[data-vibeui-block="badge-004"] [data-part="bell"]::after{
content:"";position:absolute;left:-0.1875rem;right:-0.1875rem;bottom:-0.125rem;
height:1.5px;background:oklch(0.42 0.014 265);border-radius:9999px;
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="badge-004"] *{animation:none!important;transition:none!important}}
`

/**
 * Точка-маркер поверх чужого элемента: обёртка, а не плашка.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Badge004({
  children,
  label = "есть непрочитанные уведомления",
  show = true,
  tone = "danger",
  placement = "top-right",
  className,
  style,
  ...props
}: Badge004Props) {
  return (
    <>
      <style href="vibeui-badge-004" precedence="medium">
        {STYLES}
      </style>
      <span
        {...props}
        data-vibeui-block="badge-004"
        data-tone={tone}
        data-placement={placement}
        className={className}
        style={style as CSSProperties}
      >
        {children ?? (
          <span data-part="sample" aria-hidden="true">
            <span data-part="bell" />
          </span>
        )}
        {show ? (
          <>
            <span data-part="dot" aria-hidden="true" />
            <span data-part="text" role="status">
              {label}
            </span>
          </>
        ) : null}
      </span>
    </>
  )
}