diff --git a/README.md b/README.md
index 835034e..4c26516 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ Jinjalume is an open-source, HTML-first component kit for developers who want re
- A copy-paste [component reference](docs/components.md) with signatures and rendered markup examples
- Tailwind CSS v4 build setup
- A working Flask demo
+- Copy-paste UI blocks for authentication and admin dashboards
- Contributor documentation, issue templates, and continuous integration
## Quick start
@@ -97,6 +98,10 @@ The demo includes a light/dark toggle backed by semantic CSS custom properties.
The `/htmx` demo shows an optional HTMX enhancement around a normal Flask form. The form remains usable without JavaScript or HTMX, and HTMX is loaded only by that demo page. The core package has no HTMX dependency.
+## UI blocks
+
+The demo also includes realistic application screens at `/blocks`, `/login`, `/signup`, and `/admin`. The reusable block macros live under `jinjalume/blocks/` and compose the core components into auth flows and a responsive admin console. See [docs/blocks.md](docs/blocks.md) for signatures and copy-paste examples.
+
## Development
```bash
diff --git a/demo/app.py b/demo/app.py
index 0b296cf..994d30c 100644
--- a/demo/app.py
+++ b/demo/app.py
@@ -11,6 +11,171 @@ def index():
return render_template("index.html")
+@app.get("/blocks")
+def blocks():
+ return render_template("blocks.html")
+
+
+@app.route("/login", methods=["GET", "POST"])
+def login():
+ email = request.form.get("email", "").strip()
+ errors = {}
+ form_error = None
+ success_message = None
+
+ if request.method == "POST":
+ if not email:
+ errors["email"] = "Enter your email address."
+ if not request.form.get("password"):
+ errors["password"] = "Enter your password."
+ if errors:
+ form_error = "Review the fields below and try again."
+ else:
+ success_message = "Demo sign-in accepted. No account was created."
+
+ return render_template(
+ "login.html",
+ email=email,
+ remember=bool(request.form.get("remember")),
+ email_error=errors.get("email"),
+ password_error=errors.get("password"),
+ form_error=form_error,
+ success_message=success_message,
+ )
+
+
+@app.route("/signup", methods=["GET", "POST"])
+def signup():
+ name = request.form.get("name", "").strip()
+ email = request.form.get("email", "").strip()
+ errors = {}
+ form_error = None
+ success_message = None
+
+ if request.method == "POST":
+ password = request.form.get("password", "")
+ confirmation = request.form.get("password_confirmation", "")
+ if not name:
+ errors["name"] = "Enter your full name."
+ if not email:
+ errors["email"] = "Enter your email address."
+ if len(password) < 8:
+ errors["password"] = "Use at least 8 characters."
+ if password != confirmation:
+ errors["confirmation"] = "Passwords must match."
+ if not request.form.get("terms"):
+ errors["terms"] = "Accept the terms to continue."
+ if errors:
+ form_error = "Review the fields below and try again."
+ else:
+ success_message = "Demo account created. No data was saved."
+
+ return render_template(
+ "signup.html",
+ name=name,
+ email=email,
+ name_error=errors.get("name"),
+ email_error=errors.get("email"),
+ password_error=errors.get("password"),
+ confirmation_error=errors.get("confirmation"),
+ terms_error=errors.get("terms"),
+ form_error=form_error,
+ success_message=success_message,
+ )
+
+
+@app.get("/admin")
+def admin():
+ return render_template(
+ "admin.html",
+ nav_items=[
+ {"label": "Overview", "href": "/admin"},
+ {"label": "Customers", "href": "#customers", "badge": "24"},
+ {"label": "Orders", "href": "#orders"},
+ {"label": "Analytics", "href": "#analytics"},
+ {"label": "Settings", "href": "#settings"},
+ ],
+ metrics=[
+ {
+ "label": "Total revenue",
+ "value": "$48,294",
+ "change": "+12.8%",
+ "note": "Compared with last month",
+ "icon": "$",
+ },
+ {
+ "label": "Active customers",
+ "value": "2,431",
+ "change": "+8.2%",
+ "note": "Across all workspaces",
+ "icon": "↗",
+ },
+ {
+ "label": "Open tickets",
+ "value": "18",
+ "change": "-4.5%",
+ "trend": "down",
+ "note": "Compared with last week",
+ "icon": "!",
+ },
+ {
+ "label": "Conversion rate",
+ "value": "6.24%",
+ "change": "+1.4%",
+ "note": "From 38,910 sessions",
+ "icon": "%",
+ },
+ ],
+ orders=[
+ {
+ "customer": "Olivia Martin",
+ "email": "olivia@example.com",
+ "amount": "$1,240.00",
+ "status": "Paid",
+ "date": "Sep 12, 2026",
+ },
+ {
+ "customer": "Phoenix Baker",
+ "email": "phoenix@example.com",
+ "amount": "$860.00",
+ "status": "Processing",
+ "date": "Sep 11, 2026",
+ },
+ {
+ "customer": "Lana Byrd",
+ "email": "lana@example.com",
+ "amount": "$540.00",
+ "status": "Paid",
+ "date": "Sep 11, 2026",
+ },
+ {
+ "customer": "Demi Wilkinson",
+ "email": "demi@example.com",
+ "amount": "$320.00",
+ "status": "Refunded",
+ "date": "Sep 10, 2026",
+ },
+ ],
+ activity=[
+ {
+ "label": "New workspace created",
+ "detail": "Acme Inc. joined the Pro plan",
+ "time": "12 minutes ago",
+ },
+ {
+ "label": "Invoice paid",
+ "detail": "Olivia Martin paid invoice #1048",
+ "time": "48 minutes ago",
+ },
+ {
+ "label": "Support ticket resolved",
+ "detail": "Ticket #284 was closed by Maya",
+ "time": "2 hours ago",
+ },
+ ],
+ )
+
+
@app.route("/htmx", methods=["GET", "POST"])
def htmx_demo():
"""Show an optional HTMX enhancement with a plain form fallback."""
diff --git a/demo/templates/admin.html b/demo/templates/admin.html
new file mode 100644
index 0000000..d718244
--- /dev/null
+++ b/demo/templates/admin.html
@@ -0,0 +1,77 @@
+{% extends "base.html" %}
+{% from "jinjalume/blocks/dashboard.html" import dashboard_shell, stat_card %}
+{% from "jinjalume/components/badge.html" import badge %}
+{% from "jinjalume/components/button.html" import button %}
+{% from "jinjalume/components/select.html" import select_field %}
+
+{% block title %}Admin dashboard · Jinjalume{% endblock %}
+{% block site_header %}{% endblock %}
+{% block main_class %}max-w-none p-0{% endblock %}
+
+{% block content %}
+ {% call dashboard_shell(
+ title="Overview",
+ description="Monitor your workspace performance and keep an eye on the latest customer activity.",
+ nav_items=nav_items,
+ active_item="Overview",
+ user_name="Alex Morgan",
+ user_email="alex@example.com"
+ ) %}
+ Your key numbers for this month.At a glance
The latest transactions across your workspace.
| Customer | Status | Amount | Date |
|---|---|---|---|
| {{ order.customer }}{{ order.email }} | +{{ badge(order.status, variant=status_variant) }} | +{{ order.amount }} | +{{ order.date }} | +
What is happening today.
{{ event.label }}
+{{ event.detail }}
+{{ event.time }}
+