GrBadge
Берут, когда статус нужен словом.
Обзор
Когда брать
- статус нужен словом — «Оплачено», «Черновик», «Просрочен»: тон несёт смысл, а не только цвет;
- метка стоит в потоке — в строке таблицы, в карточке, рядом с заголовком;
- категорий много — вся шкала тонов пакета без своих цветов;
- метка ничего не делает — компонент презентационный: не кликается и никуда не ведёт.
Когда взять другое
| Нужно | Берите |
|---|---|
| Счётчик поверх кнопки или иконки | GrBadgeWrap |
| Метка снимается или выбирается | GrChip; в поле ввода — GrInputTag |
| Нужно действие | GrButton |
| Число как показатель | GrStatistic |
| Клавиша или сочетание | GrKbd |
Варианты все тона и виды — настоящим компонентом
tone \ dark | false | true |
|---|---|---|
primary | ||
neutral | ||
success | ||
warning | ||
danger | ||
info | ||
slate | ||
azure |
Размеры
xssmmdlg
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrBadge>Текст</GrBadge>Примеры 5 у каждого — живое демо и его код
Статус одним словом, категория — ярлыком
Тон несёт приговор, слово — факт: «Payment failed» опасным тоном не нуждается во втором предложении в строке таблицы. Тариф — категория, а не приговор, поэтому у него
radius="semi": две колонки различает форма раньше, чем цвет.Customer Plan Status MRR Northwind Traders Enterprise Active €4,800 Contoso Ltd Team Trial — Fabrikam Inc Team Past due €960 Tailspin Toys Starter Payment failed €240 Wide World Importers Enterprise Renewing €3,600 Litware Starter Cancelled — Тихие плашки, цветные точки
Имя тона в
dotкрасит маркер отдельно от плашки. Десять цветных плашек в колонке соревнуются за внимание; нейтральные плашки с цветными точками читаются построчно, и единственная красная точка находится с первого взгляда.- APIeu-central-1 · 99.99 % uptimeOperational
- Webhookseu-central-1 · 99.41 % uptimeDegraded
- Searchus-east-1 · 97.80 % uptimePartial outage
- Exportseu-west-2 · 99.95 % uptimeMaintenance
- Dashboardglobal · 100 % uptimeOperational
Иконка говорит, что работа идёт
Слот
iconставит иконку перед подписью, а её обёртка берёт размер у ступени бейджа. «Processing» без спиннера читается как окончательное состояние, сanimate-spinна иконке — как работа в процессе.- invoices-2026-09.csv2.4 MBImported
- customers.xlsx880 KBProcessing
- contracts.zip41 MBNeeds review
- payroll.csv120 KBRejected
- suppliers.csv64 KBQueued
Заливка там, где фон пёстрый
На картинке мягкая плашка растворяется;
darkзаливает её весом, который держится на любой обложке. На заливке точка берёт цвет текста — маркер «Live» виден и на красном, — а полускруглённый бейджxsстановится штампом длительности.Live1:12:40Design tokens at scale
Ada Lin · 3 Oct
New48:12Accessible data tables
Grace Wu · 26 Sep
Updated36:05Theming without forks
Linus Ray · 12 Sep
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
tone |
| undefined | Тон метки: несёт смысл, а не только цвет — «Оплачено» в `success`, «Просрочено» в `danger`. Не задан — из `GrConfigProvider`, иначе `neutral`. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Ступень контрольной шкалы `--gr-control-text-*`: бейдж чаще стоит рядом с кнопками и полями, чем внутри абзаца. Не задан — из `GrConfigProvider`. |
dark | boolean | undefined | false | Заливка вместо мягкой подложки: плотный вес для пёстрого фона и для метки, которая обязана выделяться. Цвета — из слоя `--gr-badge-{tone}-bg`/`-fg`. |
radius | GrBadgeRadius | undefined | undefined | Форма: `round` — таблетка статуса, `semi` — ярлык категории или кода, `square` — штамп. Не задан — из `GrConfigProvider`, иначе `round`. |
dot | boolean | GrBadgeDotTone | undefined | false | Точка-маркер перед подписью: «● Активен». Имя тона красит маркер отдельно от плашки — ради пары «тихая плашка + цветной маркер», из-за которой паттерн и существует. `true` берёт тон самого бейджа. На заливке (`dark`) маркер всегда цвета текста: тон там слился бы с подложкой, взятой из того же слоя. |
Слоты
| Slot | Type | Описание |
|---|---|---|
default | any | Содержимое метки. |
icon | any | Иконка перед подписью: статус со спиннером, флаг, значок типа. Размер задаёт обёртка по ступени `size`, поэтому содержимое слота тянется до неё — `class="h-full w-full"`, как у `GrChip`. Вращение спиннера — `animate-spin` на самой иконке. `GrProgressCircle` сюда не кладут: у кругового индикатора шкала виджетная, нижняя ступень — `2rem`, и в строку бейджа он не помещается. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrBadgeConfigurableProps, GrBadgeTone, GrBadgeDotTone } from '@feugene/granularity/components/GrBadge'Файлdist/types/components/GrBadge/config.d.ts
const grBadgeConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrBadge">;Файлdist/types/components/GrBadge/defaults.d.ts
Пропы GrBadge, настраиваемые глобально через componentDefaults.
Только оформление — см. GrButton/defaults.ts.
interface GrBadgeConfigurableProps {
tone: GrBadgeTone;
size: GrBadgeSize;
radius: GrBadgeRadius;
}Файлdist/types/components/GrBadge/grBadgeStyles.d.ts
type GrBadgeTone = GrTone;Тон маркера — отдельным литеральным перечислением, а не GrTone.
Компилятор SFC выводит рантайм-типы пропов из TypeScript, но GrTone это
typeof GR_TONES[number], и развернуть его в конструкторы он не умеет. У
dot?: GrTone | boolean из объединения выживал один Boolean, и строковый
тон отбраковывался предупреждением Vue у каждого потребителя — при том, что
компонент работал. Литеральное объединение разрешается и даёт
type: [Boolean, String]; так же написан showTooltip у GrSlider.
От расхождения с GR_TONES держит assertDotToneCoversTones ниже: списки
обязаны совпадать в обе стороны, иначе vue-tsc роняет сборку.
type GrBadgeDotTone = 'primary' | 'neutral' | 'success' | 'warning' | 'danger' | 'info' | 'slate' | 'azure';type GrBadgeSize = GrComponentSize;type GrBadgeRadius = 'square' | 'semi' | 'round';Файлdist/types/components/GrBadge/safelist.d.ts
const grBadgeSafelist: string[];Файлdist/types/components/GrBadge/GrBadge.vue.d.ts
interface GrBadgeProps {
/**
* Тон метки: несёт смысл, а не только цвет — «Оплачено» в `success`,
* «Просрочено» в `danger`. Не задан — из `GrConfigProvider`, иначе `neutral`.
*/
tone?: GrBadgeTone;
/**
* Заливка вместо мягкой подложки: плотный вес для пёстрого фона и для
* метки, которая обязана выделяться. Цвета — из слоя `--gr-badge-{tone}-bg`/`-fg`.
*/
dark?: boolean;
/**
* Ступень контрольной шкалы `--gr-control-text-*`: бейдж чаще стоит рядом с
* кнопками и полями, чем внутри абзаца. Не задан — из `GrConfigProvider`.
*/
size?: GrBadgeSize;
/**
* Форма: `round` — таблетка статуса, `semi` — ярлык категории или кода,
* `square` — штамп. Не задан — из `GrConfigProvider`, иначе `round`.
*/
radius?: GrBadgeRadius;
/**
* Точка-маркер перед подписью: «● Активен».
*
* Имя тона красит маркер отдельно от плашки — ради пары «тихая плашка +
* цветной маркер», из-за которой паттерн и существует. `true` берёт тон
* самого бейджа.
*
* На заливке (`dark`) маркер всегда цвета текста: тон там слился бы с
* подложкой, взятой из того же слоя.
*/
dot?: boolean | GrBadgeDotTone;
}Использование из прозы библиотеки
Два веса: подложка и заливка
dark переключает мягкую тонированную подложку на плотную заливку.
Заливка идёт через покомпонентный слой --gr-badge-{tone}-bg/-fg, а не прямо
из роли тона, потому что нужный вес у тем разный. В светлой теме
--gr-{tone} — это яркая заливка под тёмный текст, и filled-бейдж читался
тяжёлой почти чёрной плашкой; там слой уводит его на -solid/-solid-fg —
заливку кнопочного веса под светлый текст, ту же, что у solid-кнопок. В тёмной
теме пастельная заливка с тёмным текстом — штатная конвенция пакета, и слой
оставляет прежние роли.
Значения слоя лежат в темах компонента (GrBadge/themes/{light,dark}.css),
поэтому перекрасить бейдж можно, не трогая роли тонов целиком.
Почему текст на подложке — всегда -text
Насыщенный тон как цвет текста на своей же светлой подложке даёт 2.24:1, роль
-text — 6.78:1. Поэтому мягкий вес красит текст только -text-ролями.
Обе гарантии закреплены гейтом grBadgeContrast.test.ts, и обе не формальные:
- контраст текста — порог AA 4.5:1. Послаблений для крупного текста здесь нет: кегль бейджа 11–14px;
- видимость самой плашки меряется перцептивным расстоянием до фона страницы
(ΔE, порог заметности 2.3). Контрастного правила для этого мало: 3:1 из WCAG
1.4.11 не проходит даже штатная пара
--gr-muted/--gr-brd, и бейдж мог оказаться неотличимым от страницы, формально не нарушив ничего.
Про роли тонов и суффиксы — ../theming.md.
Радиус
radius — round (по умолчанию), semi или square. У semi своя лестница
по ступеням размера — 3/3/5/7 px через --gr-badge-semi-radius-*: бейдж
скругляется медленнее, чем растёт, иначе на lg он превращается в пилюлю
раньше, чем этого хочет макет.
<GrBadge radius="semi" size="lg">
SKU-14920
</GrBadge>Текст центрируется оптически
Высоту бейджа держит внутренняя обёртка (min-height: 1lh — высота line-box
при leading-none), а сам текст обрезается до cap-height (text-box-trim) и
центрируется внутри этой высоты. Так подпись стоит ровно независимо от того,
есть ли в ней выносные элементы: «Оплачено» и «Ждём оплаты» садятся одинаково.
Обрезка живёт на вложенном узле, а не на самом тексте, и это не стилистика:
text-box-trim уменьшает line-box, и повешенный прямо на текст он просадил бы
вместе с ним высоту всего бейджа.
Точка-маркер: тихая плашка, цветной статус
<GrBadge tone="neutral" dot="success">
Активен
</GrBadge>
<GrBadge tone="success" dot>
Оплачено
</GrBadge>dot ставит кружок перед подписью. Имя тона красит маркер отдельно от
плашки — ради этой пары паттерн и нужен: в списке из десятка строк цветные
плашки соревнуются за внимание, а серая плашка с цветной точкой читается
построчно. Булево значение берёт тон самого бейджа.
Маркер декоративен (aria-hidden): смысл несёт слово рядом. Точки без подписи
здесь нет намеренно — она сообщала бы состояние одним цветом. Метка поверх
чужого контрола — это GrBadgeWrap, статус на аватаре —
GrAvatar.
Почему маркер красится слоем бейджа, а не ролью тона
Роль тона напрямую не работает ни в одном виде, и обе ветки измерены гейтом:
| Что пробовали | Светлая тема | Тёмная тема |
|---|---|---|
--gr-{tone} | 2.06–2.56 — не проходит ни на одной подложке | проходит |
--gr-{tone}-solid | проходит | до 1.37 — не проходит |
--gr-badge-{tone}-bg | проходит | проходит |
Причина в том, что --gr-{tone} в светлой теме — яркая заливка под тёмный
текст, а не цвет для мелкой графики. Покомпонентный слой --gr-badge-{tone}-bg
как раз и означает «насыщенный вес, который читается в этой теме», и на нём
проходят все 128 пар «тон маркера × тон бейджа × тема».
Порог для маркера — 3:1 (WCAG 1.4.11), а не 4.5:1: кружок опознаётся формой и положением, а не начертанием.
На заливке (dark) маркер всегда цвета текста. Подложка filled-бейджа
берётся из того же слоя, поэтому маркер названного тона слился бы с ней в один
цвет; названный тон там игнорируется с предупреждением в dev.
Маркер и слот icon занимают одно место, поэтому исключают друг друга:
переданы оба — рисуется иконка, она несёт больше, чем цвет.
Иконка перед подписью — слот icon
Статус часто состоит не только из слова: «идёт разбор» без спиннера читается как окончательное состояние, а не как работа.
<GrBadge tone="warning">
<template #icon>
<IconLoader class="h-full w-full animate-spin" />
</template>
На распознании
</GrBadge>Размер держит обёртка слота, а содержимое тянется до неё (h-full w-full) —
тот же приём, что у GrChip, и по той же причине: шкала иконки
обязана следовать за size бейджа, а не за разметкой места вызова. Растёт она
медленнее кегля: на нижних ступенях иконка «один в один с текстом» разогнала бы
узкую плашку.
Иконка — сосед обрезанного текста, а не его содержимое: text-box-trim
живёт на вложенном узле подписи и до иконки не дотягивается, поэтому высоту
плашки она не тянет.
Не кладите сюда GrProgressCircle: у кругового индикатора шкала виджетная,
нижняя ступень — 2rem, и в строку бейджа он не помещается — дуга уезжает за
подложку, вынося текст наружу. Спиннер здесь — обычная иконка с animate-spin.
Слот не делает бейдж интерактивным: он по-прежнему метка. Нужен снимаемый или
выбираемый тег — это GrChip, у него своя роль и своя
клавиатура.
Размер
size берёт кегль из контрольной шкалы --gr-control-text-*, а не из
контентной: бейдж чаще стоит в одном ряду с кнопками и полями, чем внутри
абзаца. Шкалы и их разница — ../sizes.md.
tone, size и radius читаются из GrConfigProvider, поэтому вид бейджей
задаётся один раз на приложение.
Пропы этого примера