patx/projectpay

add dark mode

Commit d910bc4 · patx · 2026-07-06T01:06:48-04:00

Changeset
d910bc40cc418ab1bb7566ae991cd8f0988e3f76
Parents
bd97cd328ce6fb4595f92c5c5b0d18791e01bb67

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/templates/base.html b/templates/base.html
index de12dfe..ccee373 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -7,10 +7,62 @@
     <style>
       * { box-sizing: border-box; }
       [hidden] { display: none !important; }
+      :root {
+        color-scheme: light;
+        --page-bg: #f7f7f7;
+        --surface-bg: #fff;
+        --surface-subtle: #fafafa;
+        --field-bg: #fff;
+        --text-color: #1f2933;
+        --muted-color: #697586;
+        --border-color: #cfd6dd;
+        --border-subtle: #eceff3;
+        --primary-color: #176b5b;
+        --primary-hover-color: #13584c;
+        --primary-contrast: #fff;
+        --danger-color: #b42318;
+        --danger-bg: #fff0ee;
+        --danger-border: #efb5ad;
+        --warning-color: #8a5a00;
+        --warning-bg: #fff8e8;
+        --success-color: #166534;
+        --success-bg: #eaf7ef;
+        --info-color: #164c7d;
+        --info-bg: #edf5ff;
+        --sticky-bg: rgba(247, 247, 247, .96);
+        --shadow-color: rgba(31, 41, 51, .22);
+      }
+      @media (prefers-color-scheme: dark) {
+        :root {
+          color-scheme: dark;
+          --page-bg: #0f1720;
+          --surface-bg: #17212b;
+          --surface-subtle: #1f2a36;
+          --field-bg: #101923;
+          --text-color: #e6edf3;
+          --muted-color: #a8b3c2;
+          --border-color: #344454;
+          --border-subtle: #2a3745;
+          --primary-color: #1f7a6d;
+          --primary-hover-color: #279382;
+          --primary-contrast: #fff;
+          --danger-color: #ffb4aa;
+          --danger-bg: #3b1715;
+          --danger-border: #71332d;
+          --warning-color: #ffd47a;
+          --warning-bg: #36270c;
+          --success-color: #8ee3a6;
+          --success-bg: #12301f;
+          --info-color: #9bd2ff;
+          --info-bg: #132c45;
+          --sticky-bg: rgba(15, 23, 32, .96);
+          --shadow-color: rgba(0, 0, 0, .35);
+        }
+      }
       body {
         margin: 0;
-        background: #f7f7f7;
-        color: #1f2933;
+        background: var(--page-bg);
+        color: var(--text-color);
         font: 15px/1.45 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
       }
       a { color: inherit; }
@@ -19,11 +71,13 @@
       h2 { font-size: 18px; margin: 0 0 12px; }
       table { width: 100%; border-collapse: collapse; }
       th, td { padding: 20px 0; text-align: left; vertical-align: top; }
-      th { color: #697586; font-size: 13px; }
+      th { color: var(--muted-color); font-size: 13px; }
       input, textarea {
         width: 100%;
-        border: 1px solid #cfd6dd;
+        border: 1px solid var(--border-color);
         border-radius: 6px;
+        background: var(--field-bg);
+        color: var(--text-color);
         padding: 10px;
         font: inherit;
       }
@@ -33,19 +87,19 @@
         align-items: center;
         justify-content: center;
         min-height: 38px;
-        border: 1px solid #176b5b;
+        border: 1px solid var(--primary-color);
         border-radius: 6px;
-        background: #176b5b;
-        color: #fff;
+        background: var(--primary-color);
+        color: var(--primary-contrast);
         padding: 8px 12px;
         font: inherit;
         font-weight: 650;
         text-decoration: none;
         cursor: pointer;
       }
-      .secondary { background: #fff; border-color: #cfd6dd; color: #1f2933; }
-      .danger { background: #fff; border-color: #efb5ad; color: #b42318; }
-      .topbar { background: #fff; }
+      .secondary { background: var(--surface-bg); border-color: var(--border-color); color: var(--text-color); }
+      .danger { background: var(--surface-bg); border-color: var(--danger-border); color: var(--danger-color); }
+      .topbar { background: var(--surface-bg); }
       .topbar-inner, .page { max-width: 1080px; margin: 0 auto; padding: 16px 20px; }
       .topbar-inner, .header-row, .nav, .searchbar {
         display: flex;
@@ -69,7 +123,7 @@
         gap: 10px;
       }
       .back-link {
-        color: #697586;
+        color: var(--muted-color);
         font-size: 24px;
         line-height: 1;
         text-decoration: none;
@@ -77,12 +131,12 @@
       .page { padding-top: 28px; padding-bottom: 48px; }
       .narrow { max-width: 680px; }
       .section {
-        background: #fff;
+        background: var(--surface-bg);
         border-radius: 8px;
         padding: 18px;
         margin-bottom: 16px;
       }
-      .muted, .footer-note { color: #697586; }
+      .muted, .footer-note { color: var(--muted-color); }
       .field { margin-bottom: 14px; }
       .prewrap { white-space: pre-wrap; }
       .star-emphasis { font-size: calc(1em + 1px); font-weight: 700; }
@@ -95,34 +149,34 @@
       .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
       .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 16px 0; }
       .stat { border-radius: 8px; padding: 12px; }
-      .stat span { display: block; color: #697586; font-size: 13px; }
+      .stat span { display: block; color: var(--muted-color); font-size: 13px; }
       .stat strong { font-size: 19px; }
-      .alert { border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; }
-      .alert.error { border-color: #efb5ad; color: #b42318; background: #fff0ee; }
-      .alert.info { background: #fff8e8; }
-      .alert.ok { background: #eaf7ef; }
+      .alert { border: 1px solid transparent; border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; }
+      .alert.error { border-color: var(--danger-border); color: var(--danger-color); background: var(--danger-bg); }
+      .alert.info { color: var(--warning-color); background: var(--warning-bg); }
+      .alert.ok { color: var(--success-color); background: var(--success-bg); }
       .status {
         border-radius: 999px;
-        background: #edf5ff;
-        color: #164c7d;
+        background: var(--info-bg);
+        color: var(--info-color);
         padding: 3px 9px;
         font-size: 13px;
         font-weight: 700;
         text-transform: capitalize;
       }
       .status.paid,
-      .status.succeeded { background: #eaf7ef; color: #166534; }
+      .status.succeeded { background: var(--success-bg); color: var(--success-color); }
       .status.processing,
-      .status.pending { background: #fff8e8; color: #8a5a00; }
+      .status.pending { background: var(--warning-bg); color: var(--warning-color); }
       .status.failed,
-      .status.canceled { background: #fff0ee; color: #b42318; }
+      .status.canceled { background: var(--danger-bg); color: var(--danger-color); }
       .project-list {
         display: grid;
         gap: 12px;
       }
       .project-card,
       .payment-card {
-        background: #fff;
+        background: var(--surface-bg);
         border-radius: 8px;
         color: inherit;
         text-decoration: none;
@@ -130,7 +184,7 @@
       }
       .invoice-line-card,
       .invoice-summary {
-        background: #fff;
+        background: var(--surface-bg);
         color: inherit;
         text-decoration: none;
         padding: 14px;
@@ -146,11 +200,11 @@
         font-size: 18px;
         margin: 0 0 4px;
       }
-      .project-card-id { color: #697586; font-size: 13px; }
+      .project-card-id { color: var(--muted-color); font-size: 13px; }
       .project-card-amount { text-align: right; }
       .project-card-amount span {
         display: block;
-        color: #697586;
+        color: var(--muted-color);
         font-size: 13px;
       }
       .project-card-footer {
@@ -160,7 +214,7 @@
       }
       .payment-card-reference { overflow-wrap: anywhere; }
       .line-items-section {
-        background: #fff;
+        background: var(--surface-bg);
         border-radius: 8px;
         margin-bottom: 16px;
         overflow: hidden;
@@ -186,7 +240,7 @@
       .invoice-line-amount span,
       .invoice-summary-row span {
         display: block;
-        color: #697586;
+        color: var(--muted-color);
         font-size: 13px;
       }
       .invoice-line-amount strong,
@@ -204,7 +258,7 @@
         align-items: baseline;
       }
       .invoice-summary-row + .invoice-summary-row {
-        border-top: 1px solid #eceff3;
+        border-top: 1px solid var(--border-subtle);
         padding-top: 8px;
       }
       .invoice-summary-total strong { font-size: 18px; }
@@ -216,7 +270,7 @@
         border-radius: 8px;
         padding: 14px;
         margin-bottom: 12px;
-        background: #fafafa;
+        background: var(--surface-subtle);
       }
       .line-item-text { grid-column: 1 / -1; }
       .line-item-price { max-width: 180px; }
@@ -239,11 +293,11 @@
         width: 28px;
         border: 0;
         background: transparent;
-        color: #697586;
+        color: var(--muted-color);
         padding: 0;
         transform: translateY(-50%);
       }
-      .search-clear:hover { background: transparent; color: #1f2933; }
+      .search-clear:hover { background: transparent; color: var(--text-color); }
       .copy-field { position: relative; }
       .copy-field input {
         cursor: pointer;
@@ -255,7 +309,7 @@
         right: 12px;
         width: 22px;
         height: 22px;
-        color: #697586;
+        color: var(--muted-color);
         pointer-events: none;
         transform: translateY(-50%);
       }
@@ -267,14 +321,14 @@
         height: 13px;
         border: 1.5px solid currentColor;
         border-radius: 2px;
-        background: #fff;
+        background: var(--surface-bg);
       }
       .copy-indicator::before { left: 2px; top: 6px; }
       .copy-indicator::after { left: 7px; top: 1px; }
       .copy-indicator.copied {
         width: auto;
         height: auto;
-        color: #176b5b;
+        color: var(--primary-color);
         font-size: 13px;
         font-weight: 700;
       }
@@ -294,7 +348,7 @@
         right: 0;
         bottom: 0;
         z-index: 10;
-        background: rgba(247, 247, 247, .96);
+        background: var(--sticky-bg);
         padding: 12px 20px;
       }
       .sticky-pay-inner {
@@ -313,7 +367,7 @@
         right: 0;
         bottom: 0;
         z-index: 10;
-        background: rgba(247, 247, 247, .96);
+        background: var(--sticky-bg);
         padding: 12px 20px;
       }
       .sticky-actions-inner {
@@ -341,7 +395,7 @@
         left: 0;
         right: 0;
         z-index: 10;
-        background: rgba(247, 247, 247, .96);
+        background: var(--sticky-bg);
         padding: 12px 20px;
       }
       .sticky-index-search {
@@ -364,11 +418,11 @@
         z-index: 11;
         width: 56px;
         height: 56px;
-        border: 1px solid #176b5b;
+        border: 1px solid var(--primary-color);
         border-radius: 50%;
-        background: #176b5b;
-        color: #fff;
-        box-shadow: 0 10px 24px rgba(31, 41, 51, .22);
+        background: var(--primary-color);
+        color: var(--primary-contrast);
+        box-shadow: 0 10px 24px var(--shadow-color);
         font-size: 34px;
         font-weight: 500;
         line-height: 1;
@@ -376,10 +430,10 @@
         text-decoration: none;
       }
       .floating-new-project:hover {
-        background: #13584c;
-        border-color: #13584c;
+        background: var(--primary-hover-color);
+        border-color: var(--primary-hover-color);
       }
-      .empty { color: #697586; padding: 18px 0; }
+      .empty { color: var(--muted-color); padding: 18px 0; }
       .amount { text-align: right; }
       @media (max-width: 760px) {
         .topbar-inner, .header-row, .nav, .searchbar { align-items: stretch; flex-direction: column; }