Display
Pressed Tile Button
Плитка-кнопка с честным нажатым состоянием: включённость объявлена через aria-pressed и показана утопленной тенью, а не одним цветом.
- icontile
- toggle
- button
- pressed
Превью
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 3. Агент откроет ссылку и поставит компонент из registry.
размести это в шапке: https://vibeui.ru/c/icontile-006
Показать полную инструкцию
То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.
# Установка и размещение «icontile-006» (Pressed Tile Button) из VibeUI
## 1. Сначала установка — не пропускай, не пересоздавай
Выполни эту команду до того, как писать код:
npx shadcn@latest add https://vibeui.ru/r/icontile-006.json
Registry item: https://vibeui.ru/r/icontile-006.json
Ставится в: components/vibeui/icontile-006.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.
Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.
## 2. Что это
Плитка-кнопка с честным нажатым состоянием: включённость объявлена через aria-pressed и показана утопленной тенью, а не одним цветом.
Плитка-переключатель: aria-pressed для состояния, утопленная тень для видимости нажатия. Ноль зависимостей, один файл, состояние в useState.
## 3. Как использовать
import { Icontile006 } from "@/components/vibeui/icontile-006"
<Icontile006 glyph="★" label="В избранное" defaultPressed />
Полный список пропсов — в установленном файле.
## 4. Куда поставить
Это мелкий inline-компонент. Поставь его ровно туда, куда просил
пользователь, внутрь существующей разметки. Не создавай ради него
новую страницу, секцию или обёртку. Если на этом месте уже стоит похожий
контрол — замени его, а не добавляй второй.
Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)
## 5. Сохранить как установлено
- локальную палитру --vibeui-icontile-006-* и не подменять её токенами вашей темы
- aria-pressed вместо класса: без него скринридер не скажет, включено состояние или нет
- два признака нажатия сразу — цвет и утопленную тень: цвет один не работает в чёрно-белом режиме
- aria-label на кнопке: плитка со знаком без имени бесполезна для скринридера
- видимую обводку фокуса — плитку часто ставят в ряд и переключают с клавиатуры
- светлую заливку в покое: тёмная кнопка на тёмном фоне каталога пропадает
## 6. Можно менять
- знак кнопки через glyph и её имя через label
- начальное состояние через defaultPressed
- оттенок нажатого состояния через переменную --vibeui-icontile-006-hue
- размер через переменную --vibeui-icontile-006-size
## 7. Правила
- Состояние внутреннее: для управляемого варианта поднимите его в родителя и уберите useState.
- Кнопка-переключатель не заменяет чекбокс в форме — она ничего не отправляет.
- Сдвиг на пиксель при нажатии отключается в prefers-reduced-motion вместе с переходом.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.
## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.Для разработчика
npx shadcn@latest add https://vibeui.ru/r/icontile-006.jsonhttps://vibeui.ru/r/icontile-006.jsonКлиентский компонент: держит состояние в useState, поэтому в файле есть «use client». Состояние объявлено атрибутом aria-pressed, а не классом — без него скринридер сообщит просто «кнопка» и не скажет, включено ли. Визуально нажатие показано двумя признаками сразу: заливка меняется и плитка утоплена inset-тенью со сдвигом на пиксель, поэтому включённость видна и в чёрно-белом режиме. Доступное имя берётся из label, знак помечен aria-hidden. Заливка в покое светлая, так что кнопка читается на тёмной подложке каталога.
Исходник компонента
Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.
"use client"
import { useState } from "react"
import type { ComponentPropsWithoutRef, CSSProperties } from "react"
export type Icontile006Props = Omit<
ComponentPropsWithoutRef<"button">,
"children"
> & {
glyph?: string
label?: string
defaultPressed?: boolean
}
// Идея компонента: плитка-переключатель с честным нажатым состоянием.
// Состояние объявлено через aria-pressed, а не подкрашено классом: без него
// скринридер сообщит просто «кнопка» и не скажет, включено ли. Нажатие видно
// не одним цветом — плитка утоплена внутренней тенью и сдвинута на пиксель,
// поэтому включённость читается и в чёрно-белом режиме.
const STYLES = `
:where([data-vibeui-block="icontile-006"]){
--vibeui-icontile-006-size:2.75rem;
--vibeui-icontile-006-hue:262;
--vibeui-icontile-006-idle:oklch(0.96 0.004 265);
--vibeui-icontile-006-fg:oklch(0.36 0.014 265);
--vibeui-icontile-006-border:oklch(0.89 0.006 265);
--vibeui-icontile-006-ring:oklch(0.55 0.17 262 / 60%);
--vibeui-icontile-006-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="icontile-006"]{
appearance:none;cursor:pointer;
display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
width:var(--vibeui-icontile-006-size);height:var(--vibeui-icontile-006-size);
padding:0;border-radius:0.875rem;
background:var(--vibeui-icontile-006-idle);
border:1px solid var(--vibeui-icontile-006-border);
color:var(--vibeui-icontile-006-fg);
font-family:var(--vibeui-icontile-006-font);
font-size:calc(var(--vibeui-icontile-006-size) * 0.44);line-height:1;
transition:background-color .16s ease,color .16s ease,transform .1s ease,box-shadow .16s ease;
}
[data-vibeui-block="icontile-006"]:hover{background:oklch(0.93 0.01 var(--vibeui-icontile-006-hue))}
[data-vibeui-block="icontile-006"]:focus-visible{outline:2px solid var(--vibeui-icontile-006-ring);outline-offset:2px}
/* Нажатое состояние — не только цвет: утопленная тень видна и без цвета. */
[data-vibeui-block="icontile-006"][aria-pressed="true"]{
background:oklch(0.55 0.17 var(--vibeui-icontile-006-hue));
border-color:oklch(0.47 0.17 var(--vibeui-icontile-006-hue));
color:oklch(0.99 0 0);
box-shadow:inset 0 2px 4px oklch(0.25 0.1 var(--vibeui-icontile-006-hue) / 45%);
transform:translateY(1px);
}
[data-vibeui-block="icontile-006"]:active{transform:translateY(1px)}
[data-vibeui-block="icontile-006"]:disabled{cursor:not-allowed;opacity:.5}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="icontile-006"]{animation:none!important;transition:none!important}}
`
/**
* Плитка-кнопка с нажатым состоянием на aria-pressed.
* Один файл, ноль зависимостей, собственная палитра.
*/
export function Icontile006({
glyph = "★",
label = "В избранное",
defaultPressed = false,
type = "button",
onClick,
className,
style,
...props
}: Icontile006Props) {
const [pressed, setPressed] = useState(defaultPressed)
return (
<>
<style href="vibeui-icontile-006" precedence="medium">
{STYLES}
</style>
<button
{...props}
type={type}
data-vibeui-block="icontile-006"
aria-pressed={pressed}
aria-label={label}
onClick={(event) => {
setPressed((value) => !value)
onClick?.(event)
}}
className={className}
style={style as CSSProperties}
>
<span aria-hidden="true">{glyph}</span>
</button>
</>
)
}