Feedback

Wrapped Tooltip

A tooltip for a long explanation: a capped width, left-aligned text and word wrapping — a 400-pixel single line is unreadable.

  • tooltip
  • long text
  • wrapping
  • help

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/tooltip-005?lang=en

Остаток — это оплаченный объём минус израсходованный за текущий период. Перерасход не блокирует работу: он переносится в следующий счёт отдельной строкой.
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 "tooltip-005" (Wrapped Tooltip) 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/tooltip-005.json

Registry item: https://vibeui.ru/r/tooltip-005.json
Installs to: components/vibeui/tooltip-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
A tooltip for a long explanation: a capped width, left-aligned text and word wrapping — a 400-pixel single line is unreadable.

A tooltip with long copy: capped width, left alignment and careful wrapping. Zero dependencies, one file, no client JS.

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

<Tooltip005
  label="How the balance is counted"
  tip="The balance is the paid volume minus what has been used."
  tipWidth="17rem"
/>

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-tooltip-005-* palette — do not swap it for your theme tokens
- the width cap: a line longer than sixty characters stops being readable
- the left alignment — a centred three-line paragraph looks ragged
- the second cap against the viewport width: near the edge the tooltip must shrink
- opening on :focus-within — a long explanation is also needed by keyboard users
- the trigger's own light surface: without it the dark text disappears on a dark background

## 6. You may change
- the trigger caption through label and the copy through tip
- the maximum width through tipWidth
- the side it opens on by swapping bottom for top in the tooltip rule
- the tooltip colour through the --vibeui-tooltip-005-bg variable

## 7. Rules
- The tooltip does not capture the pointer: text inside cannot be selected, that needs a popover.
- More than four lines is a sign the explanation belongs in help, not in a tooltip.
- Breaking anywhere saves long URLs but splits words: do not put links in there.
- 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/tooltip-005.json
https://vibeui.ru/r/tooltip-005.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-tooltip-005-*. Серверный: открытие держится на :hover и :focus-within. Предельная ширина живёт в переменной --vibeui-tooltip-005-width и дополнительно ограничена шириной окна, поэтому у края экрана подсказка сжимается, а не уезжает. Перенос собран из white-space:normal, overflow-wrap:anywhere и text-wrap:pretty — последняя строка не остаётся из одного слова. Триггер несёт собственную светлую подложку.

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 Tooltip005Props = Omit<
  ComponentPropsWithoutRef<"span">,
  "children"
> & {
  label?: string
  /** Длинный текст: подсказка рассчитана на два-четыре предложения. */
  tip?: string
  /** Предельная ширина подсказки. Строка длиннее 60 знаков не читается. */
  tipWidth?: string
}

// Идея компонента: подсказка для длинного объяснения. Ширина ограничена, текст
// выключен по левому краю и переносится по словам с балансировкой строк —
// однострочная подсказка на 400 пикселей нечитаема, а обрезать текст нельзя.
const STYLES = `
:where([data-vibeui-block="tooltip-005"]){
--vibeui-tooltip-005-bg:oklch(0.25 0.014 265);
--vibeui-tooltip-005-fg:oklch(0.96 0.002 265);
--vibeui-tooltip-005-accent:oklch(0.6 0.16 265);
--vibeui-tooltip-005-width:17rem;
--vibeui-tooltip-005-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="tooltip-005"]{
position:relative;display:inline-flex;font-family:var(--vibeui-tooltip-005-font);
}
/* Своя светлая подложка: на тёмной витрине тёмный текст иначе пропадает. */
[data-vibeui-block="tooltip-005"] [data-part="trigger"]{
appearance:none;cursor:help;
display:inline-flex;align-items:center;gap:0.4375rem;
height:2.25rem;padding:0 0.75rem;border-radius:0.625rem;
border:1px solid oklch(0.9 0.006 265);
background:oklch(1 0 0);color:oklch(0.3 0.014 265);
font:inherit;font-size:0.875rem;font-weight:600;
}
[data-vibeui-block="tooltip-005"] [data-part="trigger"]::after{
content:"?";display:inline-flex;align-items:center;justify-content:center;
width:1.0625rem;height:1.0625rem;border-radius:9999px;
background:oklch(0.9 0.008 265);color:oklch(0.42 0.014 265);
font-size:0.6875rem;font-weight:800;
}
[data-vibeui-block="tooltip-005"] [data-part="trigger"]:focus-visible{outline:2px solid var(--vibeui-tooltip-005-accent);outline-offset:2px}
/* Ширина ограничена переменной, текст переносится по словам и балансируется. */
[data-vibeui-block="tooltip-005"] [data-part="tip"]{
position:absolute;bottom:calc(100% + 0.5625rem);left:50%;z-index:20;
width:var(--vibeui-tooltip-005-width);max-width:calc(100vw - 2rem);
box-sizing:border-box;padding:0.5625rem 0.6875rem;border-radius:0.625rem;
background:var(--vibeui-tooltip-005-bg);color:var(--vibeui-tooltip-005-fg);
font-size:0.75rem;line-height:1.5;text-align:left;
white-space:normal;overflow-wrap:anywhere;text-wrap:pretty;hyphens:auto;
box-shadow:0 18px 38px -26px oklch(0.15 0.02 265 / 70%);
pointer-events:none;opacity:0;
transform:translate(-50%,0.25rem);
transition:opacity .14s ease,transform .14s ease;
}
[data-vibeui-block="tooltip-005"] [data-part="tip"]::after{
content:"";position:absolute;left:50%;bottom:-0.1875rem;
width:0.5rem;height:0.5rem;margin-left:-0.25rem;
background:var(--vibeui-tooltip-005-bg);transform:rotate(45deg);
}
[data-vibeui-block="tooltip-005"]:hover [data-part="tip"],
[data-vibeui-block="tooltip-005"]:focus-within [data-part="tip"]{opacity:1;transform:translate(-50%,0)}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="tooltip-005"] *{animation:none!important;transition:none!important}}
`

/**
 * Подсказка с длинным текстом: ограниченная ширина и перенос по словам.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Tooltip005({
  label = "Как считается остаток",
  tip = "Остаток — это оплаченный объём минус израсходованный за текущий период. Перерасход не блокирует работу: он переносится в следующий счёт отдельной строкой.",
  tipWidth = "17rem",
  className,
  style,
  ...props
}: Tooltip005Props) {
  const palette = {
    "--vibeui-tooltip-005-width": tipWidth,
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-tooltip-005" precedence="medium">
        {STYLES}
      </style>
      <span
        {...props}
        data-vibeui-block="tooltip-005"
        className={className}
        style={palette}
      >
        <button
          data-part="trigger"
          type="button"
          aria-describedby="vibeui-tooltip-005-tip"
        >
          {label}
        </button>
        <span data-part="tip" role="tooltip" id="vibeui-tooltip-005-tip">
          {tip}
        </span>
      </span>
    </>
  )
}