feat: unify site theming via CSS custom properties (GH #91)

Replace all hardcoded brand color hex values with CSS custom properties
so BrandingContext drives both the customer portal and staff site.

- index.css: add derived accent/primary vars using color-mix()
  (--color-accent-hover, --color-accent-dark, --color-accent-light,
  --color-accent-lighter, --color-primary-dark); fix focus ring styles
  to use var(--color-primary) instead of hardcoded hex
- BrandingContext.tsx: also update <meta name="theme-color"> in sync
  with primaryColor so PWA theme-color tracks branding at runtime
- portal/sections: replace bg-[#8b7355], text-[#6b5a42], bg-[#f0ebe4],
  bg-[#faf5ef], hover:bg-[#7a6549] etc. with Tailwind v4 CSS var
  utilities (bg-(--color-accent), text-(--color-accent-dark), etc.)
- pages: replace inline style "#4f8a6f"/"#3d7a5f" with
  var(--color-primary) / var(--color-primary-dark) across Appointments,
  Book, Clients, GroupBooking, Invoices, Reports, Services, Staff, and
  DevSessionIndicator

Closes #91

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Scrubs McBarkley
2026-03-21 23:50:43 +00:00
parent 8fdffb9564
commit afde6b7857
18 changed files with 102 additions and 89 deletions
+3 -3
View File
@@ -291,7 +291,7 @@ export function AppointmentsPage() {
</button>
<button
onClick={() => openNewForm()}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", marginLeft: "auto", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", marginLeft: "auto", borderColor: "var(--color-primary)" }}
>
+ New Appointment
</button>
@@ -374,7 +374,7 @@ export function AppointmentsPage() {
<div
style={{
padding: "0.4rem 0.6rem",
background: isToday ? "linear-gradient(135deg, #4f8a6f, #3d7a5f)" : "#f8fafc",
background: isToday ? "linear-gradient(135deg, var(--color-primary), var(--color-primary-dark))" : "#f8fafc",
color: isToday ? "#fff" : "#374151",
fontWeight: 600,
fontSize: 12,
@@ -594,7 +594,7 @@ export function AppointmentsPage() {
<button
type="submit"
disabled={saving}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}
>
{saving
? "Saving…"
+8 -8
View File
@@ -66,8 +66,8 @@ function StepIndicator({ step }: { step: number }) {
padding: "0.5rem 0.25rem",
fontSize: 12,
fontWeight: active ? 700 : 400,
color: active ? "#4f8a6f" : done ? "#4f8a6f" : "#9ca3af",
borderBottom: `3px solid ${active ? "#4f8a6f" : done ? "#4f8a6f" : "#e5e7eb"}`,
color: active ? "var(--color-primary)" : done ? "var(--color-primary)" : "#9ca3af",
borderBottom: `3px solid ${active ? "var(--color-primary)" : done ? "var(--color-primary)" : "#e5e7eb"}`,
}}
>
<span
@@ -78,7 +78,7 @@ function StepIndicator({ step }: { step: number }) {
width: 22,
height: 22,
borderRadius: "50%",
background: active ? "#4f8a6f" : done ? "#4f8a6f" : "#e5e7eb",
background: active ? "var(--color-primary)" : done ? "var(--color-primary)" : "#e5e7eb",
color: active || done ? "#fff" : "#6b7280",
fontSize: 12,
fontWeight: 700,
@@ -218,7 +218,7 @@ export function BookPage() {
const selectedCard: React.CSSProperties = {
...card,
border: "2px solid #4f8a6f",
border: "2px solid var(--color-primary)",
background: "#f0faf5",
};
@@ -250,7 +250,7 @@ export function BookPage() {
const primaryBtn: React.CSSProperties = {
...btn,
background: "#4f8a6f",
background: "var(--color-primary)",
color: "#fff",
};
@@ -301,7 +301,7 @@ export function BookPage() {
)}
</div>
<div style={{ textAlign: "right", flexShrink: 0, marginLeft: "1rem" }}>
<div style={{ fontWeight: 700, color: "#4f8a6f", fontSize: 15 }}>
<div style={{ fontWeight: 700, color: "var(--color-primary)", fontSize: 15 }}>
{fmtPrice(svc.basePriceCents)}
</div>
<div style={{ fontSize: 12, color: "#9ca3af" }}>{fmtDuration(svc.durationMinutes)}</div>
@@ -349,8 +349,8 @@ export function BookPage() {
style={{
padding: "0.4rem 0.85rem",
borderRadius: 6,
border: `2px solid ${selectedSlot === slot ? "#4f8a6f" : "#d1d5db"}`,
background: selectedSlot === slot ? "#4f8a6f" : "#fff",
border: `2px solid ${selectedSlot === slot ? "var(--color-primary)" : "#d1d5db"}`,
background: selectedSlot === slot ? "var(--color-primary)" : "#fff",
color: selectedSlot === slot ? "#fff" : "#374151",
fontSize: 13,
fontWeight: 500,
+3 -3
View File
@@ -367,7 +367,7 @@ export function ClientsPage() {
<h1 style={{ margin: 0, fontSize: 20 }}>Clients</h1>
<button
onClick={openNewClient}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f", marginLeft: "auto", padding: "0.3rem 0.7rem" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)", marginLeft: "auto", padding: "0.3rem 0.7rem" }}
>
+ New
</button>
@@ -622,7 +622,7 @@ export function ClientsPage() {
</Field>
{clientFormError && <p style={{ color: "red", margin: "0.5rem 0 0" }}>{clientFormError}</p>}
<div style={{ display: "flex", gap: "0.5rem", marginTop: "1rem" }}>
<button type="submit" disabled={savingClient} style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}>
<button type="submit" disabled={savingClient} style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}>
{savingClient ? "Saving…" : editingClient ? "Save Changes" : "Create Client"}
</button>
<button type="button" onClick={() => setShowClientForm(false)} style={btnStyle}>Cancel</button>
@@ -761,7 +761,7 @@ export function ClientsPage() {
</Field>
{logFormError && <p style={{ color: "red", margin: "0.5rem 0 0" }}>{logFormError}</p>}
<div style={{ display: "flex", gap: "0.5rem", marginTop: "1rem" }}>
<button type="submit" disabled={savingLog} style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}>
<button type="submit" disabled={savingLog} style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}>
{savingLog ? "Saving…" : "Save Visit Log"}
</button>
<button type="button" onClick={() => setShowLogForm(false)} style={btnStyle}>Cancel</button>
+2 -2
View File
@@ -287,7 +287,7 @@ function NewGroupBookingForm({
<button
type="submit"
disabled={saving}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}
>
{saving ? "Booking…" : "Create Group Booking"}
</button>
@@ -471,7 +471,7 @@ export function GroupBookingPage() {
</select>
<button
onClick={() => setShowCreate(true)}
style={{ ...btnStyle, marginLeft: "auto", backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, marginLeft: "auto", backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}
>
+ New Group Booking
</button>
+2 -2
View File
@@ -129,7 +129,7 @@ function CreateFromAppointmentForm({
<button
type="submit"
disabled={saving || !selectedApptId}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}
>
{saving ? "Creating…" : "Create Invoice"}
</button>
@@ -540,7 +540,7 @@ export function InvoicesPage() {
</select>
<button
onClick={() => setShowCreate(true)}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f", marginLeft: "auto" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)", marginLeft: "auto" }}
>
+ Create Invoice
</button>
+1 -1
View File
@@ -270,7 +270,7 @@ export function ReportsPage() {
<option value="month">Month</option>
</select>
</label>
<button onClick={loadAll} style={{ ...btnStyle, background: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}>
<button onClick={loadAll} style={{ ...btnStyle, background: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}>
{loading ? "Loading…" : "Refresh"}
</button>
<div style={{ marginLeft: "auto", display: "flex", gap: "0.5rem" }}>
+2 -2
View File
@@ -119,7 +119,7 @@ export function ServicesPage() {
<h1 style={{ margin: 0 }}>Services</h1>
<button
onClick={openNew}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f", marginLeft: "auto" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)", marginLeft: "auto" }}
>
+ Add Service
</button>
@@ -232,7 +232,7 @@ export function ServicesPage() {
<button
type="submit"
disabled={saving}
style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}
style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}
>
{saving ? "Saving…" : editing ? "Save Changes" : "Create Service"}
</button>
+2 -2
View File
@@ -78,7 +78,7 @@ export function StaffPage() {
<div style={{ fontFamily: "system-ui, sans-serif" }}>
<div style={{ display: "flex", alignItems: "center", gap: "1rem", marginBottom: "1rem" }}>
<h1 style={{ margin: 0 }}>Staff</h1>
<button onClick={openNew} style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f", marginLeft: "auto" }}>
<button onClick={openNew} style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)", marginLeft: "auto" }}>
+ Add Staff
</button>
</div>
@@ -145,7 +145,7 @@ export function StaffPage() {
</div>
{formError && <p style={{ color: "red", margin: "0.5rem 0 0" }}>{formError}</p>}
<div style={{ display: "flex", gap: "0.5rem", marginTop: "1rem" }}>
<button type="submit" disabled={saving} style={{ ...btnStyle, backgroundColor: "#4f8a6f", color: "#fff", borderColor: "#4f8a6f" }}>
<button type="submit" disabled={saving} style={{ ...btnStyle, backgroundColor: "var(--color-primary)", color: "#fff", borderColor: "var(--color-primary)" }}>
{saving ? "Saving…" : editing ? "Save Changes" : "Add Staff"}
</button>
<button type="button" onClick={() => setShowForm(false)} style={btnStyle}>Cancel</button>