Coverage for src / lexigram / ui / atoms / link.py: 96%
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
1from __future__ import annotations
3from typing import Any, Literal
5from lexigram.ui.core.base import Component, el
7LinkVariant = Literal["default", "muted", "primary"]
8LinkSize = Literal["sm", "md", "lg"]
11class Link(Component):
12 """Create a consistent styled link element.
14 Args:
15 label: Link text
16 href: URL
17 variant: Visual style variant
18 size: Optional size (sm, md, lg)
19 """
21 def __init__(
22 self,
23 label: str,
24 href: str,
25 *,
26 as_child: bool = False,
27 variant: LinkVariant = "default",
28 size: LinkSize | None = None,
29 **props,
30 ) -> None:
31 super().__init__(as_child=as_child, **props)
32 self.label = label
33 self.href = href
34 self.variant = variant
35 self.size = size
37 def render(self) -> Any:
38 base_classes = ["text-sm", "font-medium", "inline-flex", "items-center"]
40 if self.variant == "primary":
41 base_classes.append("text-primary hover:text-primary/80")
42 elif self.variant == "muted":
43 base_classes.append(
44 "text-muted-foreground hover:text-foreground"
45 )
46 else:
47 base_classes.append("text-foreground hover:underline")
49 if self.size == "sm":
50 base_classes.append("text-sm")
51 elif self.size == "lg":
52 base_classes.append("text-lg")
54 custom_cls = self.props.get("class_") or self.props.get("class")
55 cls = " ".join(base_classes) + (f" {custom_cls}" if custom_cls else "")
57 extra_attrs = {
58 k: v
59 for k, v in self.props.items()
60 if k not in ("class_", "class", "children")
61 }
62 return el("a", self.label, href=self.href, class_=cls, **extra_attrs)