{"id":46026400,"url":"https://github.com/sn/ydnatl","last_synced_at":"2026-03-01T03:01:20.435Z","repository":{"id":290577646,"uuid":"877986337","full_name":"sn/ydnatl","owner":"sn","description":"YDNATL is a Python library that lets you build HTML UI using simple Python classes.","archived":false,"fork":false,"pushed_at":"2025-11-13T05:17:16.000Z","size":153,"stargazers_count":12,"open_issues_count":4,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-29T02:56:51.652Z","etag":null,"topics":["html","python","python3","template","templating-engine","ui"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/ydnatl","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-10-24T15:21:12.000Z","updated_at":"2025-11-13T05:16:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"d98f4d8d-05c8-42be-b9cc-ac42b7529f83","html_url":"https://github.com/sn/ydnatl","commit_stats":null,"previous_names":["sn/ydnatl"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/sn/ydnatl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sn%2Fydnatl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sn%2Fydnatl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sn%2Fydnatl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sn%2Fydnatl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sn","download_url":"https://codeload.github.com/sn/ydnatl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sn%2Fydnatl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29959284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T01:47:18.291Z","status":"online","status_checked_at":"2026-03-01T02:00:07.437Z","response_time":124,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["html","python","python3","template","templating-engine","ui"],"created_at":"2026-03-01T03:01:19.902Z","updated_at":"2026-03-01T03:01:20.402Z","avatar_url":"https://github.com/sn.png","language":"Python","readme":"# YDNATL\n\nYDNATL (**Y**ou **D**on't **N**eed **A**nother **T**emplate **L**anguage) is a Python library that lets you build HTML using simple Python classes. It's great for apps that need HTML generation while skipping the hassle of writing it by hand or using a templating engine.\n\n- ✓ Declarative syntax for building HTML documents (like Flutter)\n- ✓ Easy to read and write\n- ✓ Supports all HTML5 elements\n- ✓ JSON serialization/deserialization for saving and loading UI structures\n- ✓ Pretty printing with indentation for readable HTML\n- ✓ CSS style helpers for easy inline styling\n- ✓ External stylesheet system with theming and BEM support\n- ✓ Method chaining for fluent interfaces\n- ✓ Context manager support for clean nesting\n- ✓ Fragment support for wrapper-free grouping\n- ✓ HTML parsing to convert raw HTML strings into YDNATL elements\n- ✓ Lightweight\n- ✓ Extremely fast\n- ✓ Fully customisable\n- ✓ Compose HTML efficiently\n- ✓ Lean \u0026 clean Python with no dependencies\n- ✓ LLM Compatible\n\n## Requirements\n\nPython `3.8` or higher is required.\n\n## Installation\n\n```bash\npip install ydnatl\n```\n\n## Usage\n\n```python\nfrom ydnatl import *\n\n# Create a simple HTML document\npage = HTML(\n    Head(\n        Title(\"My Page\")\n    ),\n    Body(\n        Div(\n            H1(\"Hello, World!\"),\n            Paragraph(\"This is a test document.\")\n        )\n    )\n)\n\nprint(page.render())\n```\n\nThis code will produce:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\" dir=\"ltr\"\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eMy Page\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv\u003e\n      \u003ch1\u003eHello, World!\u003c/h1\u003e\n      \u003cp\u003eThis is a test document.\u003c/p\u003e\n    \u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Dynamic Composition \n\n```python\nfrom ydnatl import *\n\n# Dynamic content based on conditions\nday_of_week = \"Monday\"  # Example variable\n\nhtml = HTML()\nheader = Head()\nbody = Body()\n\nbody.append(\n    Div(\n        H1(\"My Headline\"),\n        Paragraph(\"Basic paragraph element\"),\n    )\n)\n\nif day_of_week == \"Monday\": \n    header.append(Title(\"Unfortunately, it's Monday!\"))\nelse:\n    header.append(Title(\"Great! It's no longer Monday!\"))\n\nhtml.append(header)\nhtml.append(body)\n\nprint(html.render())\n```\n\nAll element classes are subclasses of HTMLElement. The parent class provides all of the inherited functionality to generate the individual tags. Keyword arguments passed to element constructors will be converted to attributes on the HTML elements being generated.\n\n### Working with Attributes\n\n```python\nfrom ydnatl import *\n\n# Add attributes via constructor\ndiv = Div(id=\"my-div\", class_name=\"container\", data_value=\"123\")\n\n# Add attributes after creation\ndiv.add_attribute(\"role\", \"main\")\ndiv.add_attributes([(\"aria-label\", \"Main content\"), (\"tabindex\", \"0\")])\n\n# HTML output: \u003cdiv id=\"my-div\" class=\"container\" data-value=\"123\" role=\"main\" aria-label=\"Main content\" tabindex=\"0\"\u003e\u003c/div\u003e\n```\n\n### Pretty Printing\n\nYDNATL supports pretty printing with automatic indentation for readable HTML output:\n\n```python\nfrom ydnatl import *\n\npage = HTML(\n    Head(Title(\"My Page\")),\n    Body(\n        Div(\n            H1(\"Hello, World!\"),\n            Paragraph(\"This is a paragraph.\")\n        )\n    )\n)\n\n# Compact output (default)\nprint(page.render())\n# Output: \u003c!DOCTYPE html\u003e\u003chtml lang=\"en\" dir=\"ltr\"\u003e\u003chead\u003e\u003ctitle\u003eMy Page\u003c/title\u003e\u003c/head\u003e...\n\n# Pretty output with indentation\nprint(page.render(pretty=True))\n# Output:\n# \u003c!DOCTYPE html\u003e\n# \u003chtml lang=\"en\" dir=\"ltr\"\u003e\n#   \u003chead\u003e\n#     \u003ctitle\u003eMy Page\u003c/title\u003e\n#   \u003c/head\u003e\n#   \u003cbody\u003e\n#     \u003cdiv\u003e\n#       \u003ch1\u003eHello, World!\u003c/h1\u003e\n#       \u003cp\u003eThis is a paragraph.\u003c/p\u003e\n#     \u003c/div\u003e\n#   \u003c/body\u003e\n# \u003c/html\u003e\n```\n\nPretty printing is perfect for:\n- Development and debugging\n- Generating human-readable HTML files\n- Documentation and tutorials\n- Inspecting complex structures\n\n### CSS Style Helpers\n\nYDNATL provides convenient methods for working with inline CSS styles:\n\n```python\nfrom ydnatl import *\n\n# Create element and add styles\ndiv = Div(\"Styled content\")\n\n# Add single style\ndiv.add_style(\"color\", \"blue\")\ndiv.add_style(\"font-size\", \"16px\")\n\n# Add multiple styles at once\ndiv.add_styles({\n    \"background-color\": \"#f0f0f0\",\n    \"padding\": \"20px\",\n    \"margin\": \"10px\",\n    \"border-radius\": \"5px\"\n})\n\n# Get a specific style value\ncolor = div.get_style(\"color\")  # Returns \"blue\"\n\n# Remove a style\ndiv.remove_style(\"margin\")\n\n# Result: \u003cdiv style=\"color: blue; font-size: 16px; background-color: #f0f0f0; padding: 20px; border-radius: 5px\"\u003eStyled content\u003c/div\u003e\n```\n\n### External Stylesheet and Theming\n\nYDNATL includes a powerful styling system for managing external stylesheets, themes, and reusable component styles. This is perfect for building larger applications where you want to separate styles from markup.\n\n#### Basic StyleSheet Usage\n\n```python\nfrom ydnatl import *\nfrom ydnatl.styles import CSSStyle, StyleSheet\n\n# Create a stylesheet\nstylesheet = StyleSheet()\n\n# Register reusable styles\nbtn_primary = stylesheet.register(\"btn-primary\", CSSStyle(\n    background_color=\"#007bff\",\n    color=\"white\",\n    padding=\"10px 20px\",\n    border_radius=\"5px\",\n    border=\"none\",\n    cursor=\"pointer\",\n    _hover=CSSStyle(background_color=\"#0056b3\")  # Pseudo-selector\n))\n\n# Use in HTML\npage = HTML(\n    Head(\n        Title(\"My Page\"),\n        Style(stylesheet.render())  # Insert generated CSS\n    ),\n    Body(\n        Button(\"Click Me\", class_name=btn_primary)\n    )\n)\n```\n\n#### Theming Support\n\nYDNATL includes three preset themes (Modern, Classic, Minimal) with full CSS variable support:\n\n```python\nfrom ydnatl.styles import Theme, StyleSheet, CSSStyle\n\n# Use a preset theme\ntheme = Theme.modern()  # or Theme.classic() or Theme.minimal()\n\n# Create stylesheet with theme\nstylesheet = StyleSheet(theme=theme)\n\n# Register styles using theme variables\nbtn = stylesheet.register(\"btn\", CSSStyle(\n    background_color=\"var(--color-primary)\",\n    color=\"var(--color-white)\",\n    padding=\"var(--spacing-md)\",\n    border_radius=\"6px\",\n    _hover=CSSStyle(background_color=\"var(--color-primary-dark)\")\n))\n\ncard = stylesheet.register(\"card\", CSSStyle(\n    background_color=\"var(--color-white)\",\n    padding=\"var(--spacing-lg)\",\n    border_radius=\"8px\",\n    box_shadow=\"0 1px 3px rgba(0, 0, 0, 0.1)\"\n))\n```\n\n#### BEM Naming Convention\n\nBuilt-in support for BEM (Block Element Modifier) naming:\n\n```python\nfrom ydnatl.styles import StyleSheet, CSSStyle\n\nstylesheet = StyleSheet()\n\n# Register with BEM naming\ncard = stylesheet.register_bem(\"card\", style=CSSStyle(\n    background=\"white\",\n    padding=\"20px\"\n))\n\ncard_header = stylesheet.register_bem(\"card\", element=\"header\", style=CSSStyle(\n    font_weight=\"bold\"\n))\n\ncard_featured = stylesheet.register_bem(\"card\", modifier=\"featured\", style=CSSStyle(\n    border=\"2px solid blue\"\n))\n\n# Generates: .card, .card__header, .card--featured\n```\n\n#### Responsive Breakpoints\n\nAdd responsive styles with breakpoint support:\n\n```python\nfrom ydnatl.styles import CSSStyle, StyleSheet\n\nstylesheet = StyleSheet()\n\ncontainer = stylesheet.register(\"container\", CSSStyle(\n    padding=\"10px\",\n    _sm=CSSStyle(padding=\"15px\", max_width=\"640px\"),\n    _md=CSSStyle(padding=\"20px\", max_width=\"768px\"),\n    _lg=CSSStyle(padding=\"30px\", max_width=\"1024px\")\n))\n\n# Generates media queries automatically\n```\n\n#### Combining with Inline Styles\n\nYou can mix stylesheet classes with inline style overrides:\n\n```python\n# Register base style\nbtn = stylesheet.register(\"btn\", CSSStyle(\n    padding=\"10px 20px\",\n    border_radius=\"5px\"\n))\n\n# Use base class + inline overrides\nButton(\"Custom\", class_name=btn).add_styles({\n    \"background-color\": \"#ff0000\",\n    \"font-size\": \"18px\"\n})\n```\n\n**Key Features:**\n- Snake_case to kebab-case conversion (background_color → background-color)\n- Pseudo-selector support (:hover, :active, :focus, etc.)\n- Responsive breakpoints with media queries\n- Theme system with CSS variables\n- BEM naming convention support\n- JSON serialization for saving/loading styles\n- Combines seamlessly with existing inline styles\n\nSee the [examples/stylesheet_example.py](examples/stylesheet_example.py) file for complete working examples.\n\n### Method Chaining\n\nAll builder methods return `self`, enabling fluent method chaining:\n\n```python\nfrom ydnatl import *\n\n# Chain multiple operations together\ncontainer = (Div()\n    .add_attribute(\"id\", \"main-container\")\n    .add_attribute(\"class\", \"wrapper\")\n    .add_style(\"background\", \"#fff\")\n    .add_styles({\"padding\": \"20px\", \"margin\": \"0 auto\"})\n    .append(H1(\"Welcome\"))\n    .append(Paragraph(\"This is the main content.\"))\n    .append(Paragraph(\"Another paragraph here.\")))\n\nprint(container.render())\n```\n\nChainable methods:\n- `append()` - Add children\n- `prepend()` - Add children at the beginning\n- `add_attribute()` - Add single attribute\n- `add_attributes()` - Add multiple attributes\n- `remove_attribute()` - Remove an attribute\n- `add_style()` - Add single CSS style\n- `add_styles()` - Add multiple CSS styles\n- `remove_style()` - Remove a CSS style\n- `clear()` - Remove all children\n- `remove_all()` - Remove children matching a condition\n\n### Context Manager Support\n\nUse elements as context managers for cleaner, more intuitive nesting:\n\n```python\nfrom ydnatl import *\n\n# Using context managers\nwith Div(id=\"container\", class_name=\"main\") as container:\n    with Section(class_name=\"content\") as section:\n        section.append(H1(\"Title\"))\n        section.append(Paragraph(\"Content goes here\"))\n\n    container.append(section)\n    container.append(Footer(Paragraph(\"Footer text\")))\n\nprint(container.render())\n```\n\n### Fragment Support\n\nUse `Fragment` to group elements without adding a wrapper tag:\n\n```python\nfrom ydnatl import *\n\n# Without Fragment - adds extra div wrapper\ncontent = Div(\n    H1(\"Title\"),\n    Paragraph(\"Content\")\n)\n# Output: \u003cdiv\u003e\u003ch1\u003eTitle\u003c/h1\u003e\u003cp\u003eContent\u003c/p\u003e\u003c/div\u003e\n\n# With Fragment - no wrapper tag\ncontent = Fragment(\n    H1(\"Title\"),\n    Paragraph(\"Content\")\n)\n# Output: \u003ch1\u003eTitle\u003c/h1\u003e\u003cp\u003eContent\u003c/p\u003e\n\n# Perfect for conditional rendering\ndef render_items(items, show_header=True):\n    fragment = Fragment()\n\n    if show_header:\n        fragment.append(H2(\"Items List\"))\n\n    for item in items:\n        fragment.append(Paragraph(item))\n\n    return fragment\n\n# Use in parent element\npage = Div(\n    render_items([\"Item 1\", \"Item 2\", \"Item 3\"], show_header=True)\n)\n# Output: \u003cdiv\u003e\u003ch2\u003eItems List\u003c/h2\u003e\u003cp\u003eItem 1\u003c/p\u003e\u003cp\u003eItem 2\u003c/p\u003e\u003cp\u003eItem 3\u003c/p\u003e\u003c/div\u003e\n```\n\n**Fragment use cases:**\n- Conditional rendering without wrapper divs\n- Returning multiple elements from functions\n- List composition and iteration\n- Cleaner component architecture\n\n### HTML Parsing\n\nYDNATL can parse raw HTML strings and convert them into YDNATL elements. This is useful for importing existing HTML, migrating from other tools, or working with HTML from external sources.\n\n```python\nfrom ydnatl import from_html, HTMLElement\n\n# Parse a single HTML element\nhtml_string = '\u003cdiv class=\"container\"\u003e\u003ch1\u003eHello World\u003c/h1\u003e\u003cp\u003eWelcome to YDNATL\u003c/p\u003e\u003c/div\u003e'\nelement = from_html(html_string)\n\n# Now you can work with it like any YDNATL element\nelement.add_style(\"padding\", \"20px\")\nelement.append(Paragraph(\"Added via YDNATL\"))\n\nprint(element.render())\n# Output: \u003cdiv class=\"container\" style=\"padding: 20px\"\u003e\u003ch1\u003eHello World\u003c/h1\u003e\u003cp\u003eWelcome to YDNATL\u003c/p\u003e\u003cp\u003eAdded via YDNATL\u003c/p\u003e\u003c/div\u003e\n\n# Alternative: use the class method\nelement = HTMLElement.from_html(html_string)\n```\n\n**Parsing HTML fragments** (multiple root elements):\n\n```python\nfrom ydnatl import from_html\n\n# HTML with multiple root elements\nhtml_fragment = '''\n\u003ch1\u003eWelcome\u003c/h1\u003e\n\u003cp\u003eFirst paragraph\u003c/p\u003e\n\u003cp\u003eSecond paragraph\u003c/p\u003e\n'''\n\n# Parse as fragment (returns list of elements)\nelements = from_html(html_fragment, fragment=True)\n\n# Work with each element\nfor el in elements:\n    print(el.tag, el.text)\n# Output:\n# h1 Welcome\n# p First paragraph\n# p Second paragraph\n\n# Combine with other YDNATL features\ncontainer = Div()\nfor el in elements:\n    container.append(el)\n\nprint(container.render())\n# Output: \u003cdiv\u003e\u003ch1\u003eWelcome\u003c/h1\u003e\u003cp\u003eFirst paragraph\u003c/p\u003e\u003cp\u003eSecond paragraph\u003c/p\u003e\u003c/div\u003e\n```\n\n**Features:**\n- Handles all HTML5 elements including self-closing tags (br, img, hr, etc.)\n- Preserves attributes, including data-* attributes\n- Converts `class` attribute to `class_name` automatically\n- Supports nested structures of any depth\n- Handles HTML entities and special characters\n- No external dependencies (uses Python's built-in html.parser)\n\n**Use cases:**\n- Import existing HTML into your website builder\n- Migrate from other HTML generation tools\n- Parse HTML templates from external sources\n- Convert HTML snippets to YDNATL for manipulation\n- Testing and validation workflows\n- Combining static HTML with dynamic YDNATL generation\n\n### JSON Serialization\n\nYDNATL supports JSON serialization and deserialization, making it perfect for drag-and-drop website builders, saving UI states, or transmitting page structures over APIs.\n\n```python\nfrom ydnatl import *\n\n# Build a page structure\npage = Div(id=\"page\", class_name=\"container\")\npage.append(\n    H1(\"Welcome\"),\n    Section(\n        Paragraph(\"This is a paragraph\"),\n        Paragraph(\"Another paragraph\", class_name=\"highlight\")\n    )\n)\n\n# Serialize to JSON (for saving/storing)\njson_data = page.to_json(indent=2)\nprint(json_data)\n\n# Later... deserialize from JSON (for loading)\nfrom ydnatl.core.element import HTMLElement\nrestored_page = HTMLElement.from_json(json_data)\n\n# Generate HTML (output will be identical)\nprint(str(restored_page))\n```\n\nThe JSON format is simple and clean:\n\n```json\n{\n  \"tag\": \"div\",\n  \"self_closing\": false,\n  \"attributes\": {\n    \"id\": \"page\",\n    \"class\": \"container\"\n  },\n  \"text\": \"\",\n  \"children\": []\n}\n```\n\n**Use cases:**\n- Save/load website layouts to/from a database\n- Implement undo/redo functionality\n- Store pre-built templates as JSON\n- Version control for page structures\n- API communication between frontend and backend\n- Drag-and-drop website builders\n\n## Great For\n\n- CLI tools\n- Drag-and-drop website builders\n- Site builders with save/load functionality\n- Web frameworks\n- Alternative to heavy template engines\n- Static site generators\n- Documentation generators\n- LLM's and AI tooling that generate interfaces dynamically\n- Creating frontends for headless platforms (CMS/CRM etc)\n- Applications requiring UI state serialization\n\n## LLM Guide\n\nFor AI assistants and code generation tools, we provide a comprehensive technical reference in [LLM_GUIDE.md](LLM_GUIDE.md) with:\n- Complete API documentation with exact method signatures\n- Type hints and return values for all methods\n- 10 common patterns with code examples\n- Error handling and security best practices\n- Quick reference cheat sheet\n\n## Examples\n\n### FastAPI\n\n```python\nfrom fastapi import FastAPI\nfrom ydnatl import *\n\napp = FastAPI()\n\n@app.get(\"/\")\nasync def root():\n    return HTML(\n        Head(\n            Title(\"My Page\")\n        ),\n        Body(\n            Section(\n                H1(\"Hello, World!\"),\n                Paragraph(\"This is a test document.\")\n            )\n        )\n    )\n```\n\n### Django\n\n```python\nfrom django.http import HttpResponse\nfrom ydnatl import *\n\ndef index(request):\n    return HttpResponse(HTML(\n        Head(\n            Title(\"My Page\"),\n            Meta(charset=\"utf-8\"),\n            Meta(name=\"viewport\", content=\"width=device-width, initial-scale=1.0\"),\n            HtmlLink(rel=\"stylesheet\", href=\"style.css\"),\n            Script(src=\"script.js\")\n        ),\n        Body(\n            Section(\n                H1(\"Hello, World!\"),\n                Paragraph(\"This is a paragraph.\"),\n                Paragraph(\"This is another paragraph.\")\n            )\n        )\n    ))\n```\n\n### Flask\n\n```python\nfrom flask import Flask\nfrom ydnatl import *\n\napp = Flask(__name__)\n\n@app.route(\"/\")\ndef index():\n    return HTML(\n        Head(\n            Title(\"My Page\")\n        ),\n        Body(\n            Section(\n                H1(\"Hello, World!\"),\n                Paragraph(\"This is a test document.\")\n            )\n        )\n    )\n```\n\n## Test Coverage\n\nYDNATL has full test coverage. To run the tests locally, use:\n\n```shell\npytest\n```\n\n## Element Methods:\n\n**Element Manipulation:**\n- `instance.prepend()` - Prepend children (returns self for chaining)\n- `instance.append()` - Append children (returns self for chaining)\n- `instance.filter()` - Filter children by condition\n- `instance.remove_all()` - Remove children matching condition (returns self for chaining)\n- `instance.clear()` - Remove all children (returns self for chaining)\n- `instance.pop()` - Remove and return child at index\n- `instance.first()` - Get first child\n- `instance.last()` - Get last child\n- `instance.replace_child()` - Replace child at index\n- `instance.clone()` - Deep copy of element\n- `instance.find_by_attribute()` - Find child by attribute value\n- `instance.count_children()` - Count direct children\n\n**Attribute Management:**\n- `instance.add_attribute()` - Add single attribute (returns self for chaining)\n- `instance.add_attributes()` - Add multiple attributes (returns self for chaining)\n- `instance.remove_attribute()` - Remove attribute (returns self for chaining)\n- `instance.get_attribute()` - Get attribute value\n- `instance.has_attribute()` - Check if attribute exists\n- `instance.get_attributes()` - Get all or specific attributes\n- `instance.generate_id()` - Generate unique ID if not present\n\n**CSS Style Management:**\n- `instance.add_style()` - Add single CSS style (returns self for chaining)\n- `instance.add_styles()` - Add multiple CSS styles (returns self for chaining)\n- `instance.get_style()` - Get specific style value\n- `instance.remove_style()` - Remove CSS style (returns self for chaining)\n\n**Rendering:**\n- `instance.render(pretty=False)` - Render to HTML string (pretty=True for indented output)\n- `instance.to_dict()` - Convert to dictionary\n- `instance.to_json(indent=None)` - Serialize to JSON string\n- `HTMLElement.from_dict(data)` - Reconstruct from dictionary (class method)\n- `HTMLElement.from_json(json_str)` - Reconstruct from JSON string (class method)\n- `HTMLElement.from_html(html_str, fragment=False)` - Parse HTML string to YDNATL element(s) (class method)\n- `from_html(html_str, fragment=False)` - Parse HTML string to YDNATL element(s) (function)\n\n## Events\n\n- `instance.on_load()`\n- `instance.on_before_render()`\n- `instance.on_after_render()`\n- `instance.on_unload()`\n\n## Element Properties\n\n- `instance.tag`\n- `instance.children`\n- `instance.text`\n- `instance.attributes`\n- `instance.self_closing`\n\n## Modules\n\n| **Module**         | **Purpose**                       | **Key Elements**                                |\n|--------------------|-----------------------------------|-------------------------------------------------|\n| ydnatl.tags.form   | Common HTML form elements         | Form, Input, Button, Select, Textarea           |\n| ydnatl.tags.html   | Structural HTML document elements | HTML, Head, Body, Title, Meta, Script           |\n| ydnatl.tags.layout | Layout related HTML tags          | Div, Section, Header, Nav, Footer, Main         |\n| ydnatl.tags.lists  | HTML list elements                | UnorderedList, OrderedList, ListItem            |\n| ydnatl.tags.media  | Media related HTML elements       | Image, Video, Audio, Figure, Canvas             |\n| ydnatl.tags.table  | HTML table elements               | Table, TableRow, TableHeaderCell, TableDataCell |\n| ydnatl.tags.text   | HTML text elements                | H1-H6, Paragraph, Span, Strong, Em              |\n\n## Importing\n\nInstead of importing the entire module, you can selectively use only the elements you need like this:\n\n```python\n# Instead of importing everything\nfrom ydnatl import *\n\n# Import selectively for better performance and clarity\nfrom ydnatl.tags.form import Form, Button, Input\nfrom ydnatl.tags.html import HTML, Head, Body, Title\nfrom ydnatl.tags.layout import Div, Section\nfrom ydnatl.tags.text import H1, Paragraph\n```\n\n#### ydnatl.tags.form\n\n- `Form()`\n- `Input()`\n- `Label()`\n- `Textarea()`\n- `Select()`\n- `Option()`\n- `Button()`\n- `Fieldset()`\n- `Legend()`\n- `Optgroup()`\n- `Output()`\n- `Progress()`\n- `Meter()`\n\n#### ydnatl.tags.html\n\n- `HTML()`\n- `Head()`\n- `Body()`\n- `Title()`\n- `Meta()`\n- `Base()`\n- `HtmlLink()` (use instead of `Link()` to avoid conflicts)\n- `Script()`\n- `Style()`\n- `Noscript()`\n- `IFrame()`\n\n#### ydnatl.tags.layout\n\n- `Div()`\n- `Section()`\n- `Article()`\n- `Aside()`\n- `Header()`\n- `Nav()`\n- `Footer()`\n- `HorizontalRule()`\n- `Main()`\n- `Details()`\n- `Summary()`\n- `Dialog()`\n\n#### ydnatl.tags.lists\n\n- `UnorderedList()`\n- `OrderedList()`\n- `ListItem()`\n- `Datalist()`\n- `DescriptionDetails()`\n- `DescriptionList()`\n- `DescriptionTerm()`\n\n#### ydnatl.tags.media\n\n- `Image()`\n- `Video()`\n- `Audio()`\n- `Source()`\n- `Track()`\n- `Picture()`\n- `Figure()`\n- `Figcaption()`\n- `Canvas()`\n- `Embed()`\n- `Object()`\n- `Param()`\n- `Map()`\n- `Area()`\n\n#### ydnatl.tags.table\n\n- `Table()`\n- `TableFooter()`\n- `TableHeaderCell()`\n- `TableHeader()`\n- `TableBody()`\n- `TableDataCell()`\n- `TableRow()`\n- `Caption()`\n- `Col()`\n- `Colgroup()`\n\n#### ydnatl.tags.text\n\n- `H1()`\n- `H2()`\n- `H3()`\n- `H4()`\n- `H5()`\n- `H6()`\n- `Paragraph()`\n- `Blockquote()`\n- `Pre()`\n- `Quote()`\n- `Cite()`\n- `Em()`\n- `Italic()`\n- `Span()`\n- `Strong()`\n- `Bold()`\n- `Abbr()`\n- `Link()`\n- `Small()`\n- `Superscript()`\n- `Subscript()`\n- `Time()`\n- `Code()`\n- `Del()`\n- `Ins()`\n- `Strikethrough()`\n- `Underline()`\n- `Kbd()`\n- `Samp()`\n- `Var()`\n- `Mark()`\n- `Dfn()`\n- `Br()`\n- `Wbr()`\n\n## Creating your own elements or components\n\n```python\n\nfrom ydnatl import *\n\nclass MyTag(HTMLElement):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **{**kwargs, \"tag\": \"mytag\"})\n\n        self.add_attributes([\n            (\"id\", \"mycustomid\"),\n            (\"aria-controls\", \"menu\"),\n        ])\n\n    def on_load(self) -\u003e None:\n        print(\"The on_load event has been called\")\n\n    def on_before_render(self) -\u003e None:\n        print(\"The on_before_render event has been called\")\n\n    def on_after_render(self) -\u003e None:\n        print(\"The on_after_render event has been called\")\n\n\nmytag = MyTag(\n    Div(\n        Paragraph(\"Hello World!\")\n    )\n)\n\nprint(mytag.render())\n```\n\nThis will produce:\n\n```html\n\u003cmytag id=\"mycustomid\" aria-controls=\"menu\"\u003e\n  \u003cdiv\u003e\n    \u003cp\u003eHello World!\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/mytag\u003e\n```\n\nYou can use the event callbacks or properties/methods directly to load further child elements, fetch data or any other programmatic task to enrich or construct your tag on loading, render or even after render.\n\nYou can take this further and construct an entire page as a component where everything needed for the page is contained within the element class itself. This is a great way to build websites.\n\n## Contributions\n\nContributions, suggestions and improvements are all welcome. \n\n#### Developing YDNATL\n\n1. Create a virtual environment\n\n```bash\npython3 -m venv .venv \nsource .venv/bin/activate  # On Windows: .venv\\Scripts\\activate\npip install --upgrade pip\n```\n\n2. Install the dev dependencies:\n\n```bash\npip install \".[dev]\"\n```\n\n3. Run the tests:\n\n```bash\npytest\n```\n\nWhen you are happy with your changes, create a merge request.\n\n## License\n\nPlease see [LICENSE](LICENSE) for licensing details.\n\n## Author\n\n[github.com/sn](https://github.com/sn)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsn%2Fydnatl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsn%2Fydnatl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsn%2Fydnatl/lists"}