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.
- bluefox_components-0.2.0/.github/workflows/docs.yml +13 -0
- bluefox_components-0.2.0/.github/workflows/publish.yml +20 -0
- bluefox_components-0.2.0/.github/workflows/test.yml +11 -0
- bluefox_components-0.2.0/.gitignore +207 -0
- bluefox_components-0.2.0/Dockerfile.docs +15 -0
- bluefox_components-0.2.0/IMPLEMENTATION_PLAN.md +264 -0
- bluefox_components-0.2.0/Makefile +29 -0
- bluefox_components-0.2.0/PKG-INFO +14 -0
- bluefox_components-0.2.0/README.md +3 -0
- bluefox_components-0.2.0/bluefox_components/__init__.py +5 -0
- bluefox_components-0.2.0/bluefox_components/setup.py +34 -0
- bluefox_components-0.2.0/bluefox_components/static/bfx/bluefox.css +105 -0
- bluefox_components-0.2.0/bluefox_components/templates/bfx/base.html +50 -0
- bluefox_components-0.2.0/conftest.py +1 -0
- bluefox_components-0.2.0/docs/getting-started.md +71 -0
- bluefox_components-0.2.0/docs/guides/creating-macros.md +56 -0
- bluefox_components-0.2.0/docs/guides/theming.md +35 -0
- bluefox_components-0.2.0/docs/index.md +40 -0
- bluefox_components-0.2.0/docs/reference/base-layout.md +36 -0
- bluefox_components-0.2.0/docs/reference/css-theme.md +41 -0
- bluefox_components-0.2.0/docs/reference/setup.md +24 -0
- bluefox_components-0.2.0/docs/stylesheets/extra.css +26 -0
- bluefox_components-0.2.0/landing/index.html +557 -0
- bluefox_components-0.2.0/mkdocs.yml +58 -0
- bluefox_components-0.2.0/pyproject.toml +38 -0
- bluefox_components-0.2.0/tests/__init__.py +0 -0
- bluefox_components-0.2.0/tests/test_base_template.py +96 -0
- bluefox_components-0.2.0/tests/test_setup.py +55 -0
- bluefox_components-0.2.0/tests/test_static.py +27 -0
- bluefox_components-0.2.0/uv.lock +1259 -0
|
@@ -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,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,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
|
+
)
|