Coverage for src / lexigram / ui / atoms / label.py: 100%
27 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
1"""Label atom — accessible form field labels and standalone text labels."""
3from __future__ import annotations
5from typing import Any, Literal
7from lexigram.ui.core.base import Component, el
9LabelSize = Literal["xs", "sm", "md", "lg"]
10LabelWeight = Literal["normal", "medium", "semibold", "bold"]
13class Label(Component):
14 """Accessible label component for form fields and descriptive text.
16 Can be used as a standalone text label or associated with a form control
17 via the ``for_`` attribute (renders as HTML ``for``).
19 Example::
21 Label("Email address", for_="email-input", required=True)
22 Label("Optional note", size="sm", weight="normal")
23 """
25 def __init__(
26 self,
27 text: str,
28 for_: str | None = None,
29 required: bool = False,
30 size: LabelSize = "sm",
31 weight: LabelWeight = "medium",
32 muted: bool = False,
33 **props: Any,
34 ) -> None:
35 """Initialise a Label atom.
37 Args:
38 text: The visible label text.
39 for_: The ``id`` of the associated form control (renders as ``for``).
40 required: When ``True`` appends a required indicator asterisk.
41 size: Text size variant — ``"xs"``, ``"sm"`` (default), ``"md"``, ``"lg"``.
42 weight: Font-weight variant.
43 muted: When ``True`` applies a muted/secondary text colour.
44 **props: Additional HTML attributes forwarded to the element.
45 """
46 super().__init__(text=text, for_=for_, required=required, size=size, **props)
47 self.text = text
48 self.for_ = for_
49 self.required = required
50 self.size = size
51 self.weight = weight
52 self.muted = muted
54 def render(self) -> Any:
55 """Render the label element."""
56 size_classes: dict[LabelSize, str] = {
57 "xs": "text-xs",
58 "sm": "text-sm",
59 "md": "text-base",
60 "lg": "text-lg",
61 }
62 weight_classes: dict[LabelWeight, str] = {
63 "normal": "font-normal",
64 "medium": "font-medium",
65 "semibold": "font-semibold",
66 "bold": "font-bold",
67 }
68 colour_class = (
69 "text-muted-foreground"
70 if self.muted
71 else "text-foreground"
72 )
74 css = (
75 f"block {size_classes.get(self.size, 'text-sm')} "
76 f"{weight_classes.get(self.weight, 'font-medium')} "
77 f"{colour_class}"
78 )
80 children: list[Any] = [self.text]
81 if self.required:
82 children.append(
83 el(
84 "span",
85 " *",
86 class_="text-destructive ml-0.5",
87 aria_hidden="true",
88 )
89 )
91 attrs: dict[str, Any] = {"class_": css}
92 if self.for_:
93 attrs["for_"] = self.for_
95 return el("label", *children, **attrs)
98__all__ = ["Label"]