Metadata-Version: 2.4
Name: htmforge
Version: 0.2.1
Summary: Type-safe, composable UI components for Python — server-side rendered, HTMX-first.
Project-URL: Homepage, https://github.com/mondi04/htmforge
Project-URL: Repository, https://github.com/mondi04/htmforge
Project-URL: Bug Tracker, https://github.com/mondi04/htmforge/issues
Project-URL: Documentation, https://mondi04.github.io/htmforge/
Project-URL: Changelog, https://github.com/mondi04/htmforge/blob/main/docs/changelog.md
Author-email: mondi04 <206898989+mondi04@users.noreply.github.com>
License: MIT License with Commons Clause
        
        Copyright (c) 2026 mondi04
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        COMMONS CLAUSE — LICENSE CONDITION v1.0
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        The Software is provided to you by the Licensor under the MIT License
        (as defined below), subject to the following additional condition:
        
        Without limiting other conditions in the License, the grant of rights
        under the License will not include, and the License does not grant to
        you, the right to Sell the Software or incorporate it into a commercial
        product or service whose primary value derives from the functionality of
        this Software.
        
        For purposes of the foregoing, "Sell" means practicing any or all of
        the rights granted to you under the License to provide to third parties,
        for a fee or other consideration (including without limitation fees for
        hosting, consulting, or support services related to the Software), a
        product or service whose value derives, entirely or substantially, from
        the functionality of this Software.
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        LARGE ORGANIZATION RESTRICTION
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        Any use by organizations with annual revenue or funding exceeding
        USD 1,000,000 (one million US dollars), or by organizations employing
        more than 100 persons, requires a separate written commercial license
        agreement from the copyright holder.
        
        This includes, but is not limited to, corporations, AI companies,
        cloud providers, and technology platforms regardless of their legal
        structure or country of incorporation.
        
        For commercial licensing inquiries, contact the author via:
        https://github.com/mondi04
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        PERMITTED USE
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        The following uses are explicitly permitted at no cost:
        
          - Personal, private, and non-commercial projects
          - Open source projects (provided they carry this same license)
          - Educational and research use
          - Small businesses and freelancers with annual revenue below USD 1,000,000
          - Internal tooling at organizations below the thresholds above
        
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        MIT LICENSE
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
        
        Subject to the Commons Clause and Large Organization Restriction above,
        the following MIT License terms apply:
        
        Permission is hereby granted, free of charge, to any person obtaining a
        copy of this software and associated documentation files (the "Software"),
        to deal in the Software without restriction, including without limitation
        the rights to use, copy, modify, merge, publish, distribute, sublicense,
        and/or sell copies of the Software, and to permit persons to whom the
        Software is furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included
        in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
        OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Keywords: components,django,fastapi,flask,html,htmx,jinja2-alternative,pydantic,server-side-rendering,type-safe,web
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: markupsafe>=2.1
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.9; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-autorefs>=1.0; extra == 'docs'
Requires-Dist: mkdocs-git-revision-date-localized-plugin>=1.2; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
Description-Content-Type: text/markdown

<!-- badges -->
[![PyPI version](https://img.shields.io/pypi/v/htmforge.svg)](https://pypi.org/project/htmforge/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT + Commons Clause](https://img.shields.io/badge/License-MIT%20%2B%20Commons%20Clause-blue.svg)](https://github.com/mondi04/htmforge/blob/main/LICENSE)
[![mypy strict](https://img.shields.io/badge/mypy-strict-brightgreen.svg)](https://mypy.readthedocs.io/)
[![CI](https://github.com/mondi04/htmforge/actions/workflows/ci.yml/badge.svg)](https://github.com/mondi04/htmforge/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/badge/docs-mondi04.github.io%2Fhtmforge-orange.svg)](https://mondi04.github.io/htmforge/)

# htmforge

Type-safe, composable UI components for Python. Server-side rendered,
HTMX-first, framework-agnostic.

## Why htmforge?

- Type-safe props via Pydantic v2: props are validated on construction and
  on assignment.
- Small Element primitives with safe rendering: `Element.to_html()`
  escapes text and maps Python attrs to HTML (e.g. `cls`→`class`).
- First-class HTMX support: typed enums and helpers for `hx-*` attrs.
- Framework adapters: `to_fastapi()`, `to_flask()`, `to_django()` on
  components for easy integration.

## Installation

```bash
pip install htmforge
```

## Quickstart (Flask)

Copy-pasteable minimal Flask example using a `Page` and a `DataTable`.

```python
from flask import Flask

from htmforge.components.page import Page
from htmforge.components import DataTable
from htmforge.core.element import Element
from htmforge.elements import div, h1

app = Flask(__name__)

class UsersPage(Page):
    users: list[list[str]]

    def _body_content(self) -> list[Element | str | None]:
        return [
            div(
                h1("Users"),
                DataTable(headers=["Name", "Email"], rows=self.users),
            )
        ]

@app.route("/users")
def users():
    rows = [["Ada Lovelace", "ada@example.com"]]
    return UsersPage(title="Users", users=rows).to_flask()

if __name__ == "__main__":
    app.run(debug=True)
```

## Elements

`htmforge.elements` exposes small factory functions for HTML tags. These
map Pythonic attribute names to HTML and escape text safely. Example:

```python
from htmforge.elements import div, span, input

el = div(
    span("Name:"),
    input(type="search", name="q", hx_get="/search", cls="search"),
    cls="form-row",
)
print(el.to_html())
```

The `hx_get` argument renders as `hx-get`, and text is escaped by
default to prevent XSS.

## Components

| Component   | Description                                      | Import |
|-------------|--------------------------------------------------|--------|
| Alert       | Dismissible info/success/warning/error box       | `from htmforge.components import Alert` |
| Badge       | Small inline label with variant classes          | `from htmforge.components import Badge` |
| Breadcrumb  | Ordered nav with `aria-current` for current item | `from htmforge.components import Breadcrumb` |
| DataTable   | Table with optional HTMX reloading               | `from htmforge.components import DataTable` |
| FormField   | Label + input + optional error block             | `from htmforge.components import FormField` |
| Modal       | Trigger button + `<dialog>` overlay (HTMX body)  | `from htmforge.components import Modal` |
| Page        | Abstract full-page component (adds DOCTYPE)      | `from htmforge.components.page import Page` |
| Pagination  | Page links + prev/next, supports HTMX targets    | `from htmforge.components import Pagination` |
| SearchInput | Search input with `keyup` debounce via HTMX      | `from htmforge.components import SearchInput` |

## HTMX integration

Typed enums live in `htmforge.htmx`: `HxSwap`, `HxTrigger`, `HxTarget`,
and `HxPushUrl`. They render the correct attribute values. Example:

```python
from htmforge.elements import button
from htmforge.htmx import HxSwap, HxTarget

btn = button(
    "Delete",
    hx_delete="/items/1",
    hx_swap=HxSwap.OUTER_HTML,
    hx_target=HxTarget.CLOSEST_TR,
)
```

Use `hx_keyup_delay(ms)` to produce `keyup delay:{ms}ms` trigger strings
for debounced search inputs.

## Framework support

FastAPI example:

```python
from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def index():
    return UsersPage(title="Home").to_html()
```

Flask example (adapter shown above uses `to_flask()`):

```python
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return UsersPage(title="Home").to_flask()
```

Django example:

```python
def index(request):
    return UsersPage(title="Home").to_django()
```

## License

This project is licensed under the MIT License with the Commons Clause
condition. It is free for personal projects, open source projects, and
small businesses (see `LICENSE`). Organizations with annual revenue or
funding over USD 1,000,000 or more than 100 employees require a
separate commercial license from the author.

## Contributing

See `CONTRIBUTING.md` and the docs site at
https://mondi04.github.io/htmforge/ for contribution guidelines.

