GrBadge

  • ядро
  • Обратная связь
  • API · 5 props

Берут, когда статус нужен словом.

import { GrBadge } from '@feugene/granularity/components/GrBadge'

Обзор

Когда брать

  • статус нужен словом — «Оплачено», «Черновик», «Просрочен»: тон несёт смысл, а не только цвет;
  • метка стоит в потоке — в строке таблицы, в карточке, рядом с заголовком;
  • категорий много — вся шкала тонов пакета без своих цветов;
  • метка ничего не делает — компонент презентационный: не кликается и никуда не ведёт.

Когда взять другое

НужноБерите
Счётчик поверх кнопки или иконкиGrBadgeWrap
Метка снимается или выбираетсяGrChip; в поле ввода — GrInputTag
Нужно действиеGrButton
Число как показательGrStatistic
Клавиша или сочетаниеGrKbd

Варианты все тона и виды — настоящим компонентом

Варианты · GrBadge
tone \ darkfalsetrue
primary
neutral
success
warning
danger
info
slate
azure

Размеры

  • xs
  • sm
  • md
  • lg

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 5 у каждого — живое демо и его код

  • Статус одним словом, категория — ярлыком

    Тон несёт приговор, слово — факт: «Payment failed» опасным тоном не нуждается во втором предложении в строке таблицы. Тариф — категория, а не приговор, поэтому у него radius="semi": две колонки различает форма раньше, чем цвет.

    CustomerPlanStatusMRR
    Northwind TradersEnterpriseActive€4,800
    Contoso LtdTeamTrial—
    Fabrikam IncTeamPast due€960
    Tailspin ToysStarterPayment failed€240
    Wide World ImportersEnterpriseRenewing€3,600
    LitwareStarterCancelled—
  • Тихие плашки, цветные точки

    Имя тона в dot красит маркер отдельно от плашки. Десять цветных плашек в колонке соревнуются за внимание; нейтральные плашки с цветными точками читаются построчно, и единственная красная точка находится с первого взгляда.

    • APIeu-central-1 · 99.99 % uptime
      Operational
    • Webhookseu-central-1 · 99.41 % uptime
      Degraded
    • Searchus-east-1 · 97.80 % uptime
      Partial outage
    • Exportseu-west-2 · 99.95 % uptime
      Maintenance
    • Dashboardglobal · 100 % uptime
      Operational
  • Иконка говорит, что работа идёт

    Слот icon ставит иконку перед подписью, а её обёртка берёт размер у ступени бейджа. «Processing» без спиннера читается как окончательное состояние, с animate-spin на иконке — как работа в процессе.

    • invoices-2026-09.csv2.4 MB
      Imported
    • customers.xlsx880 KB
      Processing
    • contracts.zip41 MB
      Needs review
    • payroll.csv120 KB
      Rejected
    • suppliers.csv64 KB
      Queued
  • Заливка там, где фон пёстрый

    На картинке мягкая плашка растворяется; dark заливает её весом, который держится на любой обложке. На заливке точка берёт цвет текста — маркер «Live» виден и на красном, — а полускруглённый бейдж xs становится штампом длительности.

    Live1:12:40

    Design tokens at scale

    Ada Lin · 3 Oct

    New48:12

    Accessible data tables

    Grace Wu · 26 Sep

    Updated36:05

    Theming without forks

    Linus Ray · 12 Sep

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
tone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
undefinedТон метки: несёт смысл, а не только цвет — «Оплачено» в `success`, «Просрочено» в `danger`. Не задан — из `GrConfigProvider`, иначе `neutral`.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedСтупень контрольной шкалы `--gr-control-text-*`: бейдж чаще стоит рядом с кнопками и полями, чем внутри абзаца. Не задан — из `GrConfigProvider`.
darkboolean | undefinedfalseЗаливка вместо мягкой подложки: плотный вес для пёстрого фона и для метки, которая обязана выделяться. Цвета — из слоя `--gr-badge-{tone}-bg`/`-fg`.
radiusGrBadgeRadius | undefinedundefinedФорма: `round` — таблетка статуса, `semi` — ярлык категории или кода, `square` — штамп. Не задан — из `GrConfigProvider`, иначе `round`.
dotboolean | GrBadgeDotTone | undefinedfalseТочка-маркер перед подписью: «● Активен». Имя тона красит маркер отдельно от плашки — ради пары «тихая плашка + цветной маркер», из-за которой паттерн и существует. `true` берёт тон самого бейджа. На заливке (`dark`) маркер всегда цвета текста: тон там слился бы с подложкой, взятой из того же слоя.

Слоты

SlotTypeОписание
defaultanyСодержимое метки.
iconanyИконка перед подписью: статус со спиннером, флаг, значок типа. Размер задаёт обёртка по ступени `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

grBadgeConfigconst
const grBadgeConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrBadge">;

Файлdist/types/components/GrBadge/defaults.d.ts

GrBadgeConfigurablePropsinterface

Пропы GrBadge, настраиваемые глобально через componentDefaults. Только оформление — см. GrButton/defaults.ts.

interface GrBadgeConfigurableProps {
    tone: GrBadgeTone;
    size: GrBadgeSize;
    radius: GrBadgeRadius;
}

Файлdist/types/components/GrBadge/grBadgeStyles.d.ts

GrBadgeTonetype
type GrBadgeTone = GrTone;
GrBadgeDotTonetype

Тон маркера — отдельным литеральным перечислением, а не 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';
GrBadgeSizetype
type GrBadgeSize = GrComponentSize;
GrBadgeRadiustype
type GrBadgeRadius = 'square' | 'semi' | 'round';

Файлdist/types/components/GrBadge/safelist.d.ts

grBadgeSafelistconst
const grBadgeSafelist: string[];

Файлdist/types/components/GrBadge/GrBadge.vue.d.ts

GrBadgePropsinterface
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 он превращается в пилюлю раньше, чем этого хочет макет.

vue
<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, и повешенный прямо на текст он просадил бы вместе с ним высоту всего бейджа.

Точка-маркер: тихая плашка, цветной статус
vue
<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

Статус часто состоит не только из слова: «идёт разбор» без спиннера читается как окончательное состояние, а не как работа.

vue
<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, поэтому вид бейджей задаётся один раз на приложение.