Skip to main content

Pure Python library for programmatic HTML generation with declarative class-based API, comprehensive styling system, and JSON serialization. Zero dependencies.

Project description

NitroUI

NitroUI is a lightweight Python library for programmatically generating HTML documents using a clean, object-oriented approach. It provides a comprehensive set of HTML elements as Python classes, allowing developers to build complex web pages with ease. Fully compatible with LLM's.

Requirements

Python 3.8 or higher is required.

Installation

pip install nitro-ui

Usage

from nitro_ui import *

# Create a simple HTML document
page = HTML(
    Head(
        Title("My Page")
    ),
    Body(
        Div(
            H1("Hello, World!"),
            Paragraph("This is a test document.")
        )
    )
)

print(page.render())

This code will produce:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <title>My Page</title>
  </head>
  <body>
    <div>
      <h1>Hello, World!</h1>
      <p>This is a test document.</p>
    </div>
  </body>
</html>

Dynamic Composition

from nitro_ui import *

# Dynamic content based on conditions
day_of_week = "Monday"  # Example variable

html = HTML()
header = Head()
body = Body()

body.append(
    Div(
        H1("My Headline"),
        Paragraph("Basic paragraph element"),
    )
)

if day_of_week == "Monday": 
    header.append(Title("Unfortunately, it's Monday!"))
else:
    header.append(Title("Great! It's no longer Monday!"))

html.append(header)
html.append(body)

print(html.render())

All 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.

Working with Attributes

from nitro_ui import *

# Add attributes via constructor
div = Div(id="my-div", class_name="container", data_value="123")

# Add attributes after creation
div.add_attribute("role", "main")
div.add_attributes([("aria-label", "Main content"), ("tabindex", "0")])

# HTML output: <div id="my-div" class="container" data-value="123" role="main" aria-label="Main content" tabindex="0"></div>

Pretty Printing

NitroUI supports pretty printing with automatic indentation for readable HTML output:

from nitro_ui import *

page = HTML(
    Head(Title("My Page")),
    Body(
        Div(
            H1("Hello, World!"),
            Paragraph("This is a paragraph.")
        )
    )
)

# Compact output (default)
print(page.render())
# Output: <!DOCTYPE html><html lang="en" dir="ltr"><head><title>My Page</title></head>...

# Pretty output with indentation
print(page.render(pretty=True))
# Output:
# <!DOCTYPE html>
# <html lang="en" dir="ltr">
#   <head>
#     <title>My Page</title>
#   </head>
#   <body>
#     <div>
#       <h1>Hello, World!</h1>
#       <p>This is a paragraph.</p>
#     </div>
#   </body>
# </html>

Pretty printing is perfect for:

  • Development and debugging
  • Generating human-readable HTML files
  • Documentation and tutorials
  • Inspecting complex structures

CSS Style Helpers

NitroUI provides convenient methods for working with inline CSS styles:

from nitro_ui import *

# Create element and add styles
div = Div("Styled content")

# Add single style
div.add_style("color", "blue")
div.add_style("font-size", "16px")

# Add multiple styles at once
div.add_styles({
    "background-color": "#f0f0f0",
    "padding": "20px",
    "margin": "10px",
    "border-radius": "5px"
})

# Get a specific style value
color = div.get_style("color")  # Returns "blue"

# Remove a style
div.remove_style("margin")

# Result: <div style="color: blue; font-size: 16px; background-color: #f0f0f0; padding: 20px; border-radius: 5px">Styled content</div>

External Stylesheet and Theming

NitroUI 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.

Basic StyleSheet Usage

from nitro_ui import *
from nitro_ui.styles import CSSStyle, StyleSheet

# Create a stylesheet
stylesheet = StyleSheet()

# Register reusable styles
btn_primary = stylesheet.register("btn-primary", CSSStyle(
    background_color="#007bff",
    color="white",
    padding="10px 20px",
    border_radius="5px",
    border="none",
    cursor="pointer",
    _hover=CSSStyle(background_color="#0056b3")  # Pseudo-selector
))

# Use in HTML
page = HTML(
    Head(
        Title("My Page"),
        Style(stylesheet.render())  # Insert generated CSS
    ),
    Body(
        Button("Click Me", class_name=btn_primary)
    )
)

Theming Support

NitroUI includes three preset themes (Modern, Classic, Minimal) with full CSS variable support:

from nitro_ui.styles import Theme, StyleSheet, CSSStyle

# Use a preset theme
theme = Theme.modern()  # or Theme.classic() or Theme.minimal()

# Create stylesheet with theme
stylesheet = StyleSheet(theme=theme)

# Register styles using theme variables
btn = stylesheet.register("btn", CSSStyle(
    background_color="var(--color-primary)",
    color="var(--color-white)",
    padding="var(--spacing-md)",
    border_radius="6px",
    _hover=CSSStyle(background_color="var(--color-primary-dark)")
))

card = stylesheet.register("card", CSSStyle(
    background_color="var(--color-white)",
    padding="var(--spacing-lg)",
    border_radius="8px",
    box_shadow="0 1px 3px rgba(0, 0, 0, 0.1)"
))

BEM Naming Convention

Built-in support for BEM (Block Element Modifier) naming:

from nitro_ui.styles import StyleSheet, CSSStyle

stylesheet = StyleSheet()

# Register with BEM naming
card = stylesheet.register_bem("card", style=CSSStyle(
    background="white",
    padding="20px"
))

card_header = stylesheet.register_bem("card", element="header", style=CSSStyle(
    font_weight="bold"
))

card_featured = stylesheet.register_bem("card", modifier="featured", style=CSSStyle(
    border="2px solid blue"
))

# Generates: .card, .card__header, .card--featured

Responsive Breakpoints

Add responsive styles with breakpoint support:

from nitro_ui.styles import CSSStyle, StyleSheet

stylesheet = StyleSheet()

container = stylesheet.register("container", CSSStyle(
    padding="10px",
    _sm=CSSStyle(padding="15px", max_width="640px"),
    _md=CSSStyle(padding="20px", max_width="768px"),
    _lg=CSSStyle(padding="30px", max_width="1024px")
))

# Generates media queries automatically

Combining with Inline Styles

You can mix stylesheet classes with inline style overrides:

# Register base style
btn = stylesheet.register("btn", CSSStyle(
    padding="10px 20px",
    border_radius="5px"
))

# Use base class + inline overrides
Button("Custom", class_name=btn).add_styles({
    "background-color": "#ff0000",
    "font-size": "18px"
})

Key Features:

  • Snake_case to kebab-case conversion (background_color → background-color)
  • Pseudo-selector support (:hover, :active, :focus, etc.)
  • Responsive breakpoints with media queries
  • Theme system with CSS variables
  • BEM naming convention support
  • JSON serialization for saving/loading styles
  • Combines seamlessly with existing inline styles

See the examples/stylesheet_example.py file for complete working examples.

Method Chaining

All builder methods return self, enabling fluent method chaining:

from nitro_ui import *

# Chain multiple operations together
container = (Div()
    .add_attribute("id", "main-container")
    .add_attribute("class", "wrapper")
    .add_style("background", "#fff")
    .add_styles({"padding": "20px", "margin": "0 auto"})
    .append(H1("Welcome"))
    .append(Paragraph("This is the main content."))
    .append(Paragraph("Another paragraph here.")))

print(container.render())

Chainable methods:

  • append() - Add children
  • prepend() - Add children at the beginning
  • add_attribute() - Add single attribute
  • add_attributes() - Add multiple attributes
  • remove_attribute() - Remove an attribute
  • add_style() - Add single CSS style
  • add_styles() - Add multiple CSS styles
  • remove_style() - Remove a CSS style
  • clear() - Remove all children
  • remove_all() - Remove children matching a condition

Context Manager Support

Use elements as context managers for cleaner, more intuitive nesting:

from nitro_ui import *

# Using context managers
with Div(id="container", class_name="main") as container:
    with Section(class_name="content") as section:
        section.append(H1("Title"))
        section.append(Paragraph("Content goes here"))

    container.append(section)
    container.append(Footer(Paragraph("Footer text")))

print(container.render())

Fragment Support

Use Fragment to group elements without adding a wrapper tag:

from nitro_ui import *

# Without Fragment - adds extra div wrapper
content = Div(
    H1("Title"),
    Paragraph("Content")
)
# Output: <div><h1>Title</h1><p>Content</p></div>

# With Fragment - no wrapper tag
content = Fragment(
    H1("Title"),
    Paragraph("Content")
)
# Output: <h1>Title</h1><p>Content</p>

# Perfect for conditional rendering
def render_items(items, show_header=True):
    fragment = Fragment()

    if show_header:
        fragment.append(H2("Items List"))

    for item in items:
        fragment.append(Paragraph(item))

    return fragment

# Use in parent element
page = Div(
    render_items(["Item 1", "Item 2", "Item 3"], show_header=True)
)
# Output: <div><h2>Items List</h2><p>Item 1</p><p>Item 2</p><p>Item 3</p></div>

Fragment use cases:

  • Conditional rendering without wrapper divs
  • Returning multiple elements from functions
  • List composition and iteration
  • Cleaner component architecture

HTML Parsing

NitroUI can parse raw HTML strings and convert them into NitroUI elements. This is useful for importing existing HTML, migrating from other tools, or working with HTML from external sources.

from nitro_ui import from_html, HTMLElement

# Parse a single HTML element
html_string = '<div class="container"><h1>Hello World</h1><p>Welcome to NitroUI</p></div>'
element = from_html(html_string)

# Now you can work with it like any NitroUI element
element.add_style("padding", "20px")
element.append(Paragraph("Added via NitroUI"))

print(element.render())
# Output: <div class="container" style="padding: 20px"><h1>Hello World</h1><p>Welcome to NitroUI</p><p>Added via NitroUI</p></div>

# Alternative: use the class method
element = HTMLElement.from_html(html_string)

Parsing HTML fragments (multiple root elements):

from nitro_ui import from_html

# HTML with multiple root elements
html_fragment = '''
<h1>Welcome</h1>
<p>First paragraph</p>
<p>Second paragraph</p>
'''

# Parse as fragment (returns list of elements)
elements = from_html(html_fragment, fragment=True)

# Work with each element
for el in elements:
    print(el.tag, el.text)
# Output:
# h1 Welcome
# p First paragraph
# p Second paragraph

# Combine with other NitroUI features
container = Div()
for el in elements:
    container.append(el)

print(container.render())
# Output: <div><h1>Welcome</h1><p>First paragraph</p><p>Second paragraph</p></div>

Features:

  • Handles all HTML5 elements including self-closing tags (br, img, hr, etc.)
  • Preserves attributes, including data-* attributes
  • Converts class attribute to class_name automatically
  • Supports nested structures of any depth
  • Handles HTML entities and special characters
  • No external dependencies (uses Python's built-in html.parser)

Use cases:

  • Import existing HTML into your website builder
  • Migrate from other HTML generation tools
  • Parse HTML templates from external sources
  • Convert HTML snippets to NitroUI for manipulation
  • Testing and validation workflows
  • Combining static HTML with dynamic NitroUI generation

JSON Serialization

NitroUI supports JSON serialization and deserialization, making it perfect for drag-and-drop website builders, saving UI states, or transmitting page structures over APIs.

from nitro_ui import *

# Build a page structure
page = Div(id="page", class_name="container")
page.append(
    H1("Welcome"),
    Section(
        Paragraph("This is a paragraph"),
        Paragraph("Another paragraph", class_name="highlight")
    )
)

# Serialize to JSON (for saving/storing)
json_data = page.to_json(indent=2)
print(json_data)

# Later... deserialize from JSON (for loading)
from nitro_ui.core.element import HTMLElement
restored_page = HTMLElement.from_json(json_data)

# Generate HTML (output will be identical)
print(str(restored_page))

The JSON format is simple and clean:

{
  "tag": "div",
  "self_closing": false,
  "attributes": {
    "id": "page",
    "class": "container"
  },
  "text": "",
  "children": []
}

Use cases:

  • Save/load website layouts to/from a database
  • Implement undo/redo functionality
  • Store pre-built templates as JSON
  • Version control for page structures
  • API communication between frontend and backend
  • Drag-and-drop website builders

Great For

  • CLI tools
  • Drag-and-drop website builders
  • Site builders with save/load functionality
  • Web frameworks
  • Alternative to heavy template engines
  • Static site generators
  • Documentation generators
  • LLM's and AI tooling that generate interfaces dynamically
  • Creating frontends for headless platforms (CMS/CRM etc)
  • Applications requiring UI state serialization

LLM Guide

For AI assistants and code generation tools, we provide a comprehensive technical reference in LLM_GUIDE.md with:

  • Complete API documentation with exact method signatures
  • Type hints and return values for all methods
  • 10 common patterns with code examples
  • Error handling and security best practices
  • Quick reference cheat sheet

Examples

FastAPI

from fastapi import FastAPI
from nitro_ui import *

app = FastAPI()

@app.get("/")
async def root():
    return HTML(
        Head(
            Title("My Page")
        ),
        Body(
            Section(
                H1("Hello, World!"),
                Paragraph("This is a test document.")
            )
        )
    )

Django

from django.http import HttpResponse
from nitro_ui import *

def index(request):
    return HttpResponse(HTML(
        Head(
            Title("My Page"),
            Meta(charset="utf-8"),
            Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
            HtmlLink(rel="stylesheet", href="style.css"),
            Script(src="script.js")
        ),
        Body(
            Section(
                H1("Hello, World!"),
                Paragraph("This is a paragraph."),
                Paragraph("This is another paragraph.")
            )
        )
    ))

Flask

from flask import Flask
from nitro_ui import *

app = Flask(__name__)

@app.route("/")
def index():
    return HTML(
        Head(
            Title("My Page")
        ),
        Body(
            Section(
                H1("Hello, World!"),
                Paragraph("This is a test document.")
            )
        )
    )

Test Coverage

NitroUI has full test coverage. To run the tests locally, use:

pytest

Element Methods:

Element Manipulation:

  • instance.prepend() - Prepend children (returns self for chaining)
  • instance.append() - Append children (returns self for chaining)
  • instance.filter() - Filter children by condition
  • instance.remove_all() - Remove children matching condition (returns self for chaining)
  • instance.clear() - Remove all children (returns self for chaining)
  • instance.pop() - Remove and return child at index
  • instance.first() - Get first child
  • instance.last() - Get last child
  • instance.replace_child() - Replace child at index
  • instance.clone() - Deep copy of element
  • instance.find_by_attribute() - Find child by attribute value
  • instance.count_children() - Count direct children

Attribute Management:

  • instance.add_attribute() - Add single attribute (returns self for chaining)
  • instance.add_attributes() - Add multiple attributes (returns self for chaining)
  • instance.remove_attribute() - Remove attribute (returns self for chaining)
  • instance.get_attribute() - Get attribute value
  • instance.has_attribute() - Check if attribute exists
  • instance.get_attributes() - Get all or specific attributes
  • instance.generate_id() - Generate unique ID if not present

CSS Style Management:

  • instance.add_style() - Add single CSS style (returns self for chaining)
  • instance.add_styles() - Add multiple CSS styles (returns self for chaining)
  • instance.get_style() - Get specific style value
  • instance.remove_style() - Remove CSS style (returns self for chaining)

Rendering:

  • instance.render(pretty=False) - Render to HTML string (pretty=True for indented output)
  • instance.to_dict() - Convert to dictionary
  • instance.to_json(indent=None) - Serialize to JSON string
  • HTMLElement.from_dict(data) - Reconstruct from dictionary (class method)
  • HTMLElement.from_json(json_str) - Reconstruct from JSON string (class method)
  • HTMLElement.from_html(html_str, fragment=False) - Parse HTML string to NitroUI element(s) (class method)
  • from_html(html_str, fragment=False) - Parse HTML string to NitroUI element(s) (function)

Events

  • instance.on_load()
  • instance.on_before_render()
  • instance.on_after_render()
  • instance.on_unload()

Element Properties

  • instance.tag
  • instance.children
  • instance.text
  • instance.attributes
  • instance.self_closing

Modules

Module Purpose Key Elements
nitro_ui.tags.form Common HTML form elements Form, Input, Button, Select, Textarea
nitro_ui.tags.html Structural HTML document elements HTML, Head, Body, Title, Meta, Script
nitro_ui.tags.layout Layout related HTML tags Div, Section, Header, Nav, Footer, Main
nitro_ui.tags.lists HTML list elements UnorderedList, OrderedList, ListItem
nitro_ui.tags.media Media related HTML elements Image, Video, Audio, Figure, Canvas
nitro_ui.tags.table HTML table elements Table, TableRow, TableHeaderCell, TableDataCell
nitro_ui.tags.text HTML text elements H1-H6, Paragraph, Span, Strong, Em

Importing

Instead of importing the entire module, you can selectively use only the elements you need like this:

# Instead of importing everything
from nitro_ui import *

# Import selectively for better performance and clarity
from nitro_ui.tags.form import Form, Button, Input
from nitro_ui.tags.html import HTML, Head, Body, Title
from nitro_ui.tags.layout import Div, Section
from nitro_ui.tags.text import H1, Paragraph

nitro_ui.tags.form

  • Form()
  • Input()
  • Label()
  • Textarea()
  • Select()
  • Option()
  • Button()
  • Fieldset()
  • Legend()
  • Optgroup()
  • Output()
  • Progress()
  • Meter()

nitro_ui.tags.html

  • HTML()
  • Head()
  • Body()
  • Title()
  • Meta()
  • Base()
  • HtmlLink() (use instead of Link() to avoid conflicts)
  • Script()
  • Style()
  • Noscript()
  • IFrame()

nitro_ui.tags.layout

  • Div()
  • Section()
  • Article()
  • Aside()
  • Header()
  • Nav()
  • Footer()
  • HorizontalRule()
  • Main()
  • Details()
  • Summary()
  • Dialog()

nitro_ui.tags.lists

  • UnorderedList()
  • OrderedList()
  • ListItem()
  • Datalist()
  • DescriptionDetails()
  • DescriptionList()
  • DescriptionTerm()

nitro_ui.tags.media

  • Image()
  • Video()
  • Audio()
  • Source()
  • Track()
  • Picture()
  • Figure()
  • Figcaption()
  • Canvas()
  • Embed()
  • Object()
  • Param()
  • Map()
  • Area()

nitro_ui.tags.table

  • Table()
  • TableFooter()
  • TableHeaderCell()
  • TableHeader()
  • TableBody()
  • TableDataCell()
  • TableRow()
  • Caption()
  • Col()
  • Colgroup()

nitro_ui.tags.text

  • H1()
  • H2()
  • H3()
  • H4()
  • H5()
  • H6()
  • Paragraph()
  • Blockquote()
  • Pre()
  • Quote()
  • Cite()
  • Em()
  • Italic()
  • Span()
  • Strong()
  • Bold()
  • Abbr()
  • Link()
  • Small()
  • Superscript()
  • Subscript()
  • Time()
  • Code()
  • Del()
  • Ins()
  • Strikethrough()
  • Underline()
  • Kbd()
  • Samp()
  • Var()
  • Mark()
  • Dfn()
  • Br()
  • Wbr()

Creating your own elements or components

from nitro_ui import *

class MyTag(HTMLElement):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **{**kwargs, "tag": "mytag"})

        self.add_attributes([
            ("id", "mycustomid"),
            ("aria-controls", "menu"),
        ])

    def on_load(self) -> None:
        print("The on_load event has been called")

    def on_before_render(self) -> None:
        print("The on_before_render event has been called")

    def on_after_render(self) -> None:
        print("The on_after_render event has been called")


mytag = MyTag(
    Div(
        Paragraph("Hello World!")
    )
)

print(mytag.render())

This will produce:

<mytag id="mycustomid" aria-controls="menu">
  <div>
    <p>Hello World!</p>
  </div>
</mytag>

You 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.

You 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.

Contributions

Contributions, suggestions and improvements are all welcome.

Developing NitroUI

  1. Create a virtual environment
python3 -m venv .venv 
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install --upgrade pip
  1. Install the dev dependencies:
pip install ".[dev]"
  1. Run the tests:
pytest

When you are happy with your changes, create a merge request.

License

Please see LICENSE for licensing details.

Author

github.com/sn

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nitro_ui-1.0.0.tar.gz (51.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nitro_ui-1.0.0-py3-none-any.whl (29.6 kB view details)

Uploaded Python 3

File details

Details for the file nitro_ui-1.0.0.tar.gz.

File metadata

  • Download URL: nitro_ui-1.0.0.tar.gz
  • Upload date:
  • Size: 51.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nitro_ui-1.0.0.tar.gz
Algorithm Hash digest
SHA256 8094d27d53e596cf99e9e257f3bc4045ff428f0332cbaa2c19c48484bd6421e9
MD5 adf07af26f7cf3393abaf993f119c755
BLAKE2b-256 f0e8f0b4b6cbb33fa717fd02dbf06b0d78294b253b3c676f9ec6b28caf8b0b0c

See more details on using hashes here.

File details

Details for the file nitro_ui-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: nitro_ui-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 29.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for nitro_ui-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d5a33a368a0161541cf3ae1b7750b2e5a9d55aed33ca714bb33b0f2ff0e3f4ee
MD5 d68f64b0d813253ed1a63c02b0c4526f
BLAKE2b-256 7905f5aa2702a752dc31a542940fd2b1e4d52a25219c75c94789c6a06854f493

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page