Parameterized component templates, Pico CSS theming, and a built-in showcase. Import a macro, pass your data, get consistent UI across every Bluefox app.
Install the package
$ uv add bluefox-components
Register templates and static assets
from bluefox_core import BluefoxSettings, create_bluefox_app from bluefox_components import setup_components settings = BluefoxSettings() app = create_bluefox_app(settings) setup_components(app)
Use macros in your templates
{% extends "bfx/base.html" %} {% from "bfx/button.html" import button %} {% from "bfx/input.html" import input %} {% block content %} <form method="post"> {{ input(name="email", type="email", label="Email") }} {{ input(name="password", type="password", label="Password") }} {{ button("Sign in") }} </form> {% endblock %}
Buttons, inputs, selects, cards, alerts, modals, tables, and more. Each macro has explicit parameters for the common case and an attrs dict for escape hatches.
One CSS file overrides Pico's custom properties with the Bluefox palette. DM Sans typography, warm backgrounds, consistent borders. Light and dark modes included.
Every macro accepts arbitrary HTML attributes via the attrs parameter. Pass hx-post, hx-target, or any attribute without the macro knowing about HTMX.
Extend bfx/base.html for a complete page with nav, main content area, and footer. Override blocks to customize any section.
A production route at /components displays every macro with live examples and copy-paste code. Living documentation for your team.
Pure Jinja2 templates and CSS custom properties. No Tailwind, no bundler, no compilation. Install the package, call one function, start using macros.
Macros emit semantic HTML that Pico already styles. A card is an <article>, a form group is a <label> wrapping an input. Minimal custom classes.
{% from "bfx/button.html" import button %} {% from "bfx/card.html" import card %} {% from "bfx/alert.html" import alert %} {# Simple button #} {{ button("Save") }} {# Button with HTMX #} {{ button("Delete", variant="outline", attrs={"hx-delete": "/items/1"}) }} {# Card with content slot #} {% call card(title="Users") %} <p>42 active users</p> {% endcall %} {# Flash message #} {{ alert("Changes saved.", variant="success") }}