Skip to main content

Hyperclass

Subclass the web.

Hyperclass is a small experiment in building interactive web applications as Python class hierarchies.

HTML elements are Python base classes. Python subclasses become CSS classes. Styles follow inheritance. Routes are methods. Decorated handlers are URLs. Interaction is ordinary HTTP over WSGI, with htmx 4 in the browser.

pip install hyperclass

Sixty-second tour

from dataclasses import dataclass

from hyperclass import (
    App, button, css, div, form, get, grid, hx, input, outer_morph,
    post, rem,
)


class card(div):
    style = css(
        display=grid,
        gap=1 * rem,
        padding=1.25 * rem,
        border="1px solid #ddd",
        border_radius=.75 * rem,
    )


class guest_form(form):
    style = css(display=grid, gap=.75 * rem)

    def content(self):
        yield input(name="name", placeholder="Your name", required=True)
        yield button("Say hello", type="submit")


@dataclass
class Guest:
    name: str


class guestbook(App):
    @get("/")
    def index(self, request):
        return card(
            "Who are you?",
            guest_form(
                hx=hx.post(
                    guestbook.create,
                    target=card,
                    swap=outer_morph,
                )
            ),
        )

    @post("/guests")
    def create(self, request, form: Guest):
        return card(f"Hello, {form.name}!")


app = guestbook(title="Guestbook")

Run it:

python -m hyperclass myapp:app

Then open http://127.0.0.1:8000. There is no JavaScript build, template language, ASGI dependency, or CSS file hidden elsewhere.

HTML classes are Python classes

Every built-in element can be subclassed:

from hyperclass import css, div, grid, orange, rem


class card(div):
    style = css(display=grid, gap=1 * rem, padding=1.25 * rem)


class warning_card(card):
    style = css(
        border_color=orange,
        background=orange.fade(0.08),
    )

Calling:

warning_card("Something happened")

produces ordinary, inspectable HTML:

<div class="card warning-card">Something happened</div>

The first built-in HTML ancestor determines the tag. Each semantic subclass contributes a CSS class. snake_case becomes kebab-case.

Multiple inheritance composes behavior and styles:

class compact:
    style = css(padding=.5 * rem)


class clickable:
    style = css(cursor="pointer")


class result_card(card, compact, clickable):
    pass
<div class="card compact clickable result-card"></div>

Components use normal Python state and methods:

from hyperclass import strong


class greeting(card):
    def __init__(self, name):
        self.name = name

    def content(self):
        yield "Hello, "
        yield strong(self.name)

Text and attribute values are escaped by default. markup(...) is the explicit escape hatch for trusted HTML.

CSS is Python too

Base styles, pseudo-states, and media rules live on the component:

from hyperclass import button, css, media, rem


class primary_button(button):
    style = css(
        padding=".7rem 1rem",
        background="#6d28d9",
        color="white",
        border=0,
        border_radius=.5 * rem,
    )
    hover = css(background="#5b21b6")
    focus_visible = css(outline="3px solid #c4b5fd")
    narrow = media(max_width=40 * rem, width="100%")

Pages collect only the rules used by their element tree. Python inheritance and the CSS cascade cooperate instead of imitating one another.

Classes and IDs are selectors

Classes can be used directly anywhere a selector is expected:

hx.get(search, target=result_card)
closest(card)

IDs are lazy, interned Python objects:

from hyperclass import id, span

span("3 unread", id=id.unread_count)
hx.get(count, target=id.unread_count)

assert id.unread_count is id.unread_count

As an HTML attribute, id.unread_count renders as unread-count. As a selector, it renders as #unread-count.

Routes are references, not strings

Application subclasses collect decorated method routes:

from hyperclass import App, get, patch


class bookmarks(App):
    @get("/")
    def index(self, request):
        return bookmark_list(...)

    @patch("/bookmarks/<int:bookmark_id>")
    def toggle(self, request, bookmark_id):
        return bookmark_card(...)

Decorated handlers retain their route metadata:

bookmarks.toggle.url(bookmark_id=42)
# '/bookmarks/42'

hx.patch(
    bookmarks.toggle,
    bookmark_id=42,
    target=bookmark_card,
)

Typed path parameters are converted before the handler runs. Query strings can be attached with .url(query={...}) or the query= option on an htmx request.

Forms bind to dataclasses

Annotate a route parameter with a dataclass and Hyperclass builds it from the submitted form:

@dataclass
class NewBookmark:
    url: str
    title: str = ""


class bookmarks(App):
    @post("/bookmarks")
    def create(self, request, form: NewBookmark):
        self.store.add(form.url, form.title)
        return bookmark_list(...)

Binding supports strings, integers, floats, booleans, optional values, and lists or tuples of those values. Dataclass defaults remain defaults. Invalid or missing required values produce a 400 Bad Request; application validation can return a more specific Response.

The underlying values remain available as request.form, request.query, .get(...), .getlist(...), and .int(...) when explicit parsing is clearer.

WSGI and htmx 4

A Hyperclass application is a normal WSGI callable. Use the standard-library development server:

python -m hyperclass package.module:app
python -m hyperclass package.module:app --host 0.0.0.0 --port 9000

Production can use any WSGI server. Returning an element from an ordinary browser request wraps it in a complete page. Returning the same element to an htmx request sends only the fragment to swap.

Page(...) controls the document explicitly. Pages include a pinned htmx 4 asset from jsDelivr. htmx 4 <hx-partial> responses can update several object-selected regions from one request.

Try the examples

Clone the repository and run the persistent SQLite bookmark inbox:

git clone https://github.com/grantjenks/python-hyperclass
cd python-hyperclass
python -m hyperclass examples.bookmarks:app

The bookmark app adds, searches, filters, edits, marks, and deletes bookmarks. Its implementation is Python plus SQLite, WSGI, generated CSS, and htmx. It is also a compact integration test for the framework's ideas.

For the smallest example:

python -m hyperclass examples.counter:app

Principles

  • Python is the authoring language. Control flow, composition, inheritance, validation, and reuse are ordinary Python.
  • The browser remains the browser. Hyperclass emits standard HTML and CSS rather than recreating the DOM on the server.
  • Classes mean classes. Python inheritance has a visible relationship to HTML classes and the CSS cascade.
  • HTTP is the state boundary. There is no hydration protocol or hidden client component lifecycle.
  • Output should be boring. Generated markup stays readable in View Source and DevTools.
  • Small is a feature. Prefer the standard library, WSGI, and a pinned htmx asset over a framework stack.

Status

Hyperclass is deliberately pre-alpha: useful enough to build small applications and young enough for its API to change. Python 3.10 through 3.14 are tested.

Apache-2.0 licensed.

Download files

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

Source Distribution

hyperclass-0.0.3.tar.gz (26.1 kB view details)

Uploaded Source

Built Distribution

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

hyperclass-0.0.3-py3-none-any.whl (20.7 kB view details)

Uploaded Python 3

File details

Details for the file hyperclass-0.0.3.tar.gz.

File metadata

  • Download URL: hyperclass-0.0.3.tar.gz
  • Upload date:
  • Size: 26.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for hyperclass-0.0.3.tar.gz
Algorithm Hash digest
SHA256 551db131a73d8668412345003173dd2d12b61a6c94d330d8572decd4089349b9
MD5 306b70229708efc7796c7254484b7919
BLAKE2b-256 c959914c8cf2babf0940ed41c6aec414f52b7ce6d0e32201c9548fbc2273c631

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyperclass-0.0.3.tar.gz:

Publisher: release.yml on grantjenks/python-hyperclass

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hyperclass-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: hyperclass-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 20.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for hyperclass-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b0a79a1a70ac0c2d760f43ed34903a865f2b3cc309e5f7996acccaf43b3fa05f
MD5 de57368d581ca04f4b875f65bd50c130
BLAKE2b-256 3583eb7dd73311f10828ee0a882bdf6b91ec542589ee52a9d3b90960807b4b1d

See more details on using hashes here.

Provenance

The following attestation bundles were made for hyperclass-0.0.3-py3-none-any.whl:

Publisher: release.yml on grantjenks/python-hyperclass

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

0.0.5

2 files

0.0.4

2 files

This release

0.0.3 This release

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page