Badge & Tag
VibeUI has two label systems: tags (small, pill-like, inline) for status and intent, and badges (medium, filled or outlined) for counts and emphasis. Choose based on size and semantic weight.
Tags (small — panel use)
Tags are compact inline labels. Use them for status, category, intent, and count inside panel rows.
{/* Intent variants */}
<span className="panel-tag panel-tag-info">pending</span>
<span className="panel-tag panel-tag-success">passed</span>
<span className="panel-tag panel-tag-warning">slow</span>
<span className="panel-tag panel-tag-danger">critical</span>
<span className="panel-tag panel-tag-neutral">unknown</span>
.panel-tag {
display: inline-flex;
align-items: center;
padding: 1px 7px;
border-radius: var(--radius-xs); /* 3px */
font-size: var(--font-size-xs); /* 10px */
font-weight: var(--font-medium); /* 500 */
white-space: nowrap;
}
.panel-tag-info { background: var(--info-bg); color: var(--text-info); }
.panel-tag-success { background: var(--success-bg); color: var(--text-success); }
.panel-tag-warning { background: var(--warning-bg); color: var(--text-warning); }
.panel-tag-danger { background: var(--error-bg); color: var(--text-danger); }
.panel-tag-neutral { background: var(--bg-tertiary); color: var(--text-secondary); }
Tag with icon
<span className="panel-tag panel-tag-success" style={{ gap: 4 }}>
<Icon name="check" size={9} />
passed
</span>
Badges (medium — app shell / prominent)
Badges are larger, with full background fill. Used in the activity bar, navigation, and for count indicators.
<span className="panel-badge badge-success">3 passed</span>
<span className="panel-badge badge-error">1 failed</span>
<span className="panel-badge badge-warning">2 slow</span>
<span className="panel-badge badge-info">5 pending</span>
<span className="panel-badge badge-neutral">draft</span>
.panel-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 12px; /* pill */
font-size: 11px;
font-weight: 500;
}
.badge-success { background: var(--success-color); color: var(--btn-primary-fg); }
.badge-error { background: var(--error-color); color: var(--btn-error-fg); }
.badge-warning { background: var(--warning-color); color: var(--btn-primary-fg); }
.badge-info { background: var(--info-color); color: var(--btn-primary-fg); }
.badge-neutral { background: var(--bg-tertiary); color: var(--text-secondary); }
Decision: Tag vs Badge
| Situation | Use |
|---|---|
| Status inside a panel row (1–2 words) | panel-tag |
| Count in panel header | panel-tag |
| Accept-rate or percentage in header | panel-tag |
| Category / type chip on a card | panel-tag |
| Count on activity bar nav item | panel-badge |
| Prominent status in a summary view | panel-badge |
| Large emphasis label (standalone) | panel-badge |
Dynamic Tag from Status String
// Generic status → tag class
const statusTag = (s: string): string => {
const l = s.toLowerCase();
if (l.includes("pass") || l.includes("ok") || l.includes("complete") || l.includes("success"))
return "panel-tag panel-tag-success";
if (l.includes("warn") || l.includes("slow") || l.includes("pending") || l.includes("progress"))
return "panel-tag panel-tag-warning";
if (l.includes("fail") || l.includes("error") || l.includes("critical") || l.includes("reject"))
return "panel-tag panel-tag-danger";
if (l.includes("run") || l.includes("info") || l.includes("active"))
return "panel-tag panel-tag-info";
return "panel-tag panel-tag-neutral";
};
<span className={statusTag(item.status)}>{item.status}</span>
Priority → tag class
const priorityTag = (p: string): string =>
p === "Critical" ? "panel-tag panel-tag-danger"
: p === "High" ? "panel-tag panel-tag-warning"
: p === "Medium" ? "panel-tag panel-tag-info"
: "panel-tag panel-tag-neutral";
<span className={priorityTag(item.priority)}>{item.priority}</span>
Confidence → tag class
const confTag = (c: number): string =>
c > 0.85 ? "panel-tag panel-tag-success"
: c > 0.70 ? "panel-tag panel-tag-warning"
: "panel-tag panel-tag-danger";
<span className={confTag(prediction.confidence)}>
{(prediction.confidence * 100).toFixed(0)}%
</span>
Tags in Context
In a panel header (count)
<div className="panel-header">
<h3>Edit Predictions</h3>
<span className="panel-tag panel-tag-neutral" style={{ marginLeft: "auto" }}>
{(model.acceptanceRate * 100).toFixed(0)}% accept rate
</span>
</div>
In a card row (status)
<div className="panel-row" style={{ marginBottom: 6 }}>
<span style={{ fontWeight: "var(--font-semibold)" }}>{item.name}</span>
<span className={statusTag(item.status)} style={{ marginLeft: 8 }}>{item.status}</span>
<span style={{ marginLeft: "auto", color: "var(--text-secondary)" }}>42 lines</span>
</div>
As operation/type chip
<span className="panel-tag panel-tag-info">{edit.operation}</span> {/* "rename", "extract" */}
<span className="panel-tag panel-tag-neutral">{prediction.pattern}</span>
Auto-fixable indicator
{item.autoFixable && (
<span className="panel-tag panel-tag-success" style={{ marginLeft: 8 }}>auto-fixable</span>
)}
Rules
Do
- Always use intent variants (
panel-tag-{intent}) — neverpanel-tagalone - Use
panel-taginside panel rows and headers - Use
panel-badgefor navigation counts and prominent standalone labels - Use semantic functions (
statusTag(),priorityTag(),confTag()) for dynamic values
Don’t
// Missing intent variant
<span className="panel-tag">pending</span>
// Hardcoded color badge
<span style={{ padding: "2px 6px", borderRadius: 3, background: "#f44336", color: "#fff", fontSize: 10 }}>
critical
</span>
// Hardcoded priority badge
<span style={{
background: priority === "Critical" ? "#f44336" : "#ff9800",
color: "#fff", padding: "2px 6px", fontSize: 11, borderRadius: 3
}}>
{priority}
</span>
// → use priorityTag() function + panel-tag classes