Queens email branding: pink palette for girls program emails #413

Closed
opened 2026-04-09 13:25:19 +00:00 by forgejo_admin · 2 comments

Type

Feature

Lineage

Discovered during first-payment email validation (2026-04-09). Related to forgejo_admin/basketball-api#369.

Repo

forgejo_admin/basketball-api

User Story

As a Queens (girls program) parent
I want emails branded with the Queens pink palette
So that the communication matches the Queens branding I see on the website and contract page

Context

Queens pink branding already exists across the platform:

  • westside-contracts src/app.css:27-31: .queens-active swaps --color-red to #e91e8c, --color-red-hover to #f23d9e
  • westside-contracts +page.svelte:140-146: applies queens-active when isGirls is true
  • westside-app src/lib/program.svelte.js: kings/queens toggle used across 28 files

basketball-api has Kings colors in brand.py but no Queens variant. Email system hardcodes _BRAND_RED = "#d42026" in email.py:192 and _brand_wrapper() at email.py:200 uses it for header borders, headings, CTAs, and links. No division context flows through the email pipeline.

Colors are already decided#e91e8c (pink) and #f23d9e (pink hover) from the existing CSS. No design approval needed.

File Targets

Files to modify:

  • src/basketball_api/brand.py — add COLOR_QUEENS_PINK = "#e91e8c" and COLOR_QUEENS_PINK_HOVER = "#f23d9e"
  • src/basketball_api/services/email.py:192 — add _BRAND_QUEENS_PINK = "#e91e8c" constant next to _BRAND_RED
  • src/basketball_api/services/email.py:200 — modify _brand_wrapper(tenant_name, body_html) to accept accent_color: str = _BRAND_RED param. Replace all _BRAND_RED references inside the wrapper function body (header border, etc.) with the accent_color parameter
  • src/basketball_api/services/email.py — in send_first_payment_email() (~line 951), check player.division == Division.girls. If true, pass accent_color=_BRAND_QUEENS_PINK to _brand_wrapper(). Also use Queens pink in any _BRAND_RED references inside the body_html construction (h3, cta button) for Queens players.
  • src/basketball_api/models.py — already has Division.girls at line 54, no change needed

Files NOT to touch:

  • Other email functions — they keep using default _BRAND_RED until explicitly opted in
  • westside-contracts, westside-app — Queens pink already works there

Acceptance Criteria

  • brand.py has COLOR_QUEENS_PINK = "#e91e8c" and COLOR_QUEENS_PINK_HOVER = "#f23d9e"
  • email.py has _BRAND_QUEENS_PINK = "#e91e8c" constant
  • _brand_wrapper() accepts optional accent_color param, defaults to _BRAND_RED (backward compatible)
  • Header border inside _brand_wrapper() uses accent_color instead of hardcoded _BRAND_RED
  • send_first_payment_email() passes Queens pink when player.division == Division.girls
  • Kings players (boys division or null) still get red/black (default behavior)
  • All 7 other _brand_wrapper() callers unchanged — still use default

Test Expectations

  • Unit test: _brand_wrapper() with no accent_color produces HTML containing #d42026
  • Unit test: _brand_wrapper() with accent_color="#e91e8c" produces HTML containing #e91e8c
  • Unit test: send_first_payment_email() for a Division.girls player produces HTML containing #e91e8c
  • Unit test: send_first_payment_email() for a Division.boys player produces HTML containing #d42026
  • Run: pytest tests/test_first_payment_email.py -v

Constraints

  • Colors must match westside-contracts CSS exactly: #e91e8c (pink), #f23d9e (hover)
  • accent_color param must be backward-compatible — existing callers of _brand_wrapper() don't change
  • Division check uses player.division == Division.girls (same pattern as contract page's isGirls)
  • If player.division is None, default to Kings red (treat as non-girls)

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • westside-basketball — project
  • Queens CSS: westside-contracts/src/app.css:26-31
  • Program toggle: westside-app/src/lib/program.svelte.js
### Type Feature ### Lineage Discovered during first-payment email validation (2026-04-09). Related to `forgejo_admin/basketball-api#369`. ### Repo `forgejo_admin/basketball-api` ### User Story As a Queens (girls program) parent I want emails branded with the Queens pink palette So that the communication matches the Queens branding I see on the website and contract page ### Context Queens pink branding already exists across the platform: - **westside-contracts** `src/app.css:27-31`: `.queens-active` swaps `--color-red` to `#e91e8c`, `--color-red-hover` to `#f23d9e` - **westside-contracts** `+page.svelte:140-146`: applies `queens-active` when `isGirls` is true - **westside-app** `src/lib/program.svelte.js`: kings/queens toggle used across 28 files basketball-api has Kings colors in `brand.py` but no Queens variant. Email system hardcodes `_BRAND_RED = "#d42026"` in `email.py:192` and `_brand_wrapper()` at `email.py:200` uses it for header borders, headings, CTAs, and links. No division context flows through the email pipeline. **Colors are already decided** — `#e91e8c` (pink) and `#f23d9e` (pink hover) from the existing CSS. No design approval needed. ### File Targets Files to modify: - `src/basketball_api/brand.py` — add `COLOR_QUEENS_PINK = "#e91e8c"` and `COLOR_QUEENS_PINK_HOVER = "#f23d9e"` - `src/basketball_api/services/email.py:192` — add `_BRAND_QUEENS_PINK = "#e91e8c"` constant next to `_BRAND_RED` - `src/basketball_api/services/email.py:200` — modify `_brand_wrapper(tenant_name, body_html)` to accept `accent_color: str = _BRAND_RED` param. Replace all `_BRAND_RED` references inside the wrapper function body (header border, etc.) with the `accent_color` parameter - `src/basketball_api/services/email.py` — in `send_first_payment_email()` (~line 951), check `player.division == Division.girls`. If true, pass `accent_color=_BRAND_QUEENS_PINK` to `_brand_wrapper()`. Also use Queens pink in any `_BRAND_RED` references inside the body_html construction (h3, cta button) for Queens players. - `src/basketball_api/models.py` — already has `Division.girls` at line 54, no change needed Files NOT to touch: - Other email functions — they keep using default `_BRAND_RED` until explicitly opted in - westside-contracts, westside-app — Queens pink already works there ### Acceptance Criteria - [ ] `brand.py` has `COLOR_QUEENS_PINK = "#e91e8c"` and `COLOR_QUEENS_PINK_HOVER = "#f23d9e"` - [ ] `email.py` has `_BRAND_QUEENS_PINK = "#e91e8c"` constant - [ ] `_brand_wrapper()` accepts optional `accent_color` param, defaults to `_BRAND_RED` (backward compatible) - [ ] Header border inside `_brand_wrapper()` uses `accent_color` instead of hardcoded `_BRAND_RED` - [ ] `send_first_payment_email()` passes Queens pink when `player.division == Division.girls` - [ ] Kings players (boys division or null) still get red/black (default behavior) - [ ] All 7 other `_brand_wrapper()` callers unchanged — still use default ### Test Expectations - [ ] Unit test: `_brand_wrapper()` with no accent_color produces HTML containing `#d42026` - [ ] Unit test: `_brand_wrapper()` with `accent_color="#e91e8c"` produces HTML containing `#e91e8c` - [ ] Unit test: `send_first_payment_email()` for a `Division.girls` player produces HTML containing `#e91e8c` - [ ] Unit test: `send_first_payment_email()` for a `Division.boys` player produces HTML containing `#d42026` - Run: `pytest tests/test_first_payment_email.py -v` ### Constraints - Colors must match westside-contracts CSS exactly: `#e91e8c` (pink), `#f23d9e` (hover) - `accent_color` param must be backward-compatible — existing callers of `_brand_wrapper()` don't change - Division check uses `player.division == Division.girls` (same pattern as contract page's `isGirls`) - If `player.division` is None, default to Kings red (treat as non-girls) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `westside-basketball` — project - Queens CSS: `westside-contracts/src/app.css:26-31` - Program toggle: `westside-app/src/lib/program.svelte.js`
Author
Owner

Scope Review: NEEDS_REFINEMENT

Review note: review-910-2026-04-06

Scope, approach, and traceability are sound. File targets all verified against origin/main. Two refinements before promoting to todo:

  • [BODY] Stale line numbers_BRAND_RED is at src/basketball_api/services/email.py:192 (not 357), and _brand_wrapper is at line 200 (not 365). PRs #376 and #411 shifted lines after this issue was drafted.
  • [SCOPE] Missing arch notesarch-basketball-api and arch-email labels are present but no backing notes exist in pal-e-docs. Platform-wide gap, not unique to this ticket — should be tracked as separate backlog items but does not block this work.

Decomposition: not needed. 2 files, 1 repo, 6 AC, 4 unit tests — well under the 5-minute rule.

Hard dependency on #369 (send_first_payment_email) is satisfied (PR #376 merged Apr 7). Division.girls = "girls" confirmed in models.py:54. Backward-compatible default-param approach correctly insulates the 8 other _brand_wrapper callers.

## Scope Review: NEEDS_REFINEMENT Review note: `review-910-2026-04-06` Scope, approach, and traceability are sound. File targets all verified against `origin/main`. Two refinements before promoting to `todo`: - **[BODY] Stale line numbers** — `_BRAND_RED` is at `src/basketball_api/services/email.py:192` (not 357), and `_brand_wrapper` is at line 200 (not 365). PRs #376 and #411 shifted lines after this issue was drafted. - **[SCOPE] Missing arch notes** — `arch-basketball-api` and `arch-email` labels are present but no backing notes exist in pal-e-docs. Platform-wide gap, not unique to this ticket — should be tracked as separate backlog items but does not block this work. Decomposition: **not needed**. 2 files, 1 repo, 6 AC, 4 unit tests — well under the 5-minute rule. Hard dependency on #369 (`send_first_payment_email`) is satisfied (PR #376 merged Apr 7). `Division.girls = "girls"` confirmed in `models.py:54`. Backward-compatible default-param approach correctly insulates the 8 other `_brand_wrapper` callers.
Author
Owner

Scope Re-Review: READY

Review note: review-910-2026-04-06-v2

Line numbers verified against origin/main (9c487dc):

  • _BRAND_RED = "#d42026" at email.py:192 ✓
  • def _brand_wrapper at email.py:200 ✓
  • def send_first_payment_email at email.py:951 ✓
  • _brand_wrapper call inside send_first_payment_email at email.py:1039

Previous NEEDS_REFINEMENT (stale 357 -> 192) is resolved. Traceability (story:WS-S7) valid. File targets all exist. No blocking dependencies. Fits single-agent pass.

Minor note (non-blocking): acceptance criterion says "All 7 other _brand_wrapper() callers unchanged" but actual count on origin/main is 10 other callers (lines 343, 470, 665, 798, 906, 1408, 1512, 1853, 2000, 2180). Intent is clear — all existing callers must remain unchanged. Dev agent will see the real count.

Ready to move backlog -> todo.

## Scope Re-Review: READY Review note: `review-910-2026-04-06-v2` Line numbers verified against origin/main (9c487dc): - `_BRAND_RED = "#d42026"` at email.py:192 ✓ - `def _brand_wrapper` at email.py:200 ✓ - `def send_first_payment_email` at email.py:951 ✓ - `_brand_wrapper` call inside `send_first_payment_email` at email.py:1039 Previous NEEDS_REFINEMENT (stale 357 -> 192) is resolved. Traceability (story:WS-S7) valid. File targets all exist. No blocking dependencies. Fits single-agent pass. Minor note (non-blocking): acceptance criterion says "All 7 other `_brand_wrapper()` callers unchanged" but actual count on origin/main is 10 other callers (lines 343, 470, 665, 798, 906, 1408, 1512, 1853, 2000, 2180). Intent is clear — all existing callers must remain unchanged. Dev agent will see the real count. Ready to move backlog -> todo.
forgejo_admin 2026-04-10 05:38:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#413
No description provided.