Add admin-configurable branding (colors, logo, favicon) and generic default fallbacks

Site Settings -> Branding now supports a Primary/Secondary/Accent brand color
system applied site-wide (buttons, nav, hover states, links) and to outgoing
email header/CTA colors, plus a favicon upload alongside the existing logo
upload, a live preview panel (website/email x desktop/mobile), and
logo-based color suggestions. The setup wizard's Branding step got the same
treatment. Fixes two related bugs found along the way: the setup wizard's
logo/favicon upload was missing its auth token, and a static favicon.ico in
Next's special app/ convention path was silently overriding the dynamic one.

Also replaces every "Hope Events"/"Hope Family Church" default (org name,
email subjects, WhatsApp messages, report metadata, API docs) with a neutral
"Cross Code" placeholder, and the optional legal settings (operator name, IO
details, website URL, effective date) with obviously-generic placeholders
instead of defaulting to real personal/organisational details -- since this
platform is deployed for multiple organisations. Adds SETTINGS.md documenting
every setting's default behaviour.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 14:49:51 +02:00
co-authored by Claude Sonnet 5
parent 2d99b7cafe
commit 86af7093ac
33 changed files with 1174 additions and 198 deletions
+28 -18
View File
@@ -1,20 +1,30 @@
"use client";
import { useEffect, useState } from "react";
/**
* Hex mirrors of the `brand` scale in tailwind.config.js, for the rare spot
* that needs a literal color value instead of a Tailwind class (SVG
* stroke/fill, canvas, etc.). Keep these in sync with tailwind.config.js by
* hand — there are few enough call sites that a build-time sync step isn't
* worth it.
*
* Do NOT use these for the org name/heading text — that stays bound to
* SiteSettingsContext.settings.accent_color, not the fixed brand palette.
* Static fallback for BRAND_600 — used as the initial value before the
* client can read the live CSS custom property (see useBrandColorVar below),
* and as the SSR/first-paint value. Keep in sync with globals.css's
* `--brand-600` default.
*/
export const BRAND_50 = "#EEF2FF";
export const BRAND_100 = "#E0E7FF";
export const BRAND_200 = "#C7D2FE";
export const BRAND_300 = "#A5B4FC";
export const BRAND_400 = "#818CF8";
export const BRAND_500 = "#6366F1";
export const BRAND_600 = "#4F46E5";
export const BRAND_700 = "#4338CA";
export const BRAND_800 = "#3730A3";
export const BRAND_900 = "#312E81";
const BRAND_600_FALLBACK = "#4F46E5";
/**
* Reads a live brand CSS custom property (e.g. `--brand-600`) off the
* document root. Needed by the rare spot that requires a literal color value
* instead of a Tailwind class (SVG stroke/fill, canvas, etc.) — Tailwind
* classes like `text-brand-600` pick up the admin-configurable primary color
* automatically via CSS variables (see tailwind.config.js), but a literal
* hex string has to be read explicitly since it can't reference `var()`.
*/
export function useBrandColorVar(cssVar: string, fallback: string = BRAND_600_FALLBACK): string {
const [color, setColor] = useState(fallback);
useEffect(() => {
const value = getComputedStyle(document.documentElement).getPropertyValue(cssVar).trim();
if (value) setColor(value);
}, [cssVar]);
return color;
}