Theme
Version
GitHub PyPI Discord
On this page

Badge

Use CBadge for short inline status, category, count, or metadata text. Badge is a visual label, not a Button, selectable Chip, removable Tag, or live announcement region.

Badge at a glance

Badge at a glance
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeAtAGlance(Component):
    template = """
      <section class="badge-glance" aria-labelledby="badge-glance-title">
        <div>
          <p>Mineral archive ยท specimen 184</p>
          <h2 id="badge-glance-title">Azurite rosette</h2>
        </div>
        <c-CGroup>
          <c-CBadge intent="primary">Copper carbonate</c-CBadge>
          <c-CBadge intent="success" variant="outline">Verified</c-CBadge>
          <c-CBadge shape="pill">3 fragments</c-CBadge>
        </c-CGroup>
      </section>
    """
    css = """
      :where(.badge-glance) {
        display: flex;
        flex-wrap: wrap;
        align-items: end;
        justify-content: space-between;
        gap: 1rem;
        max-inline-size: 38rem;
        padding: 1.25rem;
        border: 1px solid light-dark(#b7c6cf, #526873);
        border-radius: 0.85rem;
        background: light-dark(#f5fbff, #17232a);
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.badge-glance h2, .badge-glance p) {
        margin: 0;
      }

      :where(.badge-glance p) {
        color: light-dark(#496471, #a9c5d2);
        font-size: 0.75rem;
        font-weight: 700;
        letter-spacing: 0.06em;
        text-transform: uppercase;
      }

      :where(.badge-glance h2) {
        margin-block-start: 0.25rem;
        font-size: 1.1rem;
      }
    """


preview = BadgeAtAGlance()

preview  # noqa: B018

Compose a Badge

The default slot supplies the visible meaning. It is required.

Compose short inline labels
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BasicBadges(Component):
    template = """
      <div class="badge-basic">
        <p>Fluorite <c-CBadge>New</c-CBadge></p>
        <p>Cabinet 7 <c-CBadge shape="pill">24</c-CBadge></p>
        <p>Catalog record <c-CBadge variant="outline">Draft</c-CBadge></p>
      </div>
    """
    css = """
      :where(.badge-basic) {
        display: grid;
        gap: 0.75rem;
        max-inline-size: 24rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.badge-basic p) {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        margin: 0;
        padding-block-end: 0.5rem;
        border-block-end: 1px solid light-dark(#d8d2c6, #4f4a42);
      }
    """


preview = BasicBadges()

preview  # noqa: B018
<c-CBadge intent="success">Verified</c-CBadge>

Compose the same result in Python:

from citry_ui import CBadge

verified = CBadge(intent="success", slots={"default": "Verified"})

Carry meaning with text

Intent selects a palette. The visible label must still explain the state, so the result remains understandable without color.

Compare Badge intents
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeIntents(Component):
    template = """
      <c-CGroup class_="badge-intents">
        <c-CBadge intent="neutral">Unsorted</c-CBadge>
        <c-CBadge intent="primary">In study</c-CBadge>
        <c-CBadge intent="success">Verified</c-CBadge>
        <c-CBadge intent="warn">Handle carefully</c-CBadge>
        <c-CBadge intent="danger">Restricted</c-CBadge>
      </c-CGroup>
    """
    css = """
      :where(.badge-intents) {
        max-inline-size: 34rem;
        padding: 1rem;
        border-radius: 0.75rem;
        background: light-dark(#f5f1e8, #25221e);
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = BadgeIntents()

preview  # noqa: B018

Choose visual emphasis

Use soft for quiet metadata, solid for stronger emphasis, and outline when the surrounding surface should remain visible.

Compare Badge variants
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeVariants(Component):
    template = """
      <c-CStack class_="badge-variants" gap="sm">
        <c-CGroup><strong>Soft</strong><c-CBadge intent="primary">Lapis</c-CBadge></c-CGroup>
        <c-CGroup><strong>Solid</strong><c-CBadge intent="primary" variant="solid">Lapis</c-CBadge></c-CGroup>
        <c-CGroup><strong>Outline</strong><c-CBadge intent="primary" variant="outline">Lapis</c-CBadge></c-CGroup>
      </c-CStack>
    """
    css = """
      :where(.badge-variants) {
        max-inline-size: 20rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.badge-variants > [data-citry-ui-part="group"]) {
        justify-content: space-between;
        padding: 0.75rem;
        border: 1px solid light-dark(#d4cabc, #514940);
        border-radius: 0.6rem;
      }
    """


preview = BadgeVariants()

preview  # noqa: B018

Choose size and shape

Sizes change compact type and spacing. Shape changes only the corner radius.

Compare sizes and shapes
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeSizesAndShapes(Component):
    template = """
      <c-CStack class_="badge-sizes">
        <c-CGroup align="baseline">
          <c-CBadge size="sm">Small</c-CBadge>
          <c-CBadge>Medium</c-CBadge>
          <c-CBadge size="lg">Large</c-CBadge>
        </c-CGroup>
        <c-CGroup>
          <c-CBadge shape="rounded" intent="success">Rounded</c-CBadge>
          <c-CBadge shape="pill" intent="success">Pill</c-CBadge>
        </c-CGroup>
      </c-CStack>
    """
    css = """
      :where(.badge-sizes) {
        max-inline-size: 28rem;
        padding: 1rem;
        border: 1px solid light-dark(#cbd5d9, #475a62);
        border-radius: 0.75rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = BadgeSizesAndShapes()

preview  # noqa: B018

Add registered icons

Use the start and end slots for short decorative content. Keep the default label meaningful without the icon.

Add registered icons
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeIcons(Component):
    template = """
      <c-CGroup class_="badge-icons">
        <c-CBadge intent="success">
          <c-fill name="start"><c-CIcon name="check" /></c-fill>
          <c-fill name="default">Verified origin</c-fill>
        </c-CBadge>
        <c-CBadge intent="warn" variant="outline">
          <c-fill name="default">Requires gloves</c-fill>
          <c-fill name="end"><c-CIcon name="triangle-alert" /></c-fill>
        </c-CBadge>
      </c-CGroup>
    """
    css = """
      :where(.badge-icons) {
        max-inline-size: 30rem;
        padding: 1rem;
        background: light-dark(#f4f0e7, #29251f);
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = BadgeIcons()

preview  # noqa: B018

Give counts context

A lone number is ambiguous. Put counts beside understandable owner text and include the count's meaning in the owner accessible name when needed.

Present counts in context
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeCountsAndContext(Component):
    template = """
      <nav class="badge-counts" aria-label="Mineral archive queues">
        <a href="#unfiled" aria-label="Unfiled specimens, 12 items">
          <span>Unfiled specimens</span><c-CBadge shape="pill">12</c-CBadge>
        </a>
        <a href="#review" aria-label="Awaiting review, 4 items">
          <span>Awaiting review</span><c-CBadge shape="pill" intent="warn">4</c-CBadge>
        </a>
      </nav>
    """
    css = """
      :where(.badge-counts) {
        display: grid;
        gap: 0.375rem;
        max-inline-size: 22rem;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.badge-counts a) {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        padding: 0.75rem;
        border-radius: 0.6rem;
        color: CanvasText;
        text-decoration: none;
      }

      :where(.badge-counts a:hover) {
        background: light-dark(#ece6da, #322d27);
      }
    """


preview = BadgeCountsAndContext()

preview  # noqa: B018

Badge does not cap large values or hide zero. Format the slot content in your application so display and accessible context stay under one policy.

Position a Badge around an owner

Badge owns no positioning or overlap. Use ordinary CSS when a count belongs at the corner of a Button, Avatar, or other item.

Position a Badge with consumer CSS
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgePositioning(Component):
    template = """
      <div class="badge-positioning">
        <c-CButton c-attrs="{'aria-label': 'Field notes, 7 unread'}">
          Field notes
          <c-CBadge intent="danger" shape="pill">7</c-CBadge>
        </c-CButton>
      </div>
    """
    css = """
      :where(.badge-positioning) {
        min-block-size: 7rem;
        padding: 1.5rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.badge-positioning [data-citry-ui-part="button"]) {
        position: relative;
      }

      :where(.badge-positioning [data-citry-ui-part="badge"]) {
        position: absolute;
        inset-block-start: 0;
        inset-inline-end: 0;
        translate: 45% -45%;
      }
    """


preview = BadgePositioning()

preview  # noqa: B018

Customize Badge

Override public variables on an ancestor or one Badge. Stable part selectors support local geometry without relying on private classes.

Customize Badge with public CSS
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class BadgeCustomization(Component):
    template = """
      <c-CGroup class_="badge-themes">
        <div class="badge-themes__quartz">
          <c-CBadge>Quartz archive</c-CBadge>
        </div>
        <div class="badge-themes__basalt">
          <c-CBadge variant="outline">Basalt archive</c-CBadge>
        </div>
      </c-CGroup>
    """
    css = """
      :where(.badge-themes > div) {
        padding: 1.25rem;
        border-radius: 0.75rem;
      }

      :where(.badge-themes__quartz) {
        --cui-badge-background: #f0e7ff;
        --cui-badge-foreground: #4c1d75;
        --cui-badge-radius: 0.2rem;
        background: #faf7ff;
      }

      :where(.badge-themes__basalt) {
        color-scheme: dark;
        --cui-badge-background: #1e2930;
        --cui-badge-foreground: #d7edf2;
        --cui-badge-border-color: #72a8b5;
        --cui-badge-radius: 999px;
        background: #10171b;
      }
    """


preview = BadgeCustomization()

preview  # noqa: B018

Accessibility and behavior

Badge renders a neutral, unfocusable span with no JavaScript. Do not place Buttons, links, inputs, or other controls inside it. Put Badge inside the interactive owner instead.

Changing Badge text does not create a live announcement. Use a persistent status or Alert surface when a browser update must be announced.

API reference

Inputs

CBadge server inputs

Server inputs are passed in a template through <c-CBadge ... /> or in Python through CBadge(...).

InputTypeDefaultEffect
variant"soft" | "solid" | "outline" (CBadgeVariant)"soft"Selects quiet fill, strong fill, or outlined emphasis.
intent"neutral" | "primary" | "success" | "warn" | "danger" (CBadgeIntent)"neutral"Selects a visual palette; authored text must still carry status meaning.
size"sm" | "md" | "lg" (CBadgeSize)"md"Sets compact height, type, padding, icon size, and gap.
shape"rounded" | "pill" (CBadgeShape)"rounded"Selects compact rounded or fully pill-shaped geometry.
class_str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue)NoneAdds root classes and merges them with attrs.
stylestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue)NoneAdds root inline styles and merges them with attrs.
attrsMapping[str, object] | NoneNoneAdds copied trusted native, data, and targeted Alpine root attributes without replacing Badge anatomy, semantics, or Citry runtime fields.

Slots

Slots are passed as nested content or <c-fill> tags in a template, or through the slots={...} argument in Python.

CBadge slots

SlotRequiredDataFallback
defaultyes{} (CBadgeDefaultSlotData)Missing fill raises before rendering.
startno{} (CBadgeStartSlotData)Leading wrapper omitted.
endno{} (CBadgeEndSlotData)Trailing wrapper omitted.

Events

-

Methods

-

CSS

CSS variables to theme the components. Set them on an ancestor or the component itself.

CBadge CSS variables

Apply these variables to CBadge or one of its ancestors.

VariableTypePurposeDefault
--cui-badge-backgroundcolorRoot fill.Variant- and intent-derived color.
--cui-badge-foregroundcolorLabel and icon foreground.Contrast-checked variant and intent color.
--cui-badge-border-colorcolorRoot border.Variant-derived transparent or currentColor.
--cui-badge-radiuslengthRoot corner radius.Shape-derived 0.375rem or 999px.
--cui-badge-min-heightlengthCompact minimum block size.Size-derived length.
--cui-badge-padding-inlinelengthRoot inline padding.Size-derived length.
--cui-badge-gaplengthSpace between supplied slot wrappers.Size-derived length.
--cui-badge-font-sizelengthLabel font size.Size-derived length.
--cui-badge-font-weightfont-weightLabel weight.650

Attributes

HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.

CBadge attributes

AttributeElementTypeMeaning
data-variantRoot"soft" | "solid" | "outline"Reflects the selected emphasis treatment.
data-intentRoot"neutral" | "primary" | "success" | "warn" | "danger"Reflects the selected visual palette.
data-sizeRoot"sm" | "md" | "lg"Reflects compact geometry.
data-shapeRoot"rounded" | "pill"Reflects corner geometry.

Selectors

Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.

CBadge selectors

SelectorElementPurpose
[data-citry-ui-part="badge"]Native span rootStable Badge root and attrs destination.
[data-citry-ui-part="start"]Optional leading wrapperLeading icon/content layout.
[data-citry-ui-part="label"]Required label wrapperVisible meaning-bearing content.
[data-citry-ui-part="end"]Optional trailing wrapperTrailing icon/content layout.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue]
CBadgeVariantLiteral["soft", "solid", "outline"]
CBadgeIntentLiteral["neutral", "primary", "success", "warn", "danger"]
CBadgeSizeLiteral["sm", "md", "lg"]
CBadgeShapeLiteral["rounded", "pill"]

CBadgeDefaultSlotData

Empty dataclass: {}.

CBadgeStartSlotData

Empty dataclass: {}.

CBadgeEndSlotData

Empty dataclass: {}.

Translation keys

-