bluefox-components 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. bluefox_components-0.2.0/.github/workflows/docs.yml +13 -0
  2. bluefox_components-0.2.0/.github/workflows/publish.yml +20 -0
  3. bluefox_components-0.2.0/.github/workflows/test.yml +11 -0
  4. bluefox_components-0.2.0/.gitignore +207 -0
  5. bluefox_components-0.2.0/Dockerfile.docs +15 -0
  6. bluefox_components-0.2.0/IMPLEMENTATION_PLAN.md +264 -0
  7. bluefox_components-0.2.0/Makefile +29 -0
  8. bluefox_components-0.2.0/PKG-INFO +14 -0
  9. bluefox_components-0.2.0/README.md +3 -0
  10. bluefox_components-0.2.0/bluefox_components/__init__.py +5 -0
  11. bluefox_components-0.2.0/bluefox_components/setup.py +34 -0
  12. bluefox_components-0.2.0/bluefox_components/static/bfx/bluefox.css +105 -0
  13. bluefox_components-0.2.0/bluefox_components/templates/bfx/base.html +50 -0
  14. bluefox_components-0.2.0/conftest.py +1 -0
  15. bluefox_components-0.2.0/docs/getting-started.md +71 -0
  16. bluefox_components-0.2.0/docs/guides/creating-macros.md +56 -0
  17. bluefox_components-0.2.0/docs/guides/theming.md +35 -0
  18. bluefox_components-0.2.0/docs/index.md +40 -0
  19. bluefox_components-0.2.0/docs/reference/base-layout.md +36 -0
  20. bluefox_components-0.2.0/docs/reference/css-theme.md +41 -0
  21. bluefox_components-0.2.0/docs/reference/setup.md +24 -0
  22. bluefox_components-0.2.0/docs/stylesheets/extra.css +26 -0
  23. bluefox_components-0.2.0/landing/index.html +557 -0
  24. bluefox_components-0.2.0/mkdocs.yml +58 -0
  25. bluefox_components-0.2.0/pyproject.toml +38 -0
  26. bluefox_components-0.2.0/tests/__init__.py +0 -0
  27. bluefox_components-0.2.0/tests/test_base_template.py +96 -0
  28. bluefox_components-0.2.0/tests/test_setup.py +55 -0
  29. bluefox_components-0.2.0/tests/test_static.py +27 -0
  30. bluefox_components-0.2.0/uv.lock +1259 -0
@@ -0,0 +1,13 @@
1
+ name: Docs
2
+ on:
3
+ push:
4
+ branches: [main]
5
+
6
+ jobs:
7
+ build:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - uses: astral-sh/setup-uv@v5
12
+ - run: uv sync --group docs
13
+ - run: uv run mkdocs build
@@ -0,0 +1,20 @@
1
+ name: Publish to PyPI
2
+ on:
3
+ push:
4
+ tags: ["v*"]
5
+
6
+ jobs:
7
+ publish:
8
+ runs-on: ubuntu-latest
9
+ environment: pypi
10
+ permissions:
11
+ contents: read
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: "3.12"
18
+ - uses: astral-sh/setup-uv@v5
19
+ - run: uv build
20
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,11 @@
1
+ name: Test
2
+ on: [push, pull_request]
3
+
4
+ jobs:
5
+ test:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v4
9
+ - uses: astral-sh/setup-uv@v5
10
+ - run: uv sync --group test
11
+ - run: uv run pytest tests/ -x --tb=short -q
@@ -0,0 +1,207 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # SageMath parsed files
135
+ *.sage.py
136
+
137
+ # Environments
138
+ .env
139
+ .envrc
140
+ .venv
141
+ env/
142
+ venv/
143
+ ENV/
144
+ env.bak/
145
+ venv.bak/
146
+
147
+ # Spyder project settings
148
+ .spyderproject
149
+ .spyproject
150
+
151
+ # Rope project settings
152
+ .ropeproject
153
+
154
+ # mkdocs documentation
155
+ /site
156
+
157
+ # mypy
158
+ .mypy_cache/
159
+ .dmypy.json
160
+ dmypy.json
161
+
162
+ # Pyre type checker
163
+ .pyre/
164
+
165
+ # pytype static type analyzer
166
+ .pytype/
167
+
168
+ # Cython debug symbols
169
+ cython_debug/
170
+
171
+ # PyCharm
172
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
175
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
+ #.idea/
177
+
178
+ # Abstra
179
+ # Abstra is an AI-powered process automation framework.
180
+ # Ignore directories containing user credentials, local state, and settings.
181
+ # Learn more at https://abstra.io/docs
182
+ .abstra/
183
+
184
+ # Visual Studio Code
185
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
+ # you could uncomment the following to ignore the entire vscode folder
189
+ # .vscode/
190
+
191
+ # Ruff stuff:
192
+ .ruff_cache/
193
+
194
+ # PyPI configuration file
195
+ .pypirc
196
+
197
+ # Cursor
198
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200
+ # refer to https://docs.cursor.com/context/ignore-files
201
+ .cursorignore
202
+ .cursorindexingignore
203
+
204
+ # Marimo
205
+ marimo/_static/
206
+ marimo/_lsp/
207
+ __marimo__/
@@ -0,0 +1,15 @@
1
+ FROM python:3.12-slim AS builder
2
+
3
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
4
+
5
+ WORKDIR /app
6
+ RUN uv pip install --system mkdocs-material mkdocs-minify-plugin
7
+
8
+ COPY mkdocs.yml ./
9
+ COPY docs/ docs/
10
+ RUN mkdocs build
11
+
12
+ FROM nginx:alpine
13
+ COPY landing/ /usr/share/nginx/html/
14
+ COPY --from=builder /app/site /usr/share/nginx/html/docs/
15
+ EXPOSE 80
@@ -0,0 +1,264 @@
1
+ # bluefox-components — Implementation Plan
2
+
3
+ > A lightweight design system for the Bluefox Stack. Jinja2 macros, Pico CSS theming, and a built-in component showcase.
4
+
5
+ ## What it is
6
+
7
+ A PyPI package (`uv add bluefox-components`) that provides:
8
+
9
+ 1. **Jinja2 macro templates** — parameterized, composable UI building blocks under the `bfx/` namespace
10
+ 2. **A CSS file** (`bluefox.css`) — Pico CSS variable overrides for the Bluefox default theme
11
+ 3. **A FastAPI router** — mountable at `/components` (auth-protected) for a storybook-like component showcase
12
+ 4. **A setup function** — registers the package's template directory with Jinja2 and mounts static files
13
+
14
+ ## Design decisions
15
+
16
+ - **Macros, not includes.** Every component is a Jinja2 macro with explicit parameters. Apps consume them via `{% from "bfx/button.html" import button %}`. This gives composability and parameterization without a build step.
17
+ - **Pure Pico CSS + variable overrides.** No Tailwind, no custom utility classes. `bluefox.css` only sets CSS custom properties (`--pico-primary-*`, `--pico-border-radius`, font stacks). Pico's semantic HTML approach means most components need zero custom CSS.
18
+ - **Production route.** The `/components` showcase is a real route, not dev-only. Protected by auth so only logged-in users (or admins) can access it. Useful as living documentation for the team building on the stack.
19
+ - **Auth pages live in bluefox-framework.** The `bluefox-auth` package provides the auth logic (JWT, users, permissions). The actual login/register/forgot-password *templates* live in the framework layer and import macros from `bluefox-components`. This keeps `bluefox-auth` focused on backend concerns.
20
+
21
+ ## Dependency chain
22
+
23
+ ```
24
+ bluefox-core ← bluefox-components ← bluefox-auth (logic only)
25
+ ← bluefox-framework (auth pages + app templates)
26
+ ← your app modules
27
+ ```
28
+
29
+ ## Package structure
30
+
31
+ ```
32
+ bluefox-components/
33
+ ├── src/bluefox_components/
34
+ │ ├── __init__.py # setup_components(app), component_router
35
+ │ ├── router.py # FastAPI router for /components showcase
36
+ │ ├── templates/
37
+ │ │ ├── bfx/ # namespace — apps import from "bfx/..."
38
+ │ │ │ ├── base.html # base layout (html, head, body, nav, footer)
39
+ │ │ │ ├── button.html # {% macro button(label, variant, ...) %}
40
+ │ │ │ ├── input.html # {% macro input(name, type, label, ...) %}
41
+ │ │ │ ├── select.html # {% macro select(name, options, ...) %}
42
+ │ │ │ ├── textarea.html # {% macro textarea(name, label, ...) %}
43
+ │ │ │ ├── card.html # {% macro card(title, footer) %}...caller...{% endmacro %}
44
+ │ │ │ ├── alert.html # {% macro alert(message, variant) %}
45
+ │ │ │ ├── modal.html # {% macro modal(id, title) %}
46
+ │ │ │ ├── nav.html # {% macro nav(brand, items, user) %}
47
+ │ │ │ ├── table.html # {% macro data_table(headers, rows) %}
48
+ │ │ │ ├── pagination.html # {% macro pagination(page, total_pages, url) %}
49
+ │ │ │ ├── form.html # {% macro form_group(label, error) %}, csrf helpers
50
+ │ │ │ └── loading.html # {% macro loading_spinner() %}, HTMX indicators
51
+ │ │ └── bfx_preview/ # templates for the showcase UI itself
52
+ │ │ ├── layout.html
53
+ │ │ └── showcase.html
54
+ │ └── static/
55
+ │ └── bfx/
56
+ │ └── bluefox.css # Pico CSS variable overrides only
57
+ ├── tests/
58
+ │ ├── conftest.py
59
+ │ └── test_components.py # template rendering tests
60
+ ├── pyproject.toml
61
+ └── README.md
62
+ ```
63
+
64
+ ## Integration
65
+
66
+ ### App factory
67
+
68
+ ```python
69
+ from bluefox_components import setup_components, component_router
70
+
71
+ app = create_bluefox_app(settings)
72
+ setup_components(app)
73
+
74
+ # Auth-protected showcase route
75
+ app.include_router(component_router, prefix="/components", tags=["components"])
76
+ ```
77
+
78
+ ### Template usage
79
+
80
+ ```jinja2
81
+ {% extends "bfx/base.html" %}
82
+ {% from "bfx/button.html" import button %}
83
+ {% from "bfx/input.html" import input %}
84
+ {% from "bfx/form.html" import form_group, form_group_end %}
85
+
86
+ {% block content %}
87
+ <article>
88
+ <h2>Login</h2>
89
+ <form method="post" action="/auth/login">
90
+ {% call form_group("Email", error=errors.get("email")) %}
91
+ {{ input(name="email", type="email", required=true) }}
92
+ {% endcall %}
93
+ {% call form_group("Password", error=errors.get("password")) %}
94
+ {{ input(name="password", type="password", required=true) }}
95
+ {% endcall %}
96
+ {{ button("Sign in") }}
97
+ </form>
98
+ </article>
99
+ {% endblock %}
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Phases
105
+
106
+ ### Phase 1 — Foundation
107
+
108
+ **Goal:** Installable package that registers templates and serves a base layout with the Bluefox theme.
109
+
110
+ | Step | Description |
111
+ |------|-------------|
112
+ | 1.1 | Project scaffold — `pyproject.toml` with `bluefox-core` dependency, src layout, uv workspace |
113
+ | 1.2 | `setup_components(app)` — adds the package's `templates/` directory to Jinja2's loader, mounts `/static/bfx/` for the CSS file. Single function call, follows the `BluefoxAuth(app)` pattern |
114
+ | 1.3 | `bfx/base.html` — base layout template. `<html>`, Pico CSS CDN link, `bluefox.css` link, `<nav>` block, `<main>` with `{% block content %}`, `<footer>`. Every Bluefox app extends this |
115
+ | 1.4 | `bluefox.css` — CSS variable overrides only. Bluefox color palette, border-radius, font choices. No component-specific CSS, no utility classes. Just the theme on top of Pico |
116
+ | 1.5 | Tests — template rendering tests using `bluefox-test`. Verify base layout renders, static files mount correctly |
117
+
118
+ **Done when:** `uv add bluefox-components`, call `setup_components(app)`, extend `bfx/base.html` from an app template, and see the Bluefox-themed page.
119
+
120
+ ### Phase 2 — Core macros
121
+
122
+ **Goal:** The macros needed to build auth pages and basic CRUD forms.
123
+
124
+ | Step | Description |
125
+ |------|-------------|
126
+ | 2.1 | **Form macros** — `input(name, type, label, placeholder, error, required, **attrs)`, `select(name, options, label, error, **attrs)`, `textarea(name, label, error, **attrs)`. The `**attrs` dict passes through as HTML attributes, enabling HTMX (`hx-post`, `hx-target`, etc.) without the macro needing to know about HTMX |
127
+ | 2.2 | **Form group** — `form_group(label, error)` as a `{% call %}` macro that wraps a label + input + error message. Keeps the label/error/input association consistent across all forms |
128
+ | 2.3 | **Button** — `button(label, variant="primary", type="submit", **attrs)`. Variants map to Pico's existing classes: `primary` (default), `secondary`, `outline`, `contrast`. Supports `role="button"` on `<a>` tags via an `href` parameter |
129
+ | 2.4 | **Alert** — `alert(message, variant="info", dismissible=false)`. For flash messages, form errors, success notifications. Variants: `info`, `success`, `warning`, `error` |
130
+ | 2.5 | **Card** — `{% call card(title="...", footer="...") %}content{% endcall %}`. Uses Pico's `<article>` with `<header>` and `<footer>` |
131
+ | 2.6 | **Nav** — `nav(brand, items, user)`. Renders the standard Bluefox nav with logo/brand, navigation links, and auth-aware user section (login/register or user dropdown) |
132
+ | 2.7 | Tests — each macro rendered with various parameter combinations, output validated for correct HTML structure and Pico classes |
133
+
134
+ **Done when:** Auth pages (login, register, forgot-password) can be built entirely from these macros with zero custom HTML for form elements.
135
+
136
+ ### Phase 3 — Component showcase
137
+
138
+ **Goal:** A production route at `/components` that displays every macro with live examples and copy-paste code.
139
+
140
+ | Step | Description |
141
+ |------|-------------|
142
+ | 3.1 | `component_router` — FastAPI `APIRouter` with auth dependency. Single page that renders the full showcase |
143
+ | 3.2 | Showcase layout — a clean page (extends `bfx/base.html`) with a sidebar listing all components and a main area showing each one |
144
+ | 3.3 | Component sections — for each macro, render it in multiple states: default, each variant, disabled, with error, with HTMX attributes. Group by category (forms, layout, feedback, navigation) |
145
+ | 3.4 | Source preview — next to each rendered component, show the Jinja2 macro call that produces it. Developers copy-paste from here. Rendered in a `<pre><code>` block |
146
+
147
+ **Done when:** Visit `/components` in any Bluefox app and see every available macro with examples and usage code.
148
+
149
+ ### Phase 4 — Extended components
150
+
151
+ **Goal:** Additional macros for building full application pages. Built as real pages need them (YAGNI).
152
+
153
+ | Step | Description |
154
+ |------|-------------|
155
+ | 4.1 | **Data table** — `data_table(headers, rows, empty_message)` with optional sort indicators, empty state |
156
+ | 4.2 | **Pagination** — `pagination(page, total_pages, base_url)` with HTMX-powered page navigation |
157
+ | 4.3 | **Modal** — `modal(id, title)` using the `<dialog>` element, toggleable via HTMX or minimal inline JS |
158
+ | 4.4 | **Loading indicators** — `loading_spinner()` and `loading_skeleton(lines)`, compatible with `hx-indicator` |
159
+ | 4.5 | **Empty state** — `empty_state(message, action_label, action_url)` for when lists have no items |
160
+ | 4.6 | **Breadcrumbs** — `breadcrumbs(items)` for page hierarchy navigation |
161
+ | 4.7 | **Badge** — `badge(label, variant)` for status indicators, counts, tags |
162
+ | 4.8 | Update showcase with all new components |
163
+
164
+ **Done when:** A full CRUD module (list → detail → create → edit → delete) can be built entirely from macros.
165
+
166
+ ### Phase 5 — Theme variants (future)
167
+
168
+ **Goal:** Alternate visual identities for different app personalities, switchable via a single CSS variable or attribute.
169
+
170
+ | Step | Description |
171
+ |------|-------------|
172
+ | 5.1 | Named themes — alternate sets of CSS variables for different feels: `financial` (conservative, serif), `playful` (rounded, colorful), `minimal` (monochrome, tight spacing). Applied via `data-bfx-theme="financial"` on `<html>` |
173
+ | 5.2 | Theme file structure — each theme is a separate CSS file (`bluefox-financial.css`, etc.) that overrides the same Pico variables as `bluefox.css` |
174
+ | 5.3 | Theme toggle in showcase — extend the `/components` page with a theme switcher to preview all components under each theme |
175
+ | 5.4 | Custom theme guide — documentation on how to create your own theme file by overriding the CSS variables |
176
+
177
+ **Done when:** Changing one line (the theme attribute or CSS import) gives an app a completely different visual identity while keeping all macros unchanged.
178
+
179
+ ---
180
+
181
+ ## Macro API conventions
182
+
183
+ All macros follow these conventions:
184
+
185
+ 1. **Explicit parameters for common use cases.** `button(label, variant="primary", type="submit")` — the 90% case is covered by named parameters.
186
+ 2. **`**attrs` dict for escape hatches.** Every macro accepts an `attrs` parameter (a dict) that gets rendered as HTML attributes. This is how HTMX, Alpine.js, `data-*`, and `aria-*` attributes pass through without the macro needing to enumerate them.
187
+ 3. **Pico-native HTML.** Macros emit semantic HTML that Pico already styles. A `card` is an `<article>`, a `form_group` is a `<label>` wrapping an input, a `nav` is a `<nav>` with `<ul>`. Minimal custom classes.
188
+ 4. **`{% call %}` for content slots.** Components that wrap arbitrary content (card, modal, form_group) use Jinja2's caller convention: `{% call card(title="...") %}your content{% endcall %}`.
189
+ 5. **No JavaScript by default.** Macros produce static HTML. HTMX attributes are passed via `attrs`. The only exception is `modal` which may include a minimal inline script for `<dialog>` show/close.
190
+
191
+ ### Example macro implementation
192
+
193
+ ```jinja2
194
+ {# bfx/button.html #}
195
+
196
+ {% macro button(label, variant="primary", type="submit", href=none, disabled=false, attrs={}) %}
197
+ {% if href %}
198
+ <a href="{{ href }}" role="button"
199
+ {% if variant != "primary" %}class="{{ variant }}"{% endif %}
200
+ {% if disabled %}aria-disabled="true"{% endif %}
201
+ {% for key, val in attrs.items() %}{{ key }}="{{ val }}"{% endfor %}
202
+ >{{ label }}</a>
203
+ {% else %}
204
+ <button type="{{ type }}"
205
+ {% if variant != "primary" %}class="{{ variant }}"{% endif %}
206
+ {% if disabled %}disabled{% endif %}
207
+ {% for key, val in attrs.items() %}{{ key }}="{{ val }}"{% endfor %}
208
+ >{{ label }}</button>
209
+ {% endif %}
210
+ {% endmacro %}
211
+ ```
212
+
213
+ Usage:
214
+
215
+ ```jinja2
216
+ {% from "bfx/button.html" import button %}
217
+
218
+ {{ button("Save") }}
219
+ {{ button("Cancel", variant="secondary", href="/dashboard") }}
220
+ {{ button("Delete", variant="outline", attrs={"hx-delete": "/items/1", "hx-confirm": "Are you sure?"}) }}
221
+ {{ button("Processing...", disabled=true) }}
222
+ ```
223
+
224
+ ---
225
+
226
+ ## CSS variable strategy
227
+
228
+ `bluefox.css` overrides Pico's CSS custom properties. No custom classes, no `!important`, no component-scoped styles.
229
+
230
+ ```css
231
+ /* bluefox.css — theme layer on top of Pico CSS */
232
+
233
+ :root {
234
+ /* Brand colors */
235
+ --pico-primary: #3b82f6;
236
+ --pico-primary-hover: #2563eb;
237
+ --pico-primary-focus: rgba(59, 130, 246, 0.25);
238
+ --pico-primary-inverse: #fff;
239
+
240
+ /* Typography */
241
+ --pico-font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Helvetica Neue", sans-serif;
242
+ --pico-font-size: 100%;
243
+ --pico-line-height: 1.5;
244
+
245
+ /* Border radius */
246
+ --pico-border-radius: 0.375rem;
247
+
248
+ /* Spacing adjustments (if any) */
249
+ }
250
+
251
+ /* Dark mode overrides (Pico supports data-theme="dark") */
252
+ [data-theme="dark"] {
253
+ --pico-primary: #60a5fa;
254
+ --pico-primary-hover: #93bbfd;
255
+ }
256
+ ```
257
+
258
+ The exact values will be derived from the current bluefox-stack website's color palette to ensure visual consistency across all Bluefox properties.
259
+
260
+ ---
261
+
262
+ ## Build order recommendation
263
+
264
+ **Build Phases 1 → 2 first.** That unblocks auth page templates in `bluefox-framework`. Phase 3 (showcase) comes immediately after as a quality-of-life tool. Phase 4 components get added as real app pages demand them. Phase 5 is explicitly "later — only if we want it."
@@ -0,0 +1,29 @@
1
+ .PHONY: dev test test-fast docs docs-build landing clean
2
+
3
+ # Serve landing page on :8090 and docs on :8000
4
+ dev:
5
+ python -m http.server 8090 --directory landing & uv run mkdocs serve
6
+
7
+ # Run all tests
8
+ test:
9
+ uv run pytest tests/ -v --tb=short
10
+
11
+ # Run tests quickly (stop on first failure)
12
+ test-fast:
13
+ uv run pytest tests/ -x --tb=short -q
14
+
15
+ # Serve docs locally (MkDocs)
16
+ docs:
17
+ uv run mkdocs serve
18
+
19
+ # Build docs to site/
20
+ docs-build:
21
+ uv run mkdocs build
22
+
23
+ # Serve landing page locally
24
+ landing:
25
+ python -m http.server 8090 --directory landing
26
+
27
+ # Remove build artifacts
28
+ clean:
29
+ rm -rf site/ dist/ *.egg-info .pytest_cache
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: bluefox-components
3
+ Version: 0.2.0
4
+ Summary: Jinja2 macro components and Pico CSS theming for the Bluefox Stack.
5
+ Project-URL: Homepage, https://bluefox-components.bluefox.software
6
+ Project-URL: Repository, https://github.com/blue-fox-software/bluefox-components
7
+ License-Expression: MIT
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: bluefox-core>=0.4.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # bluefox-components
13
+
14
+ Jinja2 macro components and Pico CSS theming for the Bluefox Stack.
@@ -0,0 +1,3 @@
1
+ # bluefox-components
2
+
3
+ Jinja2 macro components and Pico CSS theming for the Bluefox Stack.
@@ -0,0 +1,5 @@
1
+ from bluefox_components.setup import setup_components
2
+
3
+ __all__ = [
4
+ "setup_components",
5
+ ]
@@ -0,0 +1,34 @@
1
+ """Register bluefox-components templates and static files with a FastAPI app."""
2
+
3
+ from pathlib import Path
4
+
5
+ from fastapi import FastAPI
6
+ from fastapi.staticfiles import StaticFiles
7
+
8
+ from bluefox_core.templates import add_template_directory
9
+
10
+
11
+ _PKG_DIR = Path(__file__).parent
12
+
13
+
14
+ def setup_components(app: FastAPI) -> None:
15
+ """Register the component templates and mount static assets.
16
+
17
+ Call this once during app startup::
18
+
19
+ from bluefox_components import setup_components
20
+ setup_components(app)
21
+ """
22
+ # Register bfx/ templates so apps can {% extends "bfx/base.html" %}
23
+ add_template_directory(app, _PKG_DIR / "templates")
24
+
25
+ # Mount the CSS theme file at /static/bfx/ (guard against duplicate mounts)
26
+ already_mounted = any(
27
+ getattr(r, "path", None) == "/static/bfx" for r in app.routes
28
+ )
29
+ if not already_mounted:
30
+ app.mount(
31
+ "/static/bfx",
32
+ StaticFiles(directory=str(_PKG_DIR / "static" / "bfx")),
33
+ name="bfx-static",
34
+ )