"""
A compact status indicator badge.
"""
from duck.html.components.span import Span
from quirl.theme import Theme
# Theme token used for each semantic badge variant
VARIANT_TOKEN_MAP = {
"neutral": "text_color",
"accent": "accent_color",
"success": "success_color",
"warning": "warning_color",
"error": "error_color",
"info": "info_color",
}
# Padding and font size for each badge size
SIZE_STYLE_MAP = {
"sm": {"padding": "2px 8px", "font-size": "0.75rem"},
"md": {"padding": "4px 12px", "font-size": "0.875rem"},
"lg": {"padding": "6px 16px", "font-size": "1rem"},
}
[docs]
class Badge(Span):
"""
A compact status indicator badge.
Renders as a tinted rounded pill, ideal for statuses, counts, or
categories. Pulls colors from the active theme by default.
Usage:
```python
Badge(text="New", variant="success")
```
Required Props:
- text: The badge label text
Optional Props:
- variant: One of neutral, accent, success, warning, error, info. Defaults to neutral.
- dot: Render as a small solid dot with no text. Defaults to False.
- size: One of sm, md, lg. Defaults to md.
- bg_color: Override background color.
- color: Override text color.
"""
docs_preview_kwargs = {
"text": "Beta",
"variant": "accent",
}
[docs]
def on_create(self):
"""
Build the badge with theme-aware tinting, sizing, and radius.
"""
super().on_create()
variant = self.kwargs.get("variant", "neutral")
size = self.kwargs.get("size", "md")
dot = self.kwargs.get("dot", False)
tone = getattr(Theme.current, VARIANT_TOKEN_MAP.get(variant, "text_color"))
# Tint the background with the variant's tone instead of a flat fill
bg_color = self.kwargs.get("bg_color", f"color-mix(in srgb, {tone} 16%, transparent)")
text_color = self.kwargs.get("color", tone)
# Apply theme-aware badge styles
self.style.update({
"display": "inline-flex",
"align-items": "center",
"border-radius": "9999px",
"font-weight": "600",
"line-height": "1",
"background": bg_color,
"color": text_color,
"animation": "quirl-scale-in 0.2s ease-out",
**SIZE_STYLE_MAP.get(size, SIZE_STYLE_MAP["md"]),
})
if dot:
# Collapse to a small solid indicator dot
self.style.update({
"width": "8px",
"height": "8px",
"min-width": "8px",
"padding": "0",
"background": text_color,
})
self.inner_html = ""
else:
self.inner_html = self.kwargs.get("text", "")