В интерфейсах современных веб-приложений требуется быстро передавать пользователю состояние объекта, процесса или сущности. Для этой цели применяются статусы и badges — компактные визуальные индикаторы, которые позволяют:
Типичные примеры использования:
Processing, Shipped,
DeliveredOpen, In Progress,
Done3New, BetaOnline, OfflineБиблиотека Radix UI не предоставляет готового компонента Badge, однако предлагает низкоуровневые primitives, из которых удобно строить систему статусов и меток. Такой подход соответствует философии библиотеки — предоставить поведенческие и доступные primitives, а внешний вид и композицию оставить разработчику.
Radix UI строится вокруг идеи headless-компонентов. Это означает, что библиотека:
Badge или статус можно построить на основе следующих primitives:
SlotAccessibleIconTooltipHoverCardVisuallyHiddenPresenceТакая архитектура позволяет создавать:
Badge — компактная метка, обычно содержащая текст или число.
function Badge({ children, variant = "default" }) {
return (
<span className={`badge badge-${variant}`}>
{children}
</span>
);
}
<Badge>New</Badge>
<Badge variant="success">Active</Badge>
<Badge variant="warning">Pending</Badge>
| Вариант | Значение |
|---|---|
| default | обычная метка |
| success | успешное состояние |
| warning | предупреждение |
| danger | ошибка |
| info | информационный статус |
Для удобства проектируется единая система цветовых состояний.
.badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 9999px;
font-size: 12px;
font-weight: 500;
}
.badge-default {
background: #f1f1f1;
color: #333;
}
.badge-success {
background: #e6f7ec;
color: #1a7f37;
}
.badge-warning {
background: #fff4e5;
color: #9a6700;
}
.badge-danger {
background: #fdecea;
color: #b42318;
}
Метки часто сопровождаются иконками, чтобы быстрее передавать смысл статуса.
Radix UI предоставляет primitive AccessibleIcon, позволяющий корректно описывать иконки для скринридеров.
import { AccessibleIcon } from "@radix-ui/react-accessible-icon";
import { CheckIcon } from "@radix-ui/react-icons";
function StatusBadge() {
return (
<span className="badge badge-success">
<AccessibleIcon label="Success">
<CheckIcon />
</AccessibleIcon>
Active
</span>
);
}
Преимущество:
Часто badge используется для отображения количества уведомлений.
function NotificationBadge({ count }) {
return (
<span className="notification-badge">
{count > 99 ? "99+" : count}
</span>
);
}
<button className="notification-button">
Messages
<NotificationBadge count={5} />
</button>
.notification-button {
position: relative;
}
.notification-badge {
position: absolute;
top: -6px;
right: -6px;
min-width: 18px;
height: 18px;
border-radius: 999px;
background: red;
color: white;
font-size: 11px;
display: flex;
align-items: center;
justify-content: center;
}
Иногда требуется не текстовая метка, а точечный индикатор состояния.
function StatusDot({ status }) {
return (
<span className={`status-dot status-${status}`} />
);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-online {
background: #22c55e;
}
.status-offline {
background: #9ca3af;
}
.status-busy {
background: #ef4444;
}
Иногда необходимо пояснить статус. Для этого используется Tooltip primitive.
npm install @radix-ui/react-tooltip
import * as Tooltip from "@radix-ui/react-tooltip";
function StatusWithTooltip() {
return (
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<span className="badge badge-warning">
Pending
</span>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content className="tooltip">
Awaiting approval
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
}
Преимущества:
Badges используются внутри различных элементов интерфейса.
<Tab>
Messages
<Badge>3</Badge>
</Tab>
<MenuItem>
Updates
<Badge variant="info">New</Badge>
</MenuItem>
<Card>
<Badge variant="success">Published</Badge>
</Card>
Если статус требует дополнительной информации, используется HoverCard.
import * as HoverCard from "@radix-ui/react-hover-card";
function StatusHoverCard() {
return (
<HoverCard.Root>
<HoverCard.Trigger asChild>
<span className="badge badge-info">
Build
</span>
</HoverCard.Trigger>
<HoverCard.Content className="hover-card">
Build completed successfully.
</HoverCard.Content>
</HoverCard.Root>
);
}
Иногда статус должен сигнализировать об активном процессе.
.status-loading {
background: #3b82f6;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
opacity: 0.4;
}
50% {
opacity: 1;
}
100% {
opacity: 0.4;
}
}
<Badge className="status-loading">
Loading
</Badge>
Статусы должны быть доступны для пользователей с ограниченными возможностями.
Основные правила:
<span role="status">
Processing
</span>
Radix предоставляет primitive для скрытого текста.
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
<span className="status-dot status-online">
<VisuallyHidden>User is online</VisuallyHidden>
</span>
Плохая практика:
зелёный = успех
красный = ошибка
Правильный подход:
иконка + текст + цвет
Radix UI предоставляет primitive Slot, позволяющий переиспользовать компоненты.
import { Slot } from "@radix-ui/react-slot";
function Badge({ asChild, ...props }) {
const Comp = asChild ? Slot : "span";
return <Comp className="badge" {...props} />;
}
<Badge>Default</Badge>
<Badge asChild>
<a href="/notifications">3</a>
</Badge>
Преимущества:
В крупных приложениях формируется централизованная система статусов.
export const STATUS = {
ACTIVE: "active",
PENDING: "pending",
ERROR: "error",
DISABLED: "disabled"
};
const STATUS_VARIANTS = {
active: "success",
pending: "warning",
error: "danger",
disabled: "default"
};
function StatusBadge({ status }) {
const variant = STATUS_VARIANTS[status];
return (
<Badge variant={variant}>
{status}
</Badge>
);
}
Badges используются часто, поэтому важно учитывать оптимизацию.
const Badge = React.memo(function Badge({ children }) {
return <span className="badge">{children}</span>;
});
Badges являются частью большинства дизайн-систем:
В типичной дизайн-системе badges обладают:
Beta
Alpha
Experimental
News
Article
Tutorial
Todo
In progress
Done
v1.2
Deprecated
Legacy
Плохая практика:
20 типов badges
Оптимально:
5–7 вариантов
Плохо:
This feature is currently under internal development
Хорошо:
Internal
Если success зелёный в одном месте, он должен быть
зелёным во всей системе.
Избыточное количество badges снижает их ценность.
Правило:
badge = сигнал важной информации
Хорошая система badges должна обеспечивать:
Radix UI благодаря headless primitives позволяет реализовать такую систему максимально гибко, комбинируя primitives и собственные стили, создавая как простые метки, так и сложные статусные индикаторы, интегрированные в архитектуру современного интерфейса.