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

1"""Label atom — accessible form field labels and standalone text labels.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any, Literal 

6 

7from lexigram.ui.core.base import Component, el 

8 

9LabelSize = Literal["xs", "sm", "md", "lg"] 

10LabelWeight = Literal["normal", "medium", "semibold", "bold"] 

11 

12 

13class Label(Component): 

14 """Accessible label component for form fields and descriptive text. 

15 

16 Can be used as a standalone text label or associated with a form control 

17 via the ``for_`` attribute (renders as HTML ``for``). 

18 

19 Example:: 

20 

21 Label("Email address", for_="email-input", required=True) 

22 Label("Optional note", size="sm", weight="normal") 

23 """ 

24 

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. 

36 

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 

53 

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 ) 

73 

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 ) 

79 

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 ) 

90 

91 attrs: dict[str, Any] = {"class_": css} 

92 if self.for_: 

93 attrs["for_"] = self.for_ 

94 

95 return el("label", *children, **attrs) 

96 

97 

98__all__ = ["Label"]