eidosui 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,283 @@
1
+ /* EidosUI Markdown Styles - Theme-aware markdown rendering */
2
+
3
+ /* Base container */
4
+ .eidos-md {
5
+ color: var(--color-text);
6
+ line-height: var(--line-height-relaxed);
7
+ font-size: var(--font-size-base);
8
+ }
9
+
10
+ /* Headings */
11
+ .eidos-md h1 {
12
+ font-size: var(--font-size-4xl);
13
+ font-weight: var(--font-weight-bold);
14
+ color: var(--color-text);
15
+ margin-top: var(--space-2xl);
16
+ margin-bottom: var(--space-lg);
17
+ line-height: var(--line-height-tight);
18
+ }
19
+
20
+ .eidos-md h2 {
21
+ font-size: var(--font-size-3xl);
22
+ font-weight: var(--font-weight-semibold);
23
+ color: var(--color-text);
24
+ margin-top: var(--space-xl);
25
+ margin-bottom: var(--space-md);
26
+ line-height: var(--line-height-tight);
27
+ }
28
+
29
+ .eidos-md h3 {
30
+ font-size: var(--font-size-2xl);
31
+ font-weight: var(--font-weight-semibold);
32
+ color: var(--color-text);
33
+ margin-top: var(--space-lg);
34
+ margin-bottom: var(--space-md);
35
+ line-height: var(--line-height-snug);
36
+ }
37
+
38
+ .eidos-md h4 {
39
+ font-size: var(--font-size-xl);
40
+ font-weight: var(--font-weight-medium);
41
+ color: var(--color-text);
42
+ margin-top: var(--space-lg);
43
+ margin-bottom: var(--space-sm);
44
+ line-height: var(--line-height-snug);
45
+ }
46
+
47
+ .eidos-md h5 {
48
+ font-size: var(--font-size-lg);
49
+ font-weight: var(--font-weight-medium);
50
+ color: var(--color-text);
51
+ margin-top: var(--space-md);
52
+ margin-bottom: var(--space-sm);
53
+ line-height: var(--line-height-normal);
54
+ }
55
+
56
+ .eidos-md h6 {
57
+ font-size: var(--font-size-base);
58
+ font-weight: var(--font-weight-medium);
59
+ color: var(--color-text);
60
+ margin-top: var(--space-md);
61
+ margin-bottom: var(--space-sm);
62
+ line-height: var(--line-height-normal);
63
+ }
64
+
65
+ /* Paragraphs */
66
+ .eidos-md p {
67
+ margin-top: 0;
68
+ margin-bottom: var(--space-md);
69
+ }
70
+
71
+ /* Links */
72
+ .eidos-md a {
73
+ color: var(--color-primary);
74
+ text-decoration: underline;
75
+ text-underline-offset: 2px;
76
+ transition: color var(--transition-fast);
77
+ }
78
+
79
+ .eidos-md a:hover {
80
+ color: var(--color-primary-hover);
81
+ }
82
+
83
+ /* Lists */
84
+ .eidos-md ul {
85
+ margin-top: 0;
86
+ margin-bottom: var(--space-md);
87
+ padding-left: var(--space-lg);
88
+ list-style-type: disc;
89
+ }
90
+
91
+ .eidos-md ol {
92
+ margin-top: 0;
93
+ margin-bottom: var(--space-md);
94
+ padding-left: var(--space-lg);
95
+ list-style-type: decimal;
96
+ }
97
+
98
+ .eidos-md li {
99
+ margin-bottom: var(--space-xs);
100
+ }
101
+
102
+ /* Nested lists */
103
+ .eidos-md ul ul {
104
+ list-style-type: circle;
105
+ margin-top: var(--space-xs);
106
+ margin-bottom: 0;
107
+ }
108
+
109
+ .eidos-md ul ul ul {
110
+ list-style-type: square;
111
+ }
112
+
113
+ .eidos-md ol ol {
114
+ list-style-type: lower-alpha;
115
+ margin-top: var(--space-xs);
116
+ margin-bottom: 0;
117
+ }
118
+
119
+ .eidos-md ul ol,
120
+ .eidos-md ol ul {
121
+ margin-top: var(--space-xs);
122
+ margin-bottom: 0;
123
+ }
124
+
125
+ /* Code */
126
+ .eidos-md code {
127
+ background-color: var(--color-surface);
128
+ padding: calc(var(--space-xs) / 2) var(--space-xs);
129
+ border-radius: var(--radius-sm);
130
+ font-size: var(--font-size-sm);
131
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
132
+ }
133
+
134
+ .eidos-md pre {
135
+ background-color: var(--color-surface);
136
+ border: var(--border) solid var(--color-border);
137
+ border-radius: var(--radius-md);
138
+ padding: var(--space-md);
139
+ overflow-x: auto;
140
+ margin-top: 0;
141
+ margin-bottom: var(--space-md);
142
+ }
143
+
144
+ .eidos-md pre code {
145
+ background-color: transparent;
146
+ padding: 0;
147
+ border-radius: 0;
148
+ font-size: var(--font-size-sm);
149
+ }
150
+
151
+ /* Blockquotes */
152
+ .eidos-md blockquote {
153
+ border-left: var(--border-4) solid var(--color-primary);
154
+ padding-left: var(--space-md);
155
+ margin: var(--space-lg) 0;
156
+ color: var(--color-text-muted);
157
+ }
158
+
159
+ .eidos-md blockquote p:last-child {
160
+ margin-bottom: 0;
161
+ }
162
+
163
+ /* Tables */
164
+ .eidos-md table {
165
+ width: 100%;
166
+ border-collapse: collapse;
167
+ margin: var(--space-lg) 0;
168
+ font-size: var(--font-size-sm);
169
+ }
170
+
171
+ .eidos-md th {
172
+ background-color: var(--color-surface);
173
+ font-weight: var(--font-weight-semibold);
174
+ padding: var(--space-sm) var(--space-md);
175
+ border-bottom: var(--border-2) solid var(--color-border);
176
+ text-align: left;
177
+ }
178
+
179
+ .eidos-md td {
180
+ padding: var(--space-sm) var(--space-md);
181
+ border-bottom: var(--border) solid var(--color-border);
182
+ }
183
+
184
+ .eidos-md tr:hover {
185
+ background-color: rgba(0, 0, 0, var(--opacity-5));
186
+ }
187
+
188
+ /* Horizontal rules */
189
+ .eidos-md hr {
190
+ border: none;
191
+ border-top: var(--border) solid var(--color-border);
192
+ margin: var(--space-xl) 0;
193
+ }
194
+
195
+ /* Images */
196
+ .eidos-md img {
197
+ max-width: 100%;
198
+ height: auto;
199
+ border-radius: var(--radius-md);
200
+ margin: var(--space-md) 0;
201
+ }
202
+
203
+ /* Strong and emphasis */
204
+ .eidos-md strong {
205
+ font-weight: var(--font-weight-semibold);
206
+ color: var(--color-text);
207
+ }
208
+
209
+ .eidos-md em {
210
+ font-style: italic;
211
+ }
212
+
213
+ /* Definition lists */
214
+ .eidos-md dl {
215
+ margin: var(--space-md) 0;
216
+ }
217
+
218
+ .eidos-md dt {
219
+ font-weight: var(--font-weight-semibold);
220
+ margin-top: var(--space-md);
221
+ }
222
+
223
+ .eidos-md dd {
224
+ margin-left: var(--space-lg);
225
+ margin-bottom: var(--space-sm);
226
+ }
227
+
228
+ /* GitHub-style Alerts */
229
+ .eidos-alert {
230
+ padding: var(--space-md);
231
+ border-radius: var(--radius-md);
232
+ margin: var(--space-lg) 0;
233
+ border: var(--border) solid;
234
+ }
235
+
236
+ .eidos-alert-header {
237
+ display: flex;
238
+ align-items: center;
239
+ gap: var(--space-sm);
240
+ margin-bottom: var(--space-sm);
241
+ font-weight: var(--font-weight-semibold);
242
+ }
243
+
244
+ .eidos-alert-icon {
245
+ font-size: var(--font-size-lg);
246
+ }
247
+
248
+ .eidos-alert-title {
249
+ font-size: var(--font-size-base);
250
+ }
251
+
252
+ .eidos-alert-content {
253
+ margin-left: calc(var(--font-size-lg) + var(--space-sm));
254
+ }
255
+
256
+ .eidos-alert-content > *:last-child {
257
+ margin-bottom: 0;
258
+ }
259
+
260
+ /* Alert variants */
261
+ .eidos-alert-info {
262
+ background-color: var(--color-info-light);
263
+ border-color: var(--color-info);
264
+ color: var(--color-info-dark);
265
+ }
266
+
267
+ .eidos-alert-success {
268
+ background-color: var(--color-success-light);
269
+ border-color: var(--color-success);
270
+ color: var(--color-success-dark);
271
+ }
272
+
273
+ .eidos-alert-warning {
274
+ background-color: var(--color-warning-light);
275
+ border-color: var(--color-warning);
276
+ color: var(--color-warning-dark);
277
+ }
278
+
279
+ .eidos-alert-error {
280
+ background-color: var(--color-error-light);
281
+ border-color: var(--color-error);
282
+ color: var(--color-error-dark);
283
+ }
@@ -0,0 +1 @@
1
+ # Markdown plugin extensions
@@ -0,0 +1,134 @@
1
+ """GitHub-style alerts extension for markdown"""
2
+
3
+ import re
4
+ from markdown.extensions import Extension
5
+ from markdown.blockprocessors import BlockProcessor
6
+ import xml.etree.ElementTree as etree
7
+ from xml.etree.ElementTree import SubElement
8
+
9
+
10
+ class AlertBlockProcessor(BlockProcessor):
11
+ """Process GitHub-style alert blocks"""
12
+
13
+ # Pattern to match > [!TYPE] at the start of a blockquote
14
+ RE_ALERT = re.compile(r'^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]', re.MULTILINE)
15
+
16
+ # Alert type configurations
17
+ ALERT_TYPES = {
18
+ 'NOTE': {
19
+ 'class': 'eidos-alert eidos-alert-info',
20
+ 'icon': 'ℹ️',
21
+ 'title': 'Note'
22
+ },
23
+ 'TIP': {
24
+ 'class': 'eidos-alert eidos-alert-success',
25
+ 'icon': '💡',
26
+ 'title': 'Tip'
27
+ },
28
+ 'IMPORTANT': {
29
+ 'class': 'eidos-alert eidos-alert-warning',
30
+ 'icon': '❗',
31
+ 'title': 'Important'
32
+ },
33
+ 'WARNING': {
34
+ 'class': 'eidos-alert eidos-alert-warning',
35
+ 'icon': '⚠️',
36
+ 'title': 'Warning'
37
+ },
38
+ 'CAUTION': {
39
+ 'class': 'eidos-alert eidos-alert-error',
40
+ 'icon': '🔴',
41
+ 'title': 'Caution'
42
+ }
43
+ }
44
+
45
+ def test(self, parent, block):
46
+ """Test if the block is a GitHub-style alert"""
47
+ return bool(self.RE_ALERT.match(block))
48
+
49
+ def run(self, parent, blocks):
50
+ """Process the alert block"""
51
+ block = blocks.pop(0)
52
+
53
+ # Extract alert type
54
+ match = self.RE_ALERT.match(block)
55
+ if not match:
56
+ return False
57
+
58
+ alert_type = match.group(1)
59
+ alert_config = self.ALERT_TYPES.get(alert_type, self.ALERT_TYPES['NOTE'])
60
+
61
+ # Create the alert container
62
+ alert_div = SubElement(parent, 'div')
63
+ alert_div.set('class', alert_config['class'])
64
+
65
+ # Add the header with icon and title
66
+ header = SubElement(alert_div, 'div')
67
+ header.set('class', 'eidos-alert-header')
68
+
69
+ icon_span = SubElement(header, 'span')
70
+ icon_span.set('class', 'eidos-alert-icon')
71
+ icon_span.text = alert_config['icon']
72
+
73
+ title_span = SubElement(header, 'span')
74
+ title_span.set('class', 'eidos-alert-title')
75
+ title_span.text = alert_config['title']
76
+
77
+ # Process the content
78
+ content_div = SubElement(alert_div, 'div')
79
+ content_div.set('class', 'eidos-alert-content')
80
+
81
+ # Remove the alert marker and process the remaining content
82
+ content = self.RE_ALERT.sub('', block)
83
+
84
+ # Handle multi-line content
85
+ lines = content.split('\n')
86
+ processed_lines = []
87
+
88
+ for line in lines:
89
+ # Remove leading '>' from each line
90
+ if line.startswith('>'):
91
+ line = line[1:].lstrip()
92
+ processed_lines.append(line)
93
+
94
+ # Join the content and parse it
95
+ content_text = '\n'.join(processed_lines).strip()
96
+
97
+ # Parse the content as markdown
98
+ if content_text:
99
+ # Create a temporary element to hold parsed content
100
+ temp_element = etree.Element('div')
101
+ self.parser.parseBlocks(temp_element, [content_text])
102
+
103
+ # Move all children to our content div
104
+ for child in temp_element:
105
+ content_div.append(child)
106
+
107
+ # If no children were added, add the text directly
108
+ if len(content_div) == 0:
109
+ p = SubElement(content_div, 'p')
110
+ p.text = content_text
111
+
112
+ # Continue processing subsequent blocks that might be part of the alert
113
+ while blocks and blocks[0].startswith('>'):
114
+ continuation = blocks.pop(0)
115
+ # Remove leading '>' and process
116
+ continuation_text = continuation[1:].lstrip() if continuation.startswith('>') else continuation
117
+
118
+ if continuation_text:
119
+ p = SubElement(content_div, 'p')
120
+ p.text = continuation_text
121
+
122
+ return True
123
+
124
+
125
+ class AlertExtension(Extension):
126
+ """Add GitHub-style alerts to markdown"""
127
+
128
+ def extendMarkdown(self, md):
129
+ """Add the alert processor to the markdown instance"""
130
+ md.parser.blockprocessors.register(
131
+ AlertBlockProcessor(md.parser),
132
+ 'github_alerts',
133
+ 175 # Priority - process before blockquote
134
+ )
@@ -0,0 +1,58 @@
1
+ """Core markdown rendering with theme integration"""
2
+
3
+ import markdown
4
+ from typing import Optional, List, Union
5
+ from .extensions.alerts import AlertExtension
6
+
7
+
8
+ class MarkdownRenderer:
9
+ """Core markdown rendering with theme integration"""
10
+
11
+ def __init__(self, extensions: Optional[List[Union[str, markdown.Extension]]] = None):
12
+ """Initialize the renderer with optional extensions.
13
+
14
+ Args:
15
+ extensions: List of markdown extension names or instances to enable
16
+ """
17
+ self.extensions = extensions or []
18
+ # Add some useful default extensions
19
+ default_extensions = [
20
+ 'fenced_code',
21
+ 'tables',
22
+ 'nl2br',
23
+ 'sane_lists',
24
+ AlertExtension() # Add GitHub-style alerts
25
+ ]
26
+ self.extensions.extend(default_extensions)
27
+
28
+ # Initialize the markdown processor
29
+ self.md = markdown.Markdown(extensions=self.extensions)
30
+
31
+ def render(self, markdown_text: str) -> str:
32
+ """Convert markdown to themed HTML.
33
+
34
+ Args:
35
+ markdown_text: Raw markdown text to render
36
+
37
+ Returns:
38
+ HTML string wrapped with eidos-md class for styling
39
+ """
40
+ # Reset the markdown processor to clear any state
41
+ self.md.reset()
42
+
43
+ # Convert markdown to HTML
44
+ html_content = self.md.convert(markdown_text)
45
+
46
+ # Wrap in a div with our markdown class for styling
47
+ return f'<div class="eidos-md">{html_content}</div>'
48
+
49
+ def add_extension(self, extension: str) -> None:
50
+ """Add a markdown extension.
51
+
52
+ Args:
53
+ extension: Name of the markdown extension to add
54
+ """
55
+ if extension not in self.extensions:
56
+ self.extensions.append(extension)
57
+ # Recreate the markdown processor with new extensions
58
+ self.md = markdown.Markdown(extensions=self.extensions)
eidos/styles.py ADDED
@@ -0,0 +1,80 @@
1
+ """CSS class constants for EidosUI components.
2
+
3
+ This module provides Python constants for CSS classes that exist in styles.css.
4
+ Only includes classes that are actually defined in the CSS file.
5
+ """
6
+
7
+ from typing import Final
8
+
9
+ class Theme:
10
+ """Theme-related CSS classes from styles.css."""
11
+ body: Final[str] = "eidos-body"
12
+
13
+ class Buttons:
14
+ """Button-related CSS classes from styles.css."""
15
+
16
+ # Base button class (required for all buttons)
17
+ base: Final[str] = "eidos-btn"
18
+
19
+ # Button variants
20
+ primary: Final[str] = "eidos-btn-primary"
21
+ secondary: Final[str] = "eidos-btn-secondary"
22
+ ghost: Final[str] = "eidos-btn-ghost"
23
+ outline: Final[str] = "eidos-btn-outline"
24
+ success: Final[str] = "eidos-btn-success"
25
+ error: Final[str] = "eidos-btn-error"
26
+ cta: Final[str] = "eidos-btn-cta"
27
+
28
+ class Typography:
29
+ """Typography-related CSS classes from styles.css."""
30
+
31
+ h1: Final[str] = "eidos-h1"
32
+ h2: Final[str] = "eidos-h2"
33
+ h3: Final[str] = "eidos-h3"
34
+ h4: Final[str] = "eidos-h4"
35
+ h5: Final[str] = "eidos-h5"
36
+ h6: Final[str] = "eidos-h6"
37
+
38
+ class Semantic:
39
+ """Semantic HTML element CSS classes from styles.css."""
40
+
41
+ # Text formatting
42
+ strong: Final[str] = "eidos-strong"
43
+ i: Final[str] = "eidos-i"
44
+ small: Final[str] = "eidos-small"
45
+ del_: Final[str] = "eidos-del" # del is reserved keyword
46
+ abbr: Final[str] = "eidos-abbr"
47
+ var: Final[str] = "eidos-var"
48
+ mark: Final[str] = "eidos-mark"
49
+ time: Final[str] = "eidos-time"
50
+
51
+ # Code elements
52
+ code: Final[str] = "eidos-code"
53
+ pre: Final[str] = "eidos-pre"
54
+ kbd: Final[str] = "eidos-kbd"
55
+ samp: Final[str] = "eidos-samp"
56
+
57
+ # Structural elements
58
+ blockquote: Final[str] = "eidos-blockquote"
59
+ cite: Final[str] = "eidos-cite"
60
+ address: Final[str] = "eidos-address"
61
+ hr: Final[str] = "eidos-hr"
62
+
63
+ # Interactive elements
64
+ details: Final[str] = "eidos-details"
65
+ summary: Final[str] = "eidos-summary"
66
+ details_content: Final[str] = "eidos-details-content"
67
+
68
+ # Definition list
69
+ dl: Final[str] = "eidos-dl"
70
+ dt: Final[str] = "eidos-dt"
71
+ dd: Final[str] = "eidos-dd"
72
+
73
+ # Figure
74
+ figure: Final[str] = "eidos-figure"
75
+ figcaption: Final[str] = "eidos-figcaption"
76
+
77
+ # Create singleton instance for easy access
78
+ buttons = Buttons()
79
+ typography = Typography()
80
+ semantic = Semantic()
eidos/tags.py ADDED
@@ -0,0 +1,99 @@
1
+ from typing import Optional, Literal, Any, Union
2
+ import air
3
+ from . import styles
4
+ from .utils import stringify
5
+
6
+ def Button(*content: Any, class_: Optional[Union[str, list[str]]] = styles.buttons.primary, **kwargs: Any) -> air.Tag:
7
+ return air.Button(*content, class_=stringify(styles.buttons.base, class_), **kwargs)
8
+
9
+ def H1(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
10
+ return air.H1(*content, class_=stringify(styles.typography.h1, class_), **kwargs)
11
+
12
+ def H2(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
13
+ return air.H2(*content, class_=stringify(styles.typography.h2, class_), **kwargs)
14
+
15
+ def H3(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
16
+ return air.H3(*content, class_=stringify(styles.typography.h3, class_), **kwargs)
17
+
18
+ def H4(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
19
+ return air.H4(*content, class_=stringify(styles.typography.h4, class_), **kwargs)
20
+
21
+ def H5(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
22
+ return air.H5(*content, class_=stringify(styles.typography.h5, class_), **kwargs)
23
+
24
+ def H6(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
25
+ return air.H6(*content, class_=stringify(styles.typography.h6, class_), **kwargs)
26
+
27
+ def Body(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
28
+ return air.Body(*content, class_=stringify(styles.Theme.body, class_), **kwargs)
29
+
30
+ # Semantic HTML Elements
31
+
32
+ def Strong(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
33
+ return air.Strong(*content, class_=stringify(styles.semantic.strong, class_), **kwargs)
34
+
35
+ def I(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
36
+ return air.I(*content, class_=stringify(styles.semantic.i, class_), **kwargs)
37
+
38
+ def Small(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
39
+ return air.Small(*content, class_=stringify(styles.semantic.small, class_), **kwargs)
40
+
41
+ def Del(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
42
+ return air.Del(*content, class_=stringify(styles.semantic.del_, class_), **kwargs)
43
+
44
+ def Abbr(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
45
+ return air.Abbr(*content, class_=stringify(styles.semantic.abbr, class_), **kwargs)
46
+
47
+ def Var(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
48
+ return air.Var(*content, class_=stringify(styles.semantic.var, class_), **kwargs)
49
+
50
+ def Mark(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
51
+ return air.Mark(*content, class_=stringify(styles.semantic.mark, class_), **kwargs)
52
+
53
+ def Time(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
54
+ return air.Time(*content, class_=stringify(styles.semantic.time, class_), **kwargs)
55
+
56
+ def Code(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
57
+ return air.Code(*content, class_=stringify(styles.semantic.code, class_), **kwargs)
58
+
59
+ def Pre(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
60
+ return air.Pre(*content, class_=stringify(styles.semantic.pre, class_), **kwargs)
61
+
62
+ def Kbd(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
63
+ return air.Kbd(*content, class_=stringify(styles.semantic.kbd, class_), **kwargs)
64
+
65
+ def Samp(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
66
+ return air.Samp(*content, class_=stringify(styles.semantic.samp, class_), **kwargs)
67
+
68
+ def Blockquote(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
69
+ return air.Blockquote(*content, class_=stringify(styles.semantic.blockquote, class_), **kwargs)
70
+
71
+ def Cite(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
72
+ return air.Cite(*content, class_=stringify(styles.semantic.cite, class_), **kwargs)
73
+
74
+ def Address(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
75
+ return air.Address(*content, class_=stringify(styles.semantic.address, class_), **kwargs)
76
+
77
+ def Hr(class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
78
+ return air.Hr(class_=stringify(styles.semantic.hr, class_), **kwargs)
79
+
80
+ def Details(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
81
+ return air.Details(*content, class_=stringify(styles.semantic.details, class_), **kwargs)
82
+
83
+ def Summary(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
84
+ return air.Summary(*content, class_=stringify(styles.semantic.summary, class_), **kwargs)
85
+
86
+ def Dl(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
87
+ return air.Dl(*content, class_=stringify(styles.semantic.dl, class_), **kwargs)
88
+
89
+ def Dt(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
90
+ return air.Dt(*content, class_=stringify(styles.semantic.dt, class_), **kwargs)
91
+
92
+ def Dd(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
93
+ return air.Dd(*content, class_=stringify(styles.semantic.dd, class_), **kwargs)
94
+
95
+ def Figure(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
96
+ return air.Figure(*content, class_=stringify(styles.semantic.figure, class_), **kwargs)
97
+
98
+ def Figcaption(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
99
+ return air.Figcaption(*content, class_=stringify(styles.semantic.figcaption, class_), **kwargs)