{% extends "bfx_preview/layout.html" %} {% from "bfx/button.html" import button %} {% from "bfx/input.html" import input %} {% from "bfx/select.html" import select %} {% from "bfx/textarea.html" import textarea %} {% from "bfx/form.html" import form_group, csrf_input %} {% from "bfx/alert.html" import alert %} {% from "bfx/card.html" import card %} {% from "bfx/nav.html" import nav %} {% from "bfx/table.html" import data_table %} {% from "bfx/pagination.html" import pagination %} {% from "bfx/modal.html" import modal, modal_trigger %} {% from "bfx/loading.html" import loading_spinner, loading_skeleton %} {% from "bfx/empty_state.html" import empty_state %} {% from "bfx/breadcrumbs.html" import breadcrumbs %} {% from "bfx/badge.html" import badge %} {% block content %} {# ── Hero ──────────────────────────────────────── #}
Component Showcase

All the building blocks,
in one place

Every bfx/ macro rendered live with copy-paste Jinja2 code. Import, pass your data, ship.

{# ── Table of contents ────────────────────────── #}
Components
Foundation
Forms
Layout
Data
Feedback
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# TYPOGRAPHY #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Typography

Rendered via Pico CSS semantic elements — no macros needed
Headings

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
html
{% raw %}

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
{% endraw %}
Body text

This is a paragraph of body text. The default Bluefox theme uses DM Sans for body copy and JetBrains Mono for code. Each theme variant overrides the font stack — try switching themes with the picker in the bottom right.

Text can include bold, italic, links, highlighted, small text, and inline code.

html
{% raw %}

Body text with bold, italic, links, highlighted, small, and inline code.

{% endraw %}
Blockquote
"Design is not just what it looks like and feels like. Design is how it works."
Steve Jobs
html
{% raw %}
"Design is not just what it looks like..."
Steve Jobs
{% endraw %}
Lists
  • Unordered item one
  • Unordered item two
  • Unordered item three
  1. Ordered item one
  2. Ordered item two
  3. Ordered item three
html
{% raw %}
  • Unordered item
  1. Ordered item
{% endraw %}
Code block
from bluefox_components import setup_components

app = create_bluefox_app(settings)
setup_components(app)
html
{% raw %}
your code here
{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# BUTTON #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Button

{% raw %}{% from "bfx/button.html" import button %}{% endraw %}
Variants
{{ button("Primary") }} {{ button("Secondary", variant="secondary") }} {{ button("Outline", variant="outline") }} {{ button("Contrast", variant="contrast") }}
jinja2
{% raw %}{{ button("Primary") }}
{{ button("Secondary", variant="secondary") }}
{{ button("Outline", variant="outline") }}
{{ button("Contrast", variant="contrast") }}{% endraw %}
Link as button
{{ button("Go to dashboard", href="#", variant="secondary") }}
jinja2
{% raw %}{{ button("Go to dashboard", href="/dashboard", variant="secondary") }}{% endraw %}
Disabled
{{ button("Processing...", disabled=true) }} {{ button("Disabled link", href="#", disabled=true) }}
jinja2
{% raw %}{{ button("Processing...", disabled=true) }}
{{ button("Disabled link", href="#", disabled=true) }}{% endraw %}
With HTMX attributes
jinja2
{% raw %}{{ button("Delete", variant="outline",
         attrs={"hx-delete": "/items/1", "hx-confirm": "Are you sure?"}) }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# INPUT #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Input

{% raw %}{% from "bfx/input.html" import input %}{% endraw %}
Default
{{ input("demo_email", type="email", label="Email address", placeholder="you@example.com") }}
jinja2
{% raw %}{{ input("email", type="email", label="Email address", placeholder="you@example.com") }}{% endraw %}
With error
{{ input("demo_email_err", type="email", label="Email", value="not-an-email", error="Please enter a valid email address") }}
jinja2
{% raw %}{{ input("email", type="email", label="Email", value="not-an-email",
         error="Please enter a valid email address") }}{% endraw %}
Password
{{ input("demo_pw", type="password", label="Password", placeholder="Enter password", required=true) }}
jinja2
{% raw %}{{ input("password", type="password", label="Password", required=true) }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# SELECT #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Select

{% raw %}{% from "bfx/select.html" import select %}{% endraw %}
Simple options
{{ select("demo_color", ["Red", "Green", "Blue"], label="Favorite color") }}
jinja2
{% raw %}{{ select("color", ["Red", "Green", "Blue"], label="Favorite color") }}{% endraw %}
Dict options with selected
{{ select("demo_role", [{"value": "admin", "label": "Administrator"}, {"value": "editor", "label": "Editor"}, {"value": "viewer", "label": "Viewer"}], label="Role", selected="editor") }}
jinja2
{% raw %}{{ select("role", [
    {"value": "admin", "label": "Administrator"},
    {"value": "editor", "label": "Editor"},
    {"value": "viewer", "label": "Viewer"}
  ], label="Role", selected="editor") }}{% endraw %}
With error
{{ select("demo_plan", ["Free", "Pro"], label="Plan", error="Selection required") }}
jinja2
{% raw %}{{ select("plan", ["Free", "Pro"], label="Plan", error="Selection required") }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# TEXTAREA #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Textarea

{% raw %}{% from "bfx/textarea.html" import textarea %}{% endraw %}
Default
{{ textarea("demo_bio", label="Biography", placeholder="Tell us about yourself...") }}
jinja2
{% raw %}{{ textarea("bio", label="Biography", placeholder="Tell us about yourself...") }}{% endraw %}
With error
{{ textarea("demo_bio_err", label="Biography", error="Must be at least 20 characters", rows=3) }}
jinja2
{% raw %}{{ textarea("bio", label="Biography", error="Must be at least 20 characters", rows=3) }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# FORM GROUP #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Form Group

{% raw %}{% from "bfx/form.html" import form_group, csrf_input %}{% endraw %}
Wrapping an input
{% call form_group("Email address", for_id="demo_fg_email") %} {% endcall %}
jinja2
{% raw %}{% call form_group("Email address", for_id="email") %}
  {{ input("email", type="email", placeholder="you@example.com") }}
{% endcall %}{% endraw %}
With error
{% call form_group("Password", error="Password is required", for_id="demo_fg_pw") %} {% endcall %}
jinja2
{% raw %}{% call form_group("Password", error="Password is required") %}
  {{ input("password", type="password") }}
{% endcall %}{% endraw %}
CSRF token helper
jinja2
{% raw %}{{ csrf_input(csrf_token) }}
{# Renders:  #}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# CARD #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Card

{% raw %}{% from "bfx/card.html" import card %}{% endraw %}
Basic card
{% call card() %}

A simple card with just content. Uses Pico's <article> element.

{% endcall %}
jinja2
{% raw %}{% call card() %}
  

A simple card with just content.

{% endcall %}{% endraw %}
With title and footer
{% call card(title="User Profile", footer="Last updated: today") %}

Card body content goes here. The title renders in a <header> and footer in a <footer>.

{% endcall %}
jinja2
{% raw %}{% call card(title="User Profile", footer="Last updated: today") %}
  

Card body content goes here.

{% endcall %}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# NAV #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# ALERT #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Alert

{% raw %}{% from "bfx/alert.html" import alert %}{% endraw %}
Variants
{{ alert("This is an informational message.", variant="info") }} {{ alert("Operation completed successfully!", variant="success") }} {{ alert("Please check your input.", variant="warning") }} {{ alert("Something went wrong.", variant="error") }}
jinja2
{% raw %}{{ alert("This is an informational message.", variant="info") }}
{{ alert("Operation completed successfully!", variant="success") }}
{{ alert("Please check your input.", variant="warning") }}
{{ alert("Something went wrong.", variant="error") }}{% endraw %}
Dismissible
{{ alert("You can close this alert.", variant="info", dismissible=true) }}
jinja2
{% raw %}{{ alert("You can close this alert.", variant="info", dismissible=true) }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# DATA TABLE #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Data Table

{% raw %}{% from "bfx/table.html" import data_table %}{% endraw %}
With data
{{ data_table( ["Name", "Email", "Role"], [ ["Alice Johnson", "alice@example.com", "Admin"], ["Bob Smith", "bob@example.com", "Editor"], ["Carol White", "carol@example.com", "Viewer"] ] ) }}
jinja2
{% raw %}{{ data_table(
    ["Name", "Email", "Role"],
    [
      ["Alice Johnson", "alice@example.com", "Admin"],
      ["Bob Smith", "bob@example.com", "Editor"],
      ["Carol White", "carol@example.com", "Viewer"]
    ]
) }}{% endraw %}
Empty state
{{ data_table(["Name", "Email"], [], empty_message="No users found.") }}
jinja2
{% raw %}{{ data_table(["Name", "Email"], [], empty_message="No users found.") }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# PAGINATION #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Pagination

{% raw %}{% from "bfx/pagination.html" import pagination %}{% endraw %}
Middle page
{{ pagination(page=3, total_pages=7) }}
jinja2
{% raw %}{{ pagination(page=3, total_pages=7) }}{% endraw %}
First page
{{ pagination(page=1, total_pages=5) }}
jinja2
{% raw %}{{ pagination(page=1, total_pages=5) }}{% endraw %}
Custom base URL
jinja2
{% raw %}{{ pagination(page=2, total_pages=10, base_url="/users?page=") }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# EMPTY STATE #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Empty State

{% raw %}{% from "bfx/empty_state.html" import empty_state %}{% endraw %}
Default
{{ empty_state() }}
jinja2
{% raw %}{{ empty_state() }}{% endraw %}
With action
{{ empty_state("No projects yet.", action_label="Create your first project", action_url="#") }}
jinja2
{% raw %}{{ empty_state("No projects yet.",
             action_label="Create your first project",
             action_url="/projects/new") }}{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# BREADCRUMBS #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# BADGE #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Badge

{% raw %}{% from "bfx/badge.html" import badge %}{% endraw %}
Variants
{{ badge("Primary") }} {{ badge("Secondary", variant="secondary") }} {{ badge("Success", variant="success") }} {{ badge("Warning", variant="warning") }} {{ badge("Error", variant="error") }} {{ badge("Info", variant="info") }}
jinja2
{% raw %}{{ badge("Primary") }}
{{ badge("Secondary", variant="secondary") }}
{{ badge("Success", variant="success") }}
{{ badge("Warning", variant="warning") }}
{{ badge("Error", variant="error") }}
{{ badge("Info", variant="info") }}{% endraw %}
In context
jinja2
{% raw %}

Notifications {{ badge("3", variant="error") }}

Status: {{ badge("Active", variant="success") }}

{% endraw %}
{# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# MODAL #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #} {# LOADING #} {# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ #}

Loading

{% raw %}{% from "bfx/loading.html" import loading_spinner, loading_skeleton %}{% endraw %}
Spinner
{{ loading_spinner() }} {{ loading_spinner(size="2.5rem") }}
jinja2
{% raw %}{{ loading_spinner() }}
{{ loading_spinner(size="2.5rem") }}{% endraw %}
Skeleton
{{ loading_skeleton(lines=4) }}
jinja2
{% raw %}{{ loading_skeleton(lines=4) }}{% endraw %}
With HTMX indicator
jinja2
{% raw %}
{{ loading_skeleton(lines=3) }}
{% endraw %}
{% endblock %}