simple_rsx/
lib.rs

1//! Simple RSX - A lightweight JSX-like library for Rust
2//!
3//! This crate provides a simple way to write HTML-like components in Rust using JSX-style syntax.
4//! It's perfect for building user interfaces or generating HTML content with a familiar, component-based approach.
5//!
6//! # Quick Start
7//!
8//! ```rust
9//! use simple_rsx::*;
10//!
11//! // Create a simple component
12//! let greeting = rsx!(
13//!     <div class="greeting">
14//!         <h1>Hello, World!</h1>
15//!         <p>Welcome to Simple RSX</p>
16//!     </div>
17//! );
18//!
19//! // Convert to HTML string
20//! println!("{}", greeting); // Outputs the HTML
21//! ```
22//!
23//! # Features
24//!
25//! - JSX-like syntax with the `rsx!` macro
26//! - Component-based architecture
27//! - Type-safe attributes and children
28//! - Easy conversion to HTML strings
29//! - Support for custom components
30//!
31//! # Custom Components
32//!
33//! ```rust
34//! use simple_rsx::*;
35//!
36//! #[derive(Default)]
37//! struct ButtonProps {
38//!     text: String,
39//!     children: Vec<Node>,
40//! }
41//!
42//! #[component]
43//! fn Button(props: ButtonProps) -> Node {
44//!     rsx!(
45//!         <button class="btn">
46//!             {props.text}
47//!             {props.children}
48//!         </button>
49//!     )
50//! }
51//! ```
52
53use indexmap::IndexMap;
54pub use simple_rsx_macros::{component, rsx};
55use std::fmt::Display;
56
57/// A trait for converting values into HTML attribute strings.
58///
59/// This trait is automatically implemented for any type that implements `ToString`,
60/// making it easy to use various types as attribute values.
61///
62/// # Example
63///
64/// ```rust
65/// use simple_rsx::*;
66///
67/// let element = rsx!(<div id="my-id" hidden={true} />);
68/// ```
69pub trait Attribute {
70    fn value(&self) -> String;
71}
72
73/// A trait for handling optional attribute values.
74///
75/// This trait is automatically implemented for `Option<T>` where T implements `ToString`.
76/// It allows for graceful handling of optional attributes, rendering them as empty strings when None.
77///
78/// # Example
79///
80/// ```rust
81/// use simple_rsx::*;
82///
83/// let maybe_title = Some("Hello".to_string());
84/// let element = rsx!(<div title={maybe_title} />);
85/// ```
86pub trait OptionAttribute {
87    fn value(&self) -> String;
88}
89
90impl<T: ToString> Attribute for T {
91    fn value(&self) -> String {
92        self.to_string()
93    }
94}
95
96impl<T: ToString> OptionAttribute for Option<T> {
97    fn value(&self) -> String {
98        match self {
99            Some(t) => t.to_string(),
100            None => String::new(),
101        }
102    }
103}
104
105/// Represents an HTML element with its tag name, attributes, and children.
106///
107/// Elements are the building blocks of the RSX tree structure. Each element
108/// can have attributes (like class, id, etc.) and can contain other elements
109/// or text nodes as children.
110///
111/// You typically won't create Elements directly, but rather use the `rsx!` macro:
112///
113/// ```rust
114/// use simple_rsx::*;
115///
116/// let element = rsx!(
117///     <div class="container">
118///         <p>Hello world!</p>
119///     </div>
120/// );
121/// ```
122#[derive(Clone)]
123pub struct Element {
124    tag: String,
125    attributes: IndexMap<String, String>,
126    children: Vec<Node>,
127}
128
129impl Element {
130    /// Creates a new Element node with the specified tag name.
131    ///
132    /// # Example
133    ///
134    /// ```rust
135    /// use simple_rsx::*;
136    ///
137    /// let element = Element::new("div");
138    /// assert!(matches!(element, Node::Element(_)));
139    /// ```
140    pub fn new(tag: &str) -> Node {
141        Node::Element(Element {
142            tag: tag.to_string(),
143            attributes: IndexMap::new(),
144            children: Vec::new(),
145        })
146    }
147
148    /// Sets an attribute on the element.
149    ///
150    /// # Example
151    ///
152    /// ```rust
153    /// use simple_rsx::*;
154    ///
155    /// let mut node = Element::new("div");
156    /// let mut element = node.as_element_mut().unwrap();
157    /// element.set_attribute("class", "container");
158    /// ```
159    pub fn set_attribute(&mut self, name: &str, value: impl Attribute) {
160        self.attributes.insert(name.to_string(), value.value());
161    }
162
163    /// Adds a child node to this element.
164    ///
165    /// # Example
166    ///
167    /// ```rust
168    /// use simple_rsx::*;
169    ///
170    /// let mut parent_node = Element::new("div");
171    /// let mut parent = parent_node.as_element_mut().unwrap();
172    /// parent.append_child(Element::new("p"));
173    /// ```
174    pub fn append_child(&mut self, node: Node) {
175        self.children.push(node);
176    }
177}
178
179impl Node {
180    /// Attempts to get a mutable reference to the underlying Element if this node is an Element.
181    ///
182    /// Returns None if the node is not an Element (e.g., if it's Text or Fragment).
183    pub fn as_element_mut(&mut self) -> Option<&mut Element> {
184        match self {
185            Node::Element(el) => Some(el),
186            _ => None,
187        }
188    }
189
190    /// Adds a child node if this node is an Element.
191    ///
192    /// This method has no effect if the node is not an Element.
193    pub fn append_child(&mut self, node: Node) {
194        if let Node::Element(el) = self {
195            el.children.push(node);
196        }
197    }
198}
199
200/// A trait for creating reusable components.
201///
202/// Components are the heart of RSX's reusability model. They allow you to create
203/// custom elements with their own logic and state.
204///
205/// # Example
206///
207/// ```rust
208/// use simple_rsx::*;
209///
210/// struct Card;
211/// #[derive(Default)]
212/// struct CardProps {
213///     title: String,
214///     children: Vec<Node>,
215/// }
216///
217/// impl Component for Card {
218///     type Props = CardProps;
219///     fn render(&mut self, props: Self::Props) -> Node {
220///         rsx!(
221///             <div class="card">
222///                 <h2>{props.title}</h2>
223///                 <div class="card-content">{props.children}</div>
224///             </div>
225///         )
226///     }
227/// }
228/// ```
229pub trait Component {
230    /// The type of props this component accepts
231    type Props;
232
233    /// Renders the component with the given props
234    fn render(&mut self, props: Self::Props) -> Node;
235}
236
237/// Implements Component for functions that take props and return a Node.
238///
239/// This allows you to use simple functions as components.
240///
241/// # Example
242///
243/// ```rust
244/// use simple_rsx::*;
245///
246/// fn Button(text: String) -> Node {
247///     rsx!(<button>{text}</button>)
248/// }
249/// ```
250impl<P> Component for fn(P) -> Node {
251    type Props = P;
252    fn render(&mut self, props: Self::Props) -> Node {
253        self(props.into())
254    }
255}
256
257/// Represents a node in the RSX tree.
258///
259/// Nodes are the fundamental building blocks of RSX. They can be:
260/// - Elements (like `<div>` or `<p>`)
261/// - Text content
262/// - Fragments (groups of nodes)
263/// - Comments
264///
265/// # Example
266///
267/// ```rust
268/// use simple_rsx::*;
269///
270/// let text_node = Node::Text("Hello".to_string());
271/// let element_node = Element::new("div");
272/// let fragment = Node::Fragment(vec![text_node, element_node]);
273/// ```
274#[derive(Clone)]
275pub enum Node {
276    /// An HTML element with a tag name, attributes, and children
277    Element(Element),
278    /// Plain text content
279    Text(String),
280    /// A group of nodes without a wrapper element
281    Fragment(Vec<Node>),
282    /// An HTML comment
283    Comment(String),
284}
285
286impl From<String> for Node {
287    fn from(value: String) -> Self {
288        Node::Text(value)
289    }
290}
291
292impl From<&String> for Node {
293    fn from(value: &String) -> Self {
294        Node::Text(value.to_string())
295    }
296}
297
298impl From<&str> for Node {
299    fn from(value: &str) -> Self {
300        Node::Text(value.to_string())
301    }
302}
303
304impl From<&&str> for Node {
305    fn from(value: &&str) -> Self {
306        Node::Text(value.to_string())
307    }
308}
309
310impl<T: ToString> From<Vec<T>> for Node {
311    fn from(value: Vec<T>) -> Self {
312        Node::Fragment(
313            value
314                .into_iter()
315                .map(|t| Node::Text(t.to_string()))
316                .collect(),
317        )
318    }
319}
320
321impl<T: ToString> From<Option<T>> for Node {
322    fn from(value: Option<T>) -> Self {
323        match value {
324            Some(t) => Node::Text(t.to_string()),
325            None => Node::Text("".to_string()),
326        }
327    }
328}
329
330impl From<&Vec<String>> for Node {
331    fn from(value: &Vec<String>) -> Self {
332        Node::Fragment(value.iter().map(|s| Node::Text(s.to_string())).collect())
333    }
334}
335
336impl From<i32> for Node {
337    fn from(value: i32) -> Self {
338        Node::Text(value.to_string())
339    }
340}
341
342impl From<u32> for Node {
343    fn from(value: u32) -> Self {
344        Node::Text(value.to_string())
345    }
346}
347
348impl From<u64> for Node {
349    fn from(value: u64) -> Self {
350        Node::Text(value.to_string())
351    }
352}
353
354impl FromIterator<u32> for Node {
355    fn from_iter<T: IntoIterator<Item = u32>>(iter: T) -> Self {
356        let mut result = Vec::new();
357        for i in iter {
358            result.push(Node::Text(i.to_string()));
359        }
360        Node::Fragment(result)
361    }
362}
363
364impl FromIterator<u64> for Node {
365    fn from_iter<T: IntoIterator<Item = u64>>(iter: T) -> Self {
366        let mut result = Vec::new();
367        for i in iter {
368            result.push(Node::Text(i.to_string()));
369        }
370        Node::Fragment(result)
371    }
372}
373
374impl FromIterator<i32> for Node {
375    fn from_iter<T: IntoIterator<Item = i32>>(iter: T) -> Self {
376        let mut result = Vec::new();
377        for i in iter {
378            result.push(Node::Text(i.to_string()));
379        }
380        Node::Fragment(result)
381    }
382}
383
384impl From<f32> for Node {
385    fn from(value: f32) -> Self {
386        Node::Text(value.to_string())
387    }
388}
389
390impl From<bool> for Node {
391    fn from(value: bool) -> Self {
392        Node::Text(value.to_string())
393    }
394}
395
396impl<I, F, R> From<std::iter::Map<I, F>> for Node
397where
398    I: Iterator,
399    F: FnMut(I::Item) -> R,
400    R: Into<Node>,
401    Vec<Node>: FromIterator<R>,
402{
403    fn from(iter: std::iter::Map<I, F>) -> Self {
404        let nodes: Vec<Node> = iter.collect();
405        Node::from(nodes)
406    }
407}
408
409impl Display for Node {
410    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411        match self {
412            Node::Element(el) => {
413                write!(f, "<{}", el.tag)?;
414                for (key, value) in &el.attributes {
415                    write!(f, " {}=\"{}\"", key, value)?;
416                }
417                write!(f, ">")?;
418                for child in &el.children {
419                    write!(f, "{}", child)?;
420                }
421                write!(f, "</{}>", el.tag)?;
422                Ok(())
423            }
424            Node::Text(text) => {
425                write!(f, "{}", text)?;
426                Ok(())
427            }
428            Node::Fragment(nodes) => {
429                for node in nodes {
430                    write!(f, "{}", node)?;
431                }
432                Ok(())
433            }
434            Node::Comment(comment) => {
435                write!(f, "<!--{}-->", comment)?;
436                Ok(())
437            }
438        }
439    }
440}
441
442macro_rules! derive_elements {
443    (
444        $(
445            $(#[$tag_meta:meta])*
446            $tag:ident {
447                $(
448                    $(#[$attr_meta:meta])*
449                    $attr_name:ident : $attr_value:ty
450                ),* $(,)?
451            }
452        )*
453    ) => {
454        $(
455            #[allow(non_camel_case_types)]
456            $(#[$tag_meta])*
457            pub struct $tag;
458
459            paste::paste! {
460                #[derive(Default)]
461                #[allow(non_snake_case)]
462                pub struct [<HTML $tag:camel Element Props>] {
463                    // Global HTML attributes
464
465                    /// The child nodes of the element
466                    pub children: Vec<Node>,
467
468                    /// The id attribute specifies a unique id for an HTML element
469                    pub id: String,
470
471                    /// A unique key to identify the element
472                    pub key: String,
473
474                    /// The class attribute specifies one or more class names for an HTML element
475                    pub class: String,
476
477                    /// The style attribute specifies an inline CSS style for an element
478                    pub style: String,
479
480                    /// The title attribute specifies extra information about an element (displayed as a tooltip)
481                    pub title: Option<String>,
482                    /// The width attribute specifies the width of the image
483                    pub width: Option<String>,
484                    /// The height attribute specifies the height of the image
485                    pub height: Option<String>,
486
487                    /// Specifies whether an element is draggable or not
488                    pub draggable: bool,
489
490                    /// Specifies visibility of an element (hidden or visible)
491                    pub hidden: bool,
492
493                    /// Specifies a shortcut key to activate/focus an element
494                    pub accesskey: String,
495
496                    /// Specifies whether the content of an element is editable or not
497                    pub contenteditable: bool,
498
499                    /// Specifies the text direction for the content in an element
500                    pub dir: String,
501
502                    /// Specifies the tabbing order of an element (when the tab button is used)
503                    pub tabindex: Option<i32>,
504
505                    /// Specifies whether the element is to have its spelling and grammar checked
506                    pub spellcheck: bool,
507
508                    /// Specifies the language of the element's content
509                    pub lang: String,
510
511                    /// Specifies whether an element is translateable or not
512                    pub translate: bool,
513
514                    /// Controls whether and how text input is automatically capitalized
515                    pub autocapitalize: String,
516
517                    /// Specifies an inline CSS style for an element
518                    pub role: String,
519
520                    // ARIA Accessibility attributes
521
522                    /// Identifies the current element within a set
523                    pub aria_current: String,
524
525                    /// Defines a string value that labels the current element
526                    pub aria_label: Option<String>,
527
528                    /// Identifies the element that labels the current element
529                    pub aria_labelledby: Option<String>,
530
531                    /// Identifies the element that describes the current element
532                    pub aria_describedby: Option<String>,
533
534                    /// Indicates whether an element is expanded or collapsed
535                    pub aria_expanded: bool,
536
537                    /// Indicates the element that represents the current item within a container or set
538                    pub aria_selected: bool,
539
540                    /// Indicates whether the element is checked, unchecked, or represents mixed mode
541                    pub aria_checked: String,
542
543                    /// Indicates whether an element and its subtree are hidden
544                    pub aria_hidden: bool,
545
546                    /// Indicates the availability and type of interactive popup element
547                    pub aria_haspopup: String,
548
549                    /// Defines an element's role
550                    pub aria_role: String,
551
552                    // Element specific attributes
553                    $(
554                        pub $attr_name: $attr_value,
555                    )*
556                }
557
558                impl [<HTML $tag:camel Element Props>] {
559                    fn to_attributes(&self) -> IndexMap<String, String> {
560                        #[allow(unused_mut)]
561                        let mut attributes = IndexMap::new();
562                        $(
563                            if !self.$attr_name.value().is_empty() {
564                                let mut key = stringify!($attr_name);
565                                if let Some(last_char) = key.chars().last() {
566                                    if last_char == '_' {
567                                        key = &key[..key.len() - 1];
568                                    }
569                                }
570                                attributes.insert(key.replace('_', "-"), self.$attr_name.value());
571                            }
572                        )*
573                        if !self.id.value().is_empty() {
574                            attributes.insert("id".to_string(), self.id.value());
575                        }
576                        if !self.class.value().is_empty() {
577                            attributes.insert("class".to_string(), self.class.value());
578                        }
579                        if !self.style.value().is_empty() {
580                            attributes.insert("style".to_string(), self.style.value());
581                        }
582                        if !self.title.value().is_empty() {
583                            attributes.insert("title".to_string(), self.title.value());
584                        }
585                        if self.draggable {
586                            attributes.insert("draggable".to_string(), "true".to_string());
587                        }
588                        if self.hidden {
589                            attributes.insert("hidden".to_string(), "true".to_string());
590                        }
591                        if !self.accesskey.value().is_empty() {
592                            attributes.insert("accesskey".to_string(), self.accesskey.value());
593                        }
594                        if self.contenteditable {
595                            attributes.insert("contenteditable".to_string(), "true".to_string());
596                        }
597                        if !self.dir.value().is_empty() {
598                            attributes.insert("dir".to_string(), self.dir.value());
599                        }
600                        if let Some(tabindex) = self.tabindex {
601                            attributes.insert("tabindex".to_string(), tabindex.to_string());
602                        }
603                        if self.spellcheck {
604                            attributes.insert("spellcheck".to_string(), "true".to_string());
605                        }
606                        if !self.lang.value().is_empty() {
607                            attributes.insert("lang".to_string(), self.lang.value());
608                        }
609                        if self.translate {
610                            attributes.insert("translate".to_string(), "true".to_string());
611                        }
612                        if !self.autocapitalize.value().is_empty() {
613                            attributes.insert("autocapitalize".to_string(), self.autocapitalize.value());
614                        }
615                        if !self.role.value().is_empty() {
616                            attributes.insert("role".to_string(), self.role.value());
617                        }
618                        if !self.aria_current.value().is_empty() {
619                            attributes.insert("aria-current".to_string(), self.aria_current.value());
620                        }
621                        if !self.aria_label.value().is_empty() {
622                            attributes.insert("aria-label".to_string(), self.aria_label.value());
623                        }
624                        if !self.aria_labelledby.value().is_empty() {
625                            attributes.insert("aria-labelledby".to_string(), self.aria_labelledby.value());
626                        }
627                        if !self.aria_describedby.value().is_empty() {
628                            attributes.insert("aria-describedby".to_string(), self.aria_describedby.value());
629                        }
630                        if self.aria_expanded {
631                            attributes.insert("aria-expanded".to_string(), "true".to_string());
632                        }
633                        if self.aria_selected {
634                            attributes.insert("aria-selected".to_string(), "true".to_string());
635                        }
636                        if !self.aria_checked.value().is_empty() {
637                            attributes.insert("aria-checked".to_string(), self.aria_checked.value());
638                        }
639                        if self.aria_hidden {
640                            attributes.insert("aria-hidden".to_string(), "true".to_string());
641                        }
642                        if !self.aria_haspopup.value().is_empty() {
643                            attributes.insert("aria-haspopup".to_string(), self.aria_haspopup.value());
644                        }
645                        if !self.aria_role.value().is_empty() {
646                            attributes.insert("aria-role".to_string(), self.aria_role.value());
647                        }
648
649                        attributes
650                    }
651                }
652
653                impl Component for $tag {
654                    type Props = [<HTML $tag:camel Element Props>];
655
656                    fn render(&mut self, props: Self::Props) -> Node {
657                        Node::Element(Element {
658                            tag: stringify!($tag).to_string(),
659                            attributes: props.to_attributes(),
660                            children: props.children,
661                        })
662                    }
663                }
664            }
665        )*
666    };
667}
668
669derive_elements! {
670    /// HTML `<html>` element - Root element of an HTML document
671    html {
672    }
673    /// HTML `<body>` element - Represents the content of an HTML document
674    ///
675    /// Example:
676    ///
677    /// ```<body>Content goes here</body>```
678    body {
679    }
680    /// HTML `<head>` element - Contains metadata about the document
681    ///
682    /// Example:
683    ///
684    /// ```<head><title>Document Title</title></head>```
685    head {
686    }
687    /// HTML `<title>` element - Defines the title of the document
688    ///
689    /// Example:
690    ///
691    /// ```<title>Document Title</title>```
692    title {
693    }
694    /// HTML `<meta` element - Provides metadata about the document
695    ///
696    /// Example:
697    ///
698    /// ```<meta charset="UTF-8">```
699    meta {
700        /// The character encoding of the document
701        charset: Option<String>,
702        /// The HTTP response status code
703        http_equiv: Option<String>,
704        /// The content of the document
705        content: Option<String>,
706        /// The name of the metadata
707        name: String,
708        /// The property of the metadata
709        property: Option<String>,
710    }
711    /// HTML `<style>` element - Defines style information for a document
712    ///
713    /// Example:
714    ///
715    /// ```<style>body { background-color: #f0f0f0; }</style>```
716    style {
717    }
718    /// HTML `<script>` element - Embeds executable code or data
719    ///
720    /// Example:
721    ///
722    /// ```<script src="script.js"></script>```
723    script {
724    }
725    /// HTML `<link>` element - Specifies relationships between the current document and an external resource
726    ///
727    /// Example:
728    ///
729    /// ```<link rel="stylesheet" href="style.css">```
730    link {
731        /// The relationship between the current document and the linked resource
732        rel: String,
733        /// The URL of the linked resource
734        href: String,
735        /// The type of the linked resource
736        type_: String,
737    }
738    /// HTML `<div>` element - Container element for grouping and styling content
739    ///
740    /// Example:
741    ///
742    /// ```<div class="container">Content goes here</div>```
743    div {
744    }
745
746    /// HTML `<p>` element - Represents a paragraph of text
747    ///
748    /// Example:
749    ///
750    /// ```<p>This is a paragraph of text.</p>```
751    p {
752    }
753
754    /// HTML `<span>` element - Inline container for targeting text with styles
755    ///
756    /// Example:
757    ///
758    /// ```<span class="highlight">Highlighted text</span>```
759    span {
760    }
761
762    /// HTML `<a>` element - Creates a hyperlink to other web pages or resources
763    ///
764    /// Example:
765    ///
766    /// ```<a href="https://example.com" target="_blank">Visit Example</a>```
767    a {
768        /// The href attribute specifies the URL of the page the link goes to
769        /// Example: href="https://example.com"
770        href: String,
771        /// The target attribute specifies where to open the linked document
772        /// Example: target="_blank" (opens in new tab)
773        target: String,
774        /// The rel attribute specifies the relationship between the current document and the linked document
775        /// Example: rel="nofollow" (tells search engines not to follow this link)
776        rel: String,
777        /// The download attribute indicates the browser to download the URL instead of navigating
778        /// Example: download="filename.pdf"
779        download: String,
780        /// The hreflang attribute specifies the language of the linked document
781        /// Example: hreflang="en" (English)
782        hreflang: String,
783        /// The type attribute specifies the media type of the linked document
784        /// Example: type="text/html"
785        type_: String,
786        /// The media attribute specifies what media/device the linked document is optimized for
787        /// Example: media="print" (for print stylesheets)
788        media: String,
789        /// The referrerpolicy attribute specifies which referrer information to send
790        /// Example: referrerpolicy="no-referrer"
791        referrerpolicy: String,
792        /// The ping attribute specifies URLs to be notified when the link is followed
793        /// Example: ping="https://example.com/track"
794        ping: String,
795    }
796
797    /// HTML <h1> element - First level heading (most important)
798    ///
799    /// Example:
800    ///
801    /// ```<h1>Main Page Title</h1>```
802    h1 {
803    }
804
805    /// HTML <h2> element - Second level heading
806    ///
807    /// Example:
808    ///
809    /// ```<h2>Section Heading</h2>```
810    h2 {
811    }
812
813    /// HTML <h3> element - Third level heading
814    ///
815    /// Example:
816    ///
817    /// ```<h3>Subsection Heading</h3>```
818    h3 {
819    }
820
821    /// HTML <h4> element - Fourth level heading
822    ///
823    /// Example:
824    ///
825    /// ```<h4>Sub-subsection Heading</h4>```
826    h4 {
827    }
828
829    /// HTML <h5> element - Fifth level heading
830    ///
831    /// Example:
832    ///
833    /// ```<h5>Minor Heading</h5>```
834    h5 {
835    }
836
837    /// HTML <h6> element - Sixth level heading (least important)
838    ///
839    /// Example:
840    ///
841    /// ```<h6>Fine Detail Heading</h6>```
842    h6 {
843    }
844
845    /// HTML `<img>` element - Embeds an image into the document
846    ///
847    /// Example:
848    ///
849    /// ```<img src="image.jpg" alt="Description of image">```
850    img {
851        /// The src attribute specifies the URL/path to the image
852        /// Example: src="images/logo.png"
853        src: String,
854        /// The alt attribute provides alternative text for screen readers and if image fails to load
855        /// Example: alt="Company Logo"
856        alt: String,
857        /// The loading attribute indicates how the browser should load the image
858        /// Example: loading="lazy" (defers loading until it's near viewport)
859        loading: String,
860    }
861
862    /// HTML `<br>` element - Produces a line break in text
863    ///
864    /// Example:
865    ///
866    /// ```<br>```
867    br {}
868
869    /// HTML `<hr>` element - Creates a horizontal rule (divider)
870    ///
871    /// Example:
872    ///
873    /// ```<hr>```
874    hr {
875    }
876
877    /// HTML `<ul>` element - Unordered list with bullet points
878    ///
879    /// Example:
880    ///
881    /// ```<ul><li>Item 1</li><li>Item 2</li></ul>```
882    ul {
883        /// The type attribute specifies the bullet style (disc, circle, square)
884        /// Example: type="square"
885        type_: String,
886    }
887
888    /// HTML `<li>` element - List item within ordered or unordered lists
889    ///
890    /// Example:
891    ///
892    /// ```<li>List item content</li>```
893    li {
894        /// The value attribute specifies the start value of the list item (for ol)
895        /// Example: value="3" (starts this item at number 3)
896        value: Option<i32>,
897    }
898
899    /// HTML `<ol>` element - Ordered (numbered) list
900    ///
901    /// Example:
902    ///
903    /// ```<ol start="5" type="A"><li>Item E</li><li>Item F</li></ol>```
904    ol {
905        /// The type attribute specifies the numbering type (1, A, a, I, i)
906        /// Example: type="A" (uses capital letters)
907        type_: String,
908        /// The start attribute specifies the start value of the list
909        /// Example: start="5" (starts counting from 5)
910        start: i32,
911        /// The reversed attribute specifies that list should be in descending order
912        /// Example: reversed (counts down instead of up)
913        reversed: bool,
914    }
915
916    /// HTML `<table>` element - Creates a data table with rows and columns
917    ///
918    /// Example:
919    ///
920    /// ```<table border="1"><tr><th>Header</th></tr><tr><td>Data</td></tr></table>```
921    table {
922        /// The border attribute specifies the width of the border around the table
923        /// Example: border="1" (1 pixel border)
924        border: i32,
925        /// The cellpadding attribute specifies the space between cell content and borders
926        /// Example: cellpadding="5" (5 pixels of padding)
927        cellpadding: i32,
928        /// The cellspacing attribute specifies the space between cells
929        /// Example: cellspacing="2" (2 pixels between cells)
930        cellspacing: i32,
931    }
932
933    /// HTML `<tr>` element - Table row container
934    ///
935    /// Example:
936    ///
937    /// ```<tr><td>Cell 1</td><td>Cell 2</td></tr>```
938    tr {
939    }
940
941    /// HTML `<td>` element - Table data cell
942    ///
943    /// Example:
944    ///
945    /// ```<td colspan="2">This cell spans two columns</td>```
946    td {
947        /// The colspan attribute specifies how many columns a cell should span
948        /// Example: colspan="3" (cell spans 3 columns)
949        colspan: i32,
950        /// The rowspan attribute specifies how many rows a cell should span
951        /// Example: rowspan="2" (cell spans 2 rows)
952        rowspan: i32,
953        /// The headers attribute associates data cells with header cells
954        /// Example: headers="col1 row1" (associates with those header IDs)
955        headers: String,
956        /// The scope attribute specifies whether header cells are for rows or columns
957        /// Example: scope="col" (header applies to whole column)
958        scope: String,
959    }
960
961    /// HTML `<th>` element - Table header cell
962    ///
963    /// Example:
964    ///
965    /// ```<th scope="col">Column Header</th>```
966    th {
967        /// The colspan attribute specifies how many columns a cell should span
968        /// Example: colspan="3" (header spans 3 columns)
969        colspan: i32,
970        /// The rowspan attribute specifies how many rows a cell should span
971        /// Example: rowspan="2" (header spans 2 rows)
972        rowspan: i32,
973        /// The headers attribute associates data cells with header cells
974        /// Example: headers="col1 row1" (associates with those header IDs)
975        headers: String,
976        /// The scope attribute specifies whether the header cell is for a row, column, etc.
977        /// Example: scope="row" (header applies to whole row)
978        scope: String,
979    }
980
981    /// HTML `<tbody>` element - Groups body content in a table
982    ///
983    /// Example:
984    ///
985    /// ```<table><tbody><tr><td>Data</td></tr></tbody></table>```
986    tbody {
987    }
988
989    /// HTML `<thead>` element - Groups header content in a table
990    ///
991    /// Example:
992    ///
993    /// ```<table><thead><tr><th>Header</th></tr></thead><tbody>...</tbody></table>```
994    thead {
995    }
996
997    /// HTML `<tfoot>` element - Groups footer content in a table
998    ///
999    /// Example:
1000    ///
1001    /// ```<table><thead>...</thead><tbody>...</tbody><tfoot><tr><td>Summary</td></tr></tfoot></table>```
1002    tfoot {
1003    }
1004
1005    /// HTML `<form>` element - Container for interactive inputs to collect user data
1006    ///
1007    /// Example:
1008    ///
1009    /// ```<form action="/submit" method="post"><input type="text"><button type="submit">Submit</button></form>```
1010    form {
1011        /// The action attribute specifies where to send form data when submitted
1012        ///
1013        /// Example: action="/process-form.php"
1014        action: String,
1015        /// The method attribute specifies HTTP method for sending data (GET/POST)
1016        ///
1017        /// Example: method="post" (sends data in request body)
1018        method: String,
1019        /// The target attribute specifies where to display the response
1020        ///
1021        /// Example: target="_blank" (opens response in new tab)
1022        target: String,
1023        /// The enctype attribute specifies how form data should be encoded
1024        ///
1025        /// Example: enctype="multipart/form-data" (needed for file uploads)
1026        enctype: String,
1027        /// The novalidate attribute disables browser's built-in form validation
1028        ///
1029        /// Example: novalidate (skips validation)
1030        novalidate: bool,
1031        /// The autocomplete attribute controls browser autofill behavior
1032        ///
1033        /// Example: autocomplete="off" (disables autofill)
1034        autocomplete: String,
1035        /// The accept attribute specifies file types the server accepts (for file inputs)
1036        ///
1037        /// Example: accept=".jpg,.png" (accepts only those image formats)
1038        accept: String,
1039        /// Example: name="contact-form"
1040        name: String,
1041    }
1042
1043    /// HTML `<input>` element - Creates interactive controls for forms
1044    ///
1045    /// Example:
1046    ///
1047    /// ```<input type="text" placeholder="Enter your name" required>```
1048    input {
1049        /// The type attribute specifies the input type (text, password, email, etc.)
1050        ///
1051        /// Example: type="email" (validates as email address)
1052        type_: String,
1053        /// The placeholder attribute shows hint text when field is empty
1054        ///
1055        /// Example: placeholder="Enter your email"
1056        placeholder: String,
1057        /// The required attribute makes the field mandatory
1058        ///
1059        /// Example: required (field must be filled)
1060        required: bool,
1061        /// The value attribute specifies the default/current value
1062        ///
1063        /// Example: value="Default text"
1064        value: String,
1065        /// The name attribute specifies the name of the input (for form submission)
1066        ///
1067        /// Example: name="email"
1068        name: String,
1069        /// The disabled attribute disables the input
1070        ///
1071        /// Example: disabled (user cannot interact with input)
1072        disabled: bool,
1073        /// The readonly attribute makes the input read-only
1074        ///
1075        /// Example: readonly (user cannot modify but can focus/select)
1076        readonly: bool,
1077        /// The min attribute specifies minimum value for number/date inputs
1078        ///
1079        /// Example: min="1" (number input minimum value)
1080        min: String,
1081        /// The max attribute specifies maximum value for number/date inputs
1082        ///
1083        /// Example: max="100" (number input maximum value)
1084        max: String,
1085        /// The pattern attribute specifies a regex pattern for validation
1086        ///
1087        /// Example: pattern="[0-9]{3}" (requires exactly 3 digits)
1088        pattern: String,
1089        /// The autocomplete attribute controls browser autofill for this field
1090        ///
1091        /// Example: autocomplete="current-password"
1092        autocomplete: String,
1093    }
1094
1095    /// HTML `<textarea>` element - Multi-line text input control
1096    ///
1097    /// Example:
1098    ///
1099    /// ```<textarea rows="4" cols="50" placeholder="Your message here"></textarea>```
1100    textarea {
1101        /// The placeholder attribute shows hint text when field is empty
1102        /// Example: placeholder="Enter your comments"
1103        placeholder: String,
1104        /// The required attribute makes the field mandatory
1105        /// Example: required (must be filled before submission)
1106        required: bool,
1107        /// The value attribute specifies the default/current text content
1108        /// Example: value="Default text in the textarea"
1109        value: String,
1110        /// The rows attribute specifies visible number of text lines
1111        /// Example: rows="10" (shows 10 lines of text)
1112        rows: i32,
1113        /// The cols attribute specifies visible width in average characters
1114        /// Example: cols="40" (about 40 characters wide)
1115        cols: i32,
1116        /// The name attribute specifies the name of the textarea (for form submission)
1117        /// Example: name="comments"
1118        name: String,
1119        /// The disabled attribute disables the textarea
1120        /// Example: disabled (user cannot interact)
1121        disabled: bool,
1122        /// The readonly attribute makes the textarea read-only
1123        /// Example: readonly (user cannot modify but can focus/select)
1124        readonly: bool,
1125        /// The maxlength attribute specifies maximum character count
1126        /// Example: maxlength="500" (limits to 500 characters)
1127        maxlength: i32,
1128    }
1129
1130    /// HTML `<button>` element - Clickable button control
1131    ///
1132    /// Example:
1133    ///
1134    /// ```<button type="submit">Click Me</button>```
1135    button {
1136        /// The type attribute specifies button function (submit, reset, button)
1137        /// Example: type="submit" (submits the form)
1138        type_: String,
1139        /// The value attribute specifies the value associated with the button
1140        /// Example: value="btn1" (for form processing)
1141        value: String,
1142        /// The disabled attribute disables the button
1143        /// Example: disabled (button cannot be clicked)
1144        disabled: bool,
1145        /// The name attribute specifies the name of the button (for form submission)
1146        /// Example: name="submit-button"
1147        name: String,
1148        /// The formaction attribute overrides form's action for this button
1149        /// Example: formaction="/alternative-submit"
1150        formaction: String,
1151        /// The formmethod attribute overrides form's method for this button
1152        /// Example: formmethod="get"
1153        formmethod: String,
1154    }
1155
1156    /// HTML `<select>` element - Dropdown selection list
1157    ///
1158    /// Example:
1159    ///
1160    /// ```<select><option value="1">Option 1</option><option value="2">Option 2</option></select>```
1161    select {
1162        /// The multiple attribute allows selecting multiple options
1163        /// Example: multiple (user can select multiple items)
1164        multiple: bool,
1165        /// The disabled attribute disables the dropdown
1166        /// Example: disabled (user cannot interact)
1167        disabled: bool,
1168        /// The value attribute specifies the selected value
1169        /// Example: value="option2" (preselects this option)
1170        value: String,
1171        /// The name attribute specifies the name of the select (for form submission)
1172        /// Example: name="country"
1173        name: String,
1174        /// The size attribute specifies number of visible options
1175        /// Example: size="5" (shows 5 options at once)
1176        size: i32,
1177        /// The required attribute makes selection mandatory
1178        /// Example: required (user must select an option)
1179        required: bool,
1180    }
1181
1182    /// HTML `<option>` element - Defines option in a select dropdown
1183    ///
1184    /// Example:
1185    ///
1186    /// ```<option value="blue" selected>Blue</option>```
1187    option {
1188        /// The value attribute specifies the value to be sent to server
1189        /// Example: value="NY" (value sent when this option is selected)
1190        value: String,
1191        /// The selected attribute preselects this option when page loads
1192        /// Example: selected (this option is selected by default)
1193        selected: bool,
1194        /// The disabled attribute makes this option unselectable
1195        /// Example: disabled (cannot be chosen)
1196        disabled: bool,
1197    }
1198
1199    /// HTML `<label>` element - Caption for a form control
1200    ///
1201    /// Example:
1202    ///
1203    /// ```<label for="username">Username:</label><input id="username">```
1204    label {
1205        /// The for attribute connects the label to a form control by ID
1206        /// Example: for="email" (associates with input having id="email")
1207        for_: String,
1208    }
1209
1210    /// HTML `<iframe>` element - Embeds another document within the current HTML document
1211    ///
1212    /// Example:
1213    ///
1214    /// ```<iframe src="https://example.com" title="Example Site"></iframe>```
1215    iframe {
1216        /// The src attribute specifies the URL of the embedded document
1217        /// Example: src="https://maps.google.com"
1218        src: String,
1219        /// The frameborder attribute specifies whether to display a border
1220        /// Example: frameborder="0" (no border)
1221        frameborder: String,
1222        /// The allow attribute specifies features allowed in the iframe
1223        /// Example: allow="camera; microphone" (permits access to these devices)
1224        allow: String,
1225        /// The allowfullscreen attribute allows iframe content to go fullscreen
1226        /// Example: allowfullscreen (allows fullscreen mode)
1227        allowfullscreen: bool,
1228        /// The sandbox attribute restricts iframe capabilities for security
1229        /// Example: sandbox="allow-scripts" (only allows scripts to run)
1230        sandbox: String,
1231    }
1232
1233    /// HTML `<video>` element - Embeds video content in the document
1234    ///
1235    /// Example:
1236    ///
1237    /// ```<video src="movie.mp4" controls width="500">Video not supported</video>```
1238    video {
1239        /// The src attribute specifies URL/path of the video
1240        /// Example: src="videos/intro.mp4"
1241        src: String,
1242        /// The controls attribute displays video playback controls
1243        /// Example: controls (shows play/pause/volume controls)
1244        controls: bool,
1245        /// The autoplay attribute starts playing video automatically
1246        /// Example: autoplay (video plays when page loads)
1247        autoplay: bool,
1248        /// The loop attribute makes the video replay when finished
1249        /// Example: loop (continuously replays)
1250        loop_: bool,
1251        /// The poster attribute specifies an image shown before video plays
1252        /// Example: poster="thumbnail.jpg"
1253        poster: String,
1254        /// The muted attribute mutes the audio by default
1255        /// Example: muted (starts with no sound)
1256        muted: bool,
1257        /// The preload attribute hints how to preload the video
1258        /// Example: preload="auto" (preload entire video)
1259        preload: String,
1260        /// The playsinline attribute plays inline on iOS (instead of fullscreen)
1261        /// Example: playsinline (important for iPhone users)
1262        playsinline: bool,
1263    }
1264
1265    /// HTML `<audio>` element - Embeds sound content in the document
1266    ///
1267    /// Example:
1268    ///
1269    /// ```<audio src="song.mp3" controls>Audio not supported</audio>```
1270    audio {
1271        /// The src attribute specifies URL/path of the audio file
1272        /// Example: src="audio/background-music.mp3"
1273        src: String,
1274        /// The controls attribute displays audio playback controls
1275        /// Example: controls (shows play/pause/volume controls)
1276        controls: bool,
1277        /// The autoplay attribute starts playing audio automatically
1278        /// Example: autoplay (audio plays when page loads)
1279        autoplay: bool,
1280        /// The loop attribute makes the audio replay when finished
1281        /// Example: loop (continuously replays)
1282        loop_: bool,
1283        /// The muted attribute mutes the audio by default
1284        /// Example: muted (starts with no sound)
1285        muted: bool,
1286        /// The preload attribute hints how to preload the audio
1287        /// Example: preload="none" (doesn't preload)
1288        preload: String,
1289    }
1290
1291    /// HTML `<source>` element - Defines media resources for video/audio elements
1292    ///
1293    /// Example:
1294    ///
1295    /// ```<video><source src="movie.mp4" type="video/mp4"><source src="movie.webm" type="video/webm"></video>```
1296    source {
1297        /// The src attribute specifies URL/path of the media resource
1298        /// Example: src="audio/song.ogg"
1299        src: String,
1300        /// The type attribute specifies the MIME type of the resource
1301        /// Example: type="video/webm" (defines file format)
1302        type_: String,
1303        /// The media attribute specifies for which media the resource is intended
1304        /// Example: media="(min-width: 600px)" (responsive resources)
1305        media: String,
1306    }
1307
1308    /// HTML `<canvas>` element - Container for graphics rendered with JavaScript
1309    ///
1310    /// Example:
1311    ///
1312    /// ```<canvas id="myCanvas" width="200" height="100">Your browser does not support canvas</canvas>```
1313    canvas {
1314    }
1315
1316    /// HTML `<svg>` element - Container for SVG graphics
1317    ///
1318    /// Example:
1319    ///
1320    /// ```<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="red" /></svg>```
1321    svg {
1322        /// The viewBox attribute defines coordinate system and aspect ratio
1323        /// Example: viewBox="0 0 800 600" (x, y, width, height)
1324        viewBox: String,
1325        /// The preserveAspectRatio attribute controls scaling behavior
1326        /// Example: preserveAspectRatio="xMidYMid meet" (center and scale)
1327        preserve_aspect_ratio: String,
1328        /// The xmlns attribute defines the XML namespace (required for standalone SVG)
1329        /// Example: xmlns="http://www.w3.org/2000/svg"
1330        xmlns: String,
1331    }
1332
1333    /// HTML `<path>` element - Defines a path in SVG graphics
1334    ///
1335    /// Example:
1336    ///
1337    /// ```<path d="M10 10 H 90 V 90 H 10 Z" fill="transparent" stroke="black" />```
1338    path {
1339        /// The d attribute defines the path to be drawn
1340        /// Example: d="M20,20 L80,20 L80,80 L20,80 Z" (square path)
1341        d: String,
1342        /// The fill attribute specifies the fill color
1343        /// Example: fill="#3498db" (blue fill)
1344        fill: String,
1345        /// The stroke attribute specifies the outline color
1346        /// Example: stroke="#e74c3c" (red outline)
1347        stroke: String,
1348        /// The stroke-width attribute specifies the width of the outline
1349        /// Example: stroke-width="3" (3 units thick)
1350        stroke_width: String,
1351        /// The stroke-linecap attribute specifies line end style
1352        /// Example: stroke-linecap="round" (rounded ends)
1353        stroke_linecap: String,
1354        /// The stroke-linejoin attribute specifies how line joins are rendered
1355        /// Example: stroke-linejoin="miter" (pointed corners)
1356        stroke_linejoin: String,
1357        /// The stroke-miterlimit attribute limits the length of miters
1358        /// Example: stroke-miterlimit="4" (limits pointy corners)
1359        stroke_miterlimit: String,
1360        /// The stroke-dasharray attribute creates dashed lines
1361        /// Example: stroke-dasharray="5,5" (5 units on, 5 units off)
1362        stroke_dasharray: String,
1363        /// The stroke-dashoffset attribute adjusts dash pattern start
1364        /// Example: stroke-dashoffset="10" (starts 10 units into pattern)
1365        stroke_dashoffset: String,
1366        /// The stroke-opacity attribute sets stroke transparency
1367        /// Example: stroke-opacity="0.5" (50% transparent)
1368        stroke_opacity: String,
1369        /// The fill-opacity attribute sets fill transparency
1370        /// Example: fill-opacity="0.7" (70% opaque)
1371        fill_opacity: String,
1372    }
1373
1374    /// HTML `<rect>` element - Draws a rectangle in SVG
1375    ///
1376    /// Example:
1377    ///
1378    /// ```<rect x="10" y="10" width="100" height="50" fill="blue" />```
1379    rect {
1380        /// The x attribute specifies the x-coordinate of the rectangle
1381        /// Example: x="25" (25 units from the left)
1382        x: String,
1383        /// The y attribute specifies the y-coordinate of the rectangle
1384        /// Example: y="50" (50 units from the top)
1385        y: String,
1386        /// The rx attribute specifies the horizontal corner radius
1387        /// Example: rx="10" (rounded corners)
1388        rx: String,
1389        /// The ry attribute specifies the vertical corner radius
1390        /// Example: ry="10" (rounded corners)
1391        ry: String,
1392        /// The fill attribute specifies the fill color
1393        /// Example: fill="#2ecc71" (green fill)
1394        fill: String,
1395        /// The stroke attribute specifies the outline color
1396        /// Example: stroke="#27ae60" (darker green outline)
1397        stroke: String,
1398        /// The stroke-width attribute specifies the width of the outline
1399        /// Example: stroke-width="2" (2 units thick)
1400        stroke_width: String,
1401    }
1402
1403    /// HTML `<circle>` element - Draws a circle in SVG
1404    ///
1405    /// Example:
1406    ///
1407    /// ```<circle cx="50" cy="50" r="40" fill="red" />```
1408    circle {
1409        /// The cx attribute specifies the x-coordinate of the center
1410        /// Example: cx="100" (center x at 100 units)
1411        cx: String,
1412        /// The cy attribute specifies the y-coordinate of the center
1413        /// Example: cy="100" (center y at 100 units)
1414        cy: String,
1415        /// The r attribute specifies the radius of the circle
1416        /// Example: r="75" (75 units radius)
1417        r: String,
1418        /// The fill attribute specifies the fill color
1419        /// Example: fill="#9b59b6" (purple fill)
1420        fill: String,
1421        /// The stroke attribute specifies the outline color
1422        /// Example: stroke="#8e44ad" (darker purple outline)
1423        stroke: String,
1424        /// The stroke-width attribute specifies the width of the outline
1425        /// Example: stroke-width="3" (3 units thick)
1426        stroke_width: String,
1427    }
1428
1429    /// HTML `<ellipse>` element - Draws an ellipse in SVG
1430    ///
1431    /// Example:
1432    ///
1433    /// ```<ellipse cx="100" cy="50" rx="100" ry="50" fill="yellow" />```
1434    ellipse {
1435        /// The cx attribute specifies the x-coordinate of the center
1436        /// Example: cx="150" (center x at 150 units)
1437        cx: String,
1438        /// The cy attribute specifies the y-coordinate of the center
1439        /// Example: cy="75" (center y at 75 units)
1440        cy: String,
1441        /// The rx attribute specifies the horizontal radius
1442        /// Example: rx="100" (100 units horizontal radius)
1443        rx: String,
1444        /// The ry attribute specifies the vertical radius
1445        /// Example: ry="50" (50 units vertical radius)
1446        ry: String,
1447        /// The fill attribute specifies the fill color
1448        /// Example: fill="#f1c40f" (yellow fill)
1449        fill: String,
1450        /// The stroke attribute specifies the outline color
1451        /// Example: stroke="#f39c12" (darker yellow outline)
1452        stroke: String,
1453        /// The stroke-width attribute specifies the width of the outline
1454        /// Example: stroke-width="2" (2 units thick)
1455        stroke_width: String,
1456    }
1457
1458    /// HTML `<line>` element - Draws a line in SVG
1459    ///
1460    /// Example:
1461    ///
1462    /// ```<line x1="0" y1="0" x2="100" y2="100" stroke="black" />```
1463    line {
1464        /// The x1 attribute specifies the x-coordinate of the start point
1465        /// Example: x1="10" (starts 10 units from left)
1466        x1: String,
1467        /// The y1 attribute specifies the y-coordinate of the start point
1468        /// Example: y1="10" (starts 10 units from top)
1469        y1: String,
1470        /// The x2 attribute specifies the x-coordinate of the end point
1471        /// Example: x2="200" (ends 200 units from left)
1472        x2: String,
1473        /// The y2 attribute specifies the y-coordinate of the end point
1474        /// Example: y2="200" (ends 200 units from top)
1475        y2: String,
1476        /// The stroke attribute specifies the line color
1477        /// Example: stroke="#34495e" (dark blue line)
1478        stroke: String,
1479        /// The stroke-width attribute specifies the width of the line
1480        /// Example: stroke-width="5" (5 units thick)
1481        stroke_width: String,
1482        /// The stroke-linecap attribute specifies line end style
1483        /// Example: stroke-linecap="round" (rounded ends)
1484        stroke_linecap: String,
1485        /// The stroke-dasharray attribute creates dashed lines
1486        /// Example: stroke-dasharray="10,5" (10 units on, 5 units off)
1487        stroke_dasharray: String,
1488    }
1489
1490    /// HTML `<polyline>` element - Draws connected straight lines in SVG
1491    ///
1492    /// Example:
1493    ///
1494    /// ```<polyline points="20,20 40,25 60,40 80,120 120,140 200,180" stroke="orange" fill="none" />```
1495    polyline {
1496        /// The points attribute specifies coordinates for each point
1497        /// Example: points="0,0 50,50 100,25" (series of x,y pairs)
1498        points: String,
1499        /// The fill attribute specifies the fill color between lines
1500        /// Example: fill="none" (transparent fill)
1501        fill: String,
1502        /// The stroke attribute specifies the line color
1503        /// Example: stroke="#e67e22" (orange line)
1504        stroke: String,
1505        /// The stroke-width attribute specifies the width of the lines
1506        /// Example: stroke-width="3" (3 units thick)
1507        stroke_width: String,
1508        /// The stroke-linejoin attribute specifies how lines are joined
1509        /// Example: stroke-linejoin="round" (rounded corners)
1510        stroke_linejoin: String,
1511    }
1512
1513    /// HTML `<polygon>` element - Draws a closed shape with straight lines in SVG
1514    ///
1515    /// Example:
1516    ///
1517    /// ```<polygon points="200,10 250,190 160,210" fill="green" />```
1518    polygon {
1519        /// The points attribute specifies coordinates for each point
1520        /// Example: points="50,50 150,50 100,150" (triangle coordinates)
1521        points: String,
1522        /// The fill attribute specifies the fill color of the shape
1523        /// Example: fill="#1abc9c" (teal fill)
1524        fill: String,
1525        /// The stroke attribute specifies the outline color
1526        /// Example: stroke="#16a085" (darker teal outline)
1527        stroke: String,
1528        /// The stroke-width attribute specifies the width of the outline
1529        /// Example: stroke-width="2" (2 units thick)
1530        stroke_width: String,
1531        /// The fill-rule attribute specifies how to fill shapes with holes
1532        /// Example: fill-rule="evenodd" (alternates fill for nested shapes)
1533        fill_rule: String,
1534    }
1535
1536    /// HTML `<g>` element - Groups SVG elements together
1537    ///
1538    /// Example:
1539    ///
1540    /// ```<g transform="rotate(45 50 50)"><rect x="20" y="20" width="60" height="60" /></g>```
1541    g {
1542        /// The transform attribute applies transformations to the group
1543        /// Example: transform="translate(100,50) scale(2)" (moves and scales)
1544        transform: String,
1545        /// The fill attribute specifies the fill color for all elements in the group
1546        /// Example: fill="#3498db" (blue fill for all children)
1547        fill: String,
1548        /// The stroke attribute specifies the outline color for all elements in the group
1549        /// Example: stroke="#2980b9" (darker blue outline for all children)
1550        stroke: String,
1551    }
1552
1553    /// HTML `<use>` element - Reuses an SVG element defined elsewhere
1554    ///
1555    /// Example:
1556    ///
1557    /// ```<r#use href="#myCircle" x="10" y="10" fill="blue" />```
1558    r#use {
1559        /// The href attribute specifies which element to reuse
1560        /// Example: href="#icon-star" (references element with id="icon-star")
1561        href: String,
1562        /// The x attribute specifies the x-coordinate where to place the reused element
1563        /// Example: x="100" (100 units from left)
1564        x: String,
1565        /// The y attribute specifies the y-coordinate where to place the reused element
1566        /// Example: y="50" (50 units from top)
1567        y: String,
1568    }
1569
1570    /// HTML <foreignObject> element - Includes non-SVG elements inside SVG
1571    ///
1572    /// Example:
1573    ///
1574    /// ```<foreignObject x="20" y="20" width="160" height="160"><div>HTML content inside SVG</div></foreignObject>```
1575    foreignObject {
1576        /// The x attribute specifies the x-coordinate of the foreign object
1577        /// Example: x="25" (25 units from left)
1578        x: String,
1579        /// The y attribute specifies the y-coordinate of the foreign object
1580        /// Example: y="25" (25 units from top)
1581        y: String,
1582    }
1583
1584    /// HTML `<defs>` element - Container for reusable SVG elements
1585    ///
1586    /// Example:
1587    ///
1588    /// ```<defs><circle id="myCircle" cx="5" cy="5" r="4" /></defs>```
1589    defs {
1590    }
1591
1592    /// HTML <linearGradient> element - Defines a linear gradient for SVG fills
1593    ///
1594    /// Example:
1595    ///
1596    /// ```<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" style="stop-color:rgb(255,255,0)" /></linearGradient>```
1597    linearGradient {
1598        /// The x1 attribute defines the start point of the gradient (x-coordinate)
1599        /// Example: x1="0%" (starts at left edge)
1600        x1: String,
1601        /// The y1 attribute defines the start point of the gradient (y-coordinate)
1602        /// Example: y1="0%" (starts at top edge)
1603        y1: String,
1604        /// The x2 attribute defines the end point of the gradient (x-coordinate)
1605        /// Example: x2="100%" (ends at right edge)
1606        x2: String,
1607        /// The y2 attribute defines the end point of the gradient (y-coordinate)
1608        /// Example: y2="100%" (ends at bottom edge)
1609        y2: String,
1610        /// The gradientUnits attribute defines the coordinate system for the gradient
1611        /// Example: gradientUnits="userSpaceOnUse" (uses absolute coordinates)
1612        gradientUnits: String,
1613        /// The spreadMethod attribute defines how the gradient fills beyond its bounds
1614        /// Example: spreadMethod="reflect" (gradient reflects at boundaries)
1615        spreadMethod: String,
1616    }
1617
1618    /// HTML `<stop>` element - Defines color transitions in gradients
1619    ///
1620    /// Example:
1621    ///
1622    /// ```<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />```
1623    stop {
1624        /// The offset attribute defines where along the gradient this color appears
1625        /// Example: offset="50%" (color positioned halfway through gradient)
1626        offset: String,
1627        /// The stop-color attribute defines the color at this stop
1628        /// Example: stop-color="#3498db" (blue color)
1629        stop_color: String,
1630        /// The stop-opacity attribute defines the opacity at this stop
1631        /// Example: stop-opacity="0.5" (50% transparent)
1632        stop_opacity: String,
1633    }
1634
1635    /// HTML <radialGradient> element - Defines a radial gradient for SVG fills
1636    ///
1637    /// Example:
1638    ///
1639    /// ```<radialGradient id="grad2" cx="50%" cy="50%" r="50%"><stop offset="0%" style="stop-color:red" /></radialGradient>```
1640    radialGradient {
1641        /// The cx attribute defines the x-coordinate of the center point
1642        /// Example: cx="50%" (center of the area horizontally)
1643        cx: String,
1644        /// The cy attribute defines the y-coordinate of the center point
1645        /// Example: cy="50%" (center of the area vertically)
1646        cy: String,
1647        /// The r attribute defines the radius of the gradient
1648        /// Example: r="75%" (extends to 75% of the reference area)
1649        r: String,
1650        /// The fx attribute defines the x-coordinate of the focal point
1651        /// Example: fx="60%" (focal point slightly right of center)
1652        fx: String,
1653        /// The fy attribute defines the y-coordinate of the focal point
1654        /// Example: fy="40%" (focal point slightly above center)
1655        fy: String,
1656        /// The fr attribute defines the radius of the focal point
1657        /// Example: fr="5%" (small focal point)
1658        fr: String,
1659        /// The gradientUnits attribute defines the coordinate system for the gradient
1660        /// Example: gradientUnits="objectBoundingBox" (relative to object)
1661        gradientUnits: String,
1662        /// The spreadMethod attribute defines how the gradient fills beyond its bounds
1663        /// Example: spreadMethod="pad" (uses edge color beyond boundaries)
1664        spreadMethod: String,
1665    }
1666
1667    /// HTML `<mask>` element - Defines an area where SVG elements are partially or fully hidden
1668    ///
1669    /// Example:
1670    ///
1671    /// ```<mask id="myMask"><rect width="100%" height="100%" fill="white" opacity="0.5" /></mask>```
1672    mask {
1673        /// The maskUnits attribute specifies the coordinate system for mask positioning
1674        /// Example: maskUnits="userSpaceOnUse" (absolute coordinates)
1675        mask_units: String,
1676        /// The maskContentUnits attribute specifies the coordinate system for mask content
1677        /// Example: maskContentUnits="objectBoundingBox" (relative to object)
1678        mask_content_units: String,
1679        /// The x attribute specifies the x-coordinate of the mask
1680        /// Example: x="0" (starts at left edge)
1681        x: String,
1682        /// The y attribute specifies the y-coordinate of the mask
1683        /// Example: y="0" (starts at top edge)
1684        y: String,
1685    }
1686}