bluefox-components
Component Showcase

All the building blocks,
in one place

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

Typography

Rendered via Pico CSS semantic elements — no macros needed
Headings

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
html

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
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

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

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

app = create_bluefox_app(settings)
setup_components(app)
html
your code here

Button

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

Input

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

Select

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

Textarea

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

Form Group

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

Card

{% from "bfx/card.html" import card %}
Basic card

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

jinja2
{% call card() %}
  

A simple card with just content.

{% endcall %}
With title and footer
User Profile

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

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

Card body content goes here.

{% endcall %}

Alert

{% from "bfx/alert.html" import alert %}
Variants
This is an informational message.
Operation completed successfully!
jinja2
{{ 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") }}
Dismissible
You can close this alert. ×
jinja2
{{ alert("You can close this alert.", variant="info", dismissible=true) }}

Data Table

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

Pagination

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

Empty State

{% from "bfx/empty_state.html" import empty_state %}
Default

No items found.

jinja2
{{ empty_state() }}
With action
jinja2
{{ empty_state("No projects yet.",
             action_label="Create your first project",
             action_url="/projects/new") }}

Badge

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

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

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

Loading

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

{{ loading_skeleton(lines=3) }}
Theme