Display

Icon Tile

Плитка под иконку: постоянный размер и место, а фон со знаком считаются от одного оттенка, а не подбираются вручную.

  • icon
  • tile
  • tone
  • list

Превью

1440pxHost theme

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

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

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

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

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

# Установка и размещение «icontile-001» (Icon Tile) из VibeUI

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

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

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

## 2. Что это
Плитка под иконку: постоянный размер и место, а фон со знаком считаются от одного оттенка, а не подбираются вручную.

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

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

<Icontile001 glyph="◆" tone="success" size="md" label="Готово" />

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

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

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

## 5. Сохранить как установлено
- локальную палитру --vibeui-icontile-001-* и не подменять её токенами вашей темы
- вывод фона и знака из общего оттенка: пара цветов не подбирается руками
- постоянный размер плитки — иначе список пунктов прыгает на разной ширине иконок
- скрытие от скринридера без подписи: декоративная плитка не должна читаться
- кегль знака в долях от размера плитки, а не фиксированным числом
- flex:none — плитка не должна сжиматься в узкой строке

## 6. Можно менять
- glyph — символ или своя иконка
- tone, size и shape
- label для смысловой плитки
- радиус скругления

## 7. Правила
- Смысл несёт подпись рядом: цветом одним состояние не передать.
- Символ по умолчанию — заглушка; ставьте свою иконку тем же размером.
- Пять тонов — предел: больше цветов перестают различаться в списке.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.

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

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

npx shadcn@latest add https://vibeui.ru/r/icontile-001.json
https://vibeui.ru/r/icontile-001.json

Компонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-icontile-001-*. Серверный: хуков нет. Тон задаётся парой переменных оттенка и насыщенности, а цвета фона и знака выводятся из них функцией oklch — поэтому новый тон добавляется одной строкой. Без подписи плитка скрыта от скринридера как декоративная.

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

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

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Icontile001Props = Omit<
  ComponentPropsWithoutRef<"span">,
  "children" | "color"
> & {
  glyph?: string
  label?: string
  tone?: "neutral" | "accent" | "success" | "warning" | "danger"
  size?: "sm" | "md" | "lg"
  shape?: "square" | "circle"
}

// Идея компонента: подложка под иконку. Иконка сама по себе теряется в тексте,
// а плитка задаёт ей постоянный размер и место, поэтому список пунктов не
// прыгает, когда иконки разной ширины. Тон задаётся одной парой переменных —
// фон и цвет знака считаются от общего оттенка, а не подбираются вручную.
// Плитка декоративна: она скрыта от скринридера, а смысл несёт подпись рядом.
const STYLES = `
:where([data-vibeui-block="icontile-001"]){
--vibeui-icontile-001-size:2.5rem;
--vibeui-icontile-001-hue:265;
--vibeui-icontile-001-chroma:0.02;
--vibeui-icontile-001-radius:0.75rem;
--vibeui-icontile-001-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* Фон и знак считаются от одного оттенка: пара цветов не подбирается руками. */
[data-vibeui-block="icontile-001"]{
display:inline-flex;align-items:center;justify-content:center;flex:none;
width:var(--vibeui-icontile-001-size);height:var(--vibeui-icontile-001-size);
border-radius:var(--vibeui-icontile-001-radius);
background:oklch(0.94 var(--vibeui-icontile-001-chroma) var(--vibeui-icontile-001-hue));
color:oklch(0.45 calc(var(--vibeui-icontile-001-chroma) * 4) var(--vibeui-icontile-001-hue));
font-family:var(--vibeui-icontile-001-font);
font-size:calc(var(--vibeui-icontile-001-size) * 0.44);
font-weight:650;line-height:1;
}
[data-vibeui-block="icontile-001"][data-shape="circle"]{--vibeui-icontile-001-radius:9999px}
[data-vibeui-block="icontile-001"][data-size="sm"]{--vibeui-icontile-001-size:2rem;--vibeui-icontile-001-radius:0.625rem}
[data-vibeui-block="icontile-001"][data-size="lg"]{--vibeui-icontile-001-size:3.25rem;--vibeui-icontile-001-radius:1rem}
[data-vibeui-block="icontile-001"][data-tone="accent"]{--vibeui-icontile-001-hue:262;--vibeui-icontile-001-chroma:0.05}
[data-vibeui-block="icontile-001"][data-tone="success"]{--vibeui-icontile-001-hue:152;--vibeui-icontile-001-chroma:0.045}
[data-vibeui-block="icontile-001"][data-tone="warning"]{--vibeui-icontile-001-hue:75;--vibeui-icontile-001-chroma:0.05}
[data-vibeui-block="icontile-001"][data-tone="danger"]{--vibeui-icontile-001-hue:25;--vibeui-icontile-001-chroma:0.05}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="icontile-001"]{transition:none!important}}
`

/**
 * Плитка под иконку: постоянный размер и тон из одного оттенка.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Icontile001({
  glyph = "◆",
  label,
  tone = "accent",
  size = "md",
  shape = "square",
  className,
  style,
  ...props
}: Icontile001Props) {
  return (
    <>
      <style href="vibeui-icontile-001" precedence="medium">
        {STYLES}
      </style>
      <span
        {...props}
        data-vibeui-block="icontile-001"
        data-tone={tone}
        data-size={size}
        data-shape={shape}
        role={label ? "img" : undefined}
        aria-label={label}
        aria-hidden={label ? undefined : true}
        className={className}
        style={style as CSSProperties}
      >
        {glyph}
      </span>
    </>
  )
}