diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7948812..4e2c283 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,8 +50,20 @@ concurrency: jobs: backend: - name: Backend (pytest) + name: Backend (pytest py${{ matrix.python }} / Django ${{ matrix.django }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + # Django 4.2 LTS is supported through April 2026 (#622). Add it + # alongside 5.x and 6.0 so the package keeps working for the + # majority of installed Django by deployment count. + python: ["3.12"] + django: ["4.2", "5.2"] + # 4.2 added Python 3.13 support in 4.2.16 but pip may resolve to + # an earlier 4.2.x; skip 3.13 + 4.2 conservatively. (We only + # exercise py3.12 here today; the matrix is set up for an easy + # future expansion.) steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -59,7 +71,7 @@ jobs: - name: Set up Python uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: - python-version: "3.12" + python-version: ${{ matrix.python }} - name: Install Poetry # Poetry is pinned to the version that generated poetry.lock (see @@ -72,6 +84,9 @@ jobs: - name: Install dependencies (locked) run: poetry install --no-interaction + - name: Pin Django to the matrix version + run: poetry run pip install "django~=${{ matrix.django }}.0" + # pytest with coverage (per pyproject `addopts`), including # tests/test_security.py. `filterwarnings = ["error"]` means a new # warning fails the run. diff --git a/README.md b/README.md index 9b42571..a9dcceb 100644 --- a/README.md +++ b/README.md @@ -155,9 +155,12 @@ DJANGO_ADMIN_REACT = { "BRAND_LOGO_URL": None, # str | None — favicon + sidebar logo; # falls back to AdminSite.site_logo. Absolute # URL or a path under your STATIC_URL. - "PRIMARY_COLOR": "#2563eb", # accent for primary buttons, links, and - # active states. Hex only (validated); - # injected as the --dar-primary CSS var, so + "PRIMARY_COLOR": None, # accent for primary buttons, links, and + # active states (#437 / #631). Hex only + # (validated). None → reads + # `site_primary_color` off your AdminSite; + # fallback default is "#2563eb". Injected + # as the --dar-primary CSS var, so # rebranding needs no React rebuild. # Auth + API mount @@ -212,10 +215,39 @@ Both values are written into the SPA index template as standard reads them at boot, so the first paint already carries the consumer's brand. No flash of the package's defaults. +#### Accent colour (`PRIMARY_COLOR` + `AdminSite.site_primary_color`) + +`PRIMARY_COLOR` defaults to `None` so a custom `AdminSite` subclass can +own the brand colour the same way it owns `site_header` / `site_logo` +(#631). Resolution order — explicit setting wins, AdminSite is the +structural default, built-in fallback last: + +1. `DJANGO_ADMIN_REACT["PRIMARY_COLOR"]` — explicit per-deployment override. +2. `.site_primary_color` — convention attribute on your + custom `AdminSite` subclass (Django has no such attribute by default; + add it as a constant alongside `site_header` / `site_logo`). +3. `"#2563eb"` — the package's last-resort fallback. + +Every layer runs through a strict hex-colour regex (`#rgb` / `#rgba` / +`#rrggbb` / `#rrggbbaa`) before being injected into the SPA's `