diff options
| -rw-r--r-- | .tasks/tasks.jsonl | 2 | ||||
| -rw-r--r-- | Biz/PodcastItLater/Web.py | 181 |
2 files changed, 64 insertions, 119 deletions
diff --git a/.tasks/tasks.jsonl b/.tasks/tasks.jsonl index 335b615..7749a03 100644 --- a/.tasks/tasks.jsonl +++ b/.tasks/tasks.jsonl @@ -35,4 +35,4 @@ {"taskCreatedAt":"2025-11-09T22:19:27.303689497Z","taskDependencies":[],"taskId":"t-1pIV0ZF","taskNamespace":"Biz/PodcastItLater.hs","taskParent":"t-143KQl2","taskStatus":"Done","taskTitle":"Implement billing page UI component with pricing and upgrade options","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T23:04:20.974801117Z"} {"taskCreatedAt":"2025-11-09T22:38:46.235799803Z","taskDependencies":[],"taskId":"t-1qZlMb4","taskNamespace":null,"taskParent":null,"taskStatus":"Open","taskTitle":"Add a 'task show <id>' command that prints out a long, easy to read (for humans) version of the task. Include dependencies and all information fields in the output","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T22:38:46.235799803Z"} {"taskCreatedAt":"2025-11-09T22:56:18.897655607Z","taskDependencies":[],"taskId":"t-1s8ADC0","taskNamespace":"Biz/PodcastItLater.hs","taskParent":null,"taskStatus":"Done","taskTitle":"Make PodcastItLater UI mobile-friendly and responsive","taskType":"WorkTask","taskUpdatedAt":"2025-11-09T23:09:16.712244322Z"} -{"taskCreatedAt":"2025-11-10T01:32:42.893029428Z","taskDependencies":[],"taskId":"t-64tkB5","taskNamespace":"Biz/PodcastItLater.hs","taskParent":null,"taskStatus":"InProgress","taskTitle":"Add dark mode support to PodcastItLater UI","taskType":"WorkTask","taskUpdatedAt":"2025-11-10T01:32:46.388157028Z"} +{"taskCreatedAt":"2025-11-10T01:32:42.893029428Z","taskDependencies":[],"taskId":"t-64tkB5","taskNamespace":"Biz/PodcastItLater.hs","taskParent":null,"taskStatus":"Done","taskTitle":"Add dark mode support to PodcastItLater UI","taskType":"WorkTask","taskUpdatedAt":"2025-11-10T01:36:37.714733642Z"} diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py index 39e2240..d91587a 100644 --- a/Biz/PodcastItLater/Web.py +++ b/Biz/PodcastItLater/Web.py @@ -53,55 +53,34 @@ from typing import override logger = Log.setup() -def create_theme_switcher_script() -> html.script: - """Create JavaScript for theme switching with localStorage persistence.""" - return html.script( +def create_auto_dark_mode_style() -> html.style: + """Create CSS for automatic dark mode based on prefers-color-scheme.""" + return html.style( """ - // Theme switcher with localStorage persistence - (function() { - const getStoredTheme = () => localStorage.getItem('theme'); - const setStoredTheme = (theme) => { - localStorage.setItem('theme', theme); - }; - const getPreferredTheme = () => { - const storedTheme = getStoredTheme(); - if (storedTheme) { - return storedTheme; - } - return window.matchMedia('(prefers-color-scheme: dark)').matches - ? 'dark' : 'light'; - }; - - const setTheme = theme => { - document.documentElement.setAttribute('data-bs-theme', theme); - const icon = document.getElementById('theme-icon'); - if (icon) { - icon.className = theme === 'dark' - ? 'bi bi-sun-fill' - : 'bi bi-moon-fill'; - } - }; - - // Set theme on page load - setTheme(getPreferredTheme()); - - // Theme toggle button handler - window.toggleTheme = () => { - const currentTheme = document.documentElement - .getAttribute('data-bs-theme'); - const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; - setTheme(newTheme); - setStoredTheme(newTheme); - }; - - // Listen for system theme changes - window.matchMedia('(prefers-color-scheme: dark)') - .addEventListener('change', e => { - if (!getStoredTheme()) { - setTheme(e.matches ? 'dark' : 'light'); - } - }); - })(); + /* Auto dark mode - sets Bootstrap theme based on system preference */ + @media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + } + html { + --bs-body-color: #dee2e6; + --bs-body-bg: #212529; + --bs-emphasis-color: #fff; + --bs-border-color: #495057; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-code-color: #e685b5; + } + body { + color: var(--bs-body-color); + background-color: var(--bs-body-bg); + } + } + @media (prefers-color-scheme: light) { + :root { + color-scheme: light; + } + } """, ) @@ -687,6 +666,10 @@ class BillingPage(Component[AnyChildren, BillingPageAttrs]): name="viewport", content="width=device-width, initial-scale=1", ), + html.meta( + name="color-scheme", + content="light dark", + ), html.title("Billing - PodcastItLater"), html.script( src="https://unpkg.com/htmx.org@1.9.10", @@ -701,51 +684,32 @@ class BillingPage(Component[AnyChildren, BillingPageAttrs]): "@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons" "@1.11.3/font/bootstrap-icons.min.css');", ), + # Auto dark mode CSS (must come after Bootstrap) + create_auto_dark_mode_style(), html.div( html.div( + html.h1( + html.i( + classes=["bi", "bi-credit-card", "me-2"], + ), + "Billing & Usage", + classes=["mb-4"], + ), html.div( - html.h1( + html.a( html.i( - classes=["bi", "bi-credit-card", "me-2"], - ), - "Billing & Usage", - classes=["mb-4"], - ), - html.div( - html.a( - html.i( - classes=["bi", "bi-arrow-left", "me-1"], - ), - "Back to Dashboard", - href="/", - classes=[ - "btn", - "btn-outline-secondary", - "mb-4", - ], + classes=["bi", "bi-arrow-left", "me-1"], ), + "Back to Dashboard", + href="/", + classes=[ + "btn", + "btn-outline-secondary", + "mb-4", + ], ), - classes=["flex-grow-1"], - ), - html.button( - html.i( - id="theme-icon", - classes=["bi", "bi-moon-fill"], - ), - on_click="toggleTheme()", - classes=[ - "btn", - "btn-outline-secondary", - "position-absolute", - "top-0", - "end-0", - "mt-3", - "me-3", - ], - title="Toggle dark mode", - type="button", ), - classes=["position-relative", "mb-4"], + classes=["mb-4"], ), # Success/Error alerts html.div( @@ -882,8 +846,6 @@ class BillingPage(Component[AnyChildren, BillingPageAttrs]): integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL", crossorigin="anonymous", ), - # Theme switcher script - create_theme_switcher_script(), ), ) @@ -999,6 +961,10 @@ class HomePage(Component[AnyChildren, HomePageAttrs]): name="viewport", content="width=device-width, initial-scale=1", ), + html.meta( + name="color-scheme", + content="light dark", + ), html.title("PodcastItLater"), html.script( src="https://unpkg.com/htmx.org@1.9.10", @@ -1013,40 +979,21 @@ class HomePage(Component[AnyChildren, HomePageAttrs]): "@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons" "@1.11.3/font/bootstrap-icons.min.css');", ), + # Auto dark mode CSS (must come after Bootstrap) + create_auto_dark_mode_style(), # Bootstrap container html.div( # Header html.div( - html.div( - html.h1( - "PodcastItLater", - classes=["display-4", "mb-2"], - ), - html.p( - "Convert web articles to podcast episodes", - classes=["lead", "text-muted"], - ), - classes=["text-center", "flex-grow-1"], + html.h1( + "PodcastItLater", + classes=["display-4", "mb-2"], ), - html.button( - html.i( - id="theme-icon", - classes=["bi", "bi-moon-fill"], - ), - on_click="toggleTheme()", - classes=[ - "btn", - "btn-outline-secondary", - "position-absolute", - "top-0", - "end-0", - "mt-3", - "me-3", - ], - title="Toggle dark mode", - type="button", + html.p( + "Convert web articles to podcast episodes", + classes=["lead", "text-muted"], ), - classes=["position-relative", "mb-4", "pt-4"], + classes=["text-center", "mb-4", "pt-4"], ), # Error alert html.div( @@ -1180,8 +1127,6 @@ class HomePage(Component[AnyChildren, HomePageAttrs]): integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL", crossorigin="anonymous", ), - # Theme switcher script - create_theme_switcher_script(), ), ) |
