From 0f7d4292a417da6bfbab3a2ae7ea943a59d9c151 Mon Sep 17 00:00:00 2001 From: CaliBrain Date: Sun, 16 Mar 2025 03:00:56 -0400 Subject: [PATCH] feat: implement dark mode with improved UI contrast and full-height layout (#91) - Add theme toggle (light/dark/auto) - Improve text contrast and readability - Fix viewport height issues - Ensure consistent styling across components --- config.py | 6 +- static/css/styles.css | 207 +++++++++++++++++++++++++++++++++++++++++- static/js/main.js | 63 ++++++++++++- templates/index.html | 17 +++- 4 files changed, 285 insertions(+), 8 deletions(-) diff --git a/config.py b/config.py index d7a2203..5706f65 100644 --- a/config.py +++ b/config.py @@ -67,15 +67,15 @@ if len(BOOK_LANGUAGE) == 0: BOOK_LANGUAGE = ['en'] # Custom script settings -CUSTOM_SCRIPT = os.getenv("CUSTOM_SCRIPT", None).strip() +CUSTOM_SCRIPT = os.getenv("CUSTOM_SCRIPT", "").strip() # check if the script is valid if CUSTOM_SCRIPT: if not os.path.exists(CUSTOM_SCRIPT): logger.error(f"Custom script {CUSTOM_SCRIPT} does not exist") - CUSTOM_SCRIPT = None + CUSTOM_SCRIPT = "" elif not os.access(CUSTOM_SCRIPT, os.X_OK): logger.error(f"Custom script {CUSTOM_SCRIPT} is not executable") - CUSTOM_SCRIPT = None + CUSTOM_SCRIPT = "" # API settings FLASK_HOST = os.getenv("FLASK_HOST", "0.0.0.0") diff --git a/static/css/styles.css b/static/css/styles.css index a65f3d8..dacfb21 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -1,13 +1,33 @@ /* Base styles and CSS reset */ :root { + /* Light theme variables */ --primary-color: #0073e6; --primary-dark: #005bb5; --text-color: #333; - --background-color: #fdfdfd; - --border-color: #ccc; + --background-color: #f8f8f8; + --border-color: #e5e5e5; --header-bg: #333; --header-text: #fff; --loading-overlay: rgba(0, 0, 0, 0.5); + --card-background: #fff; + --table-background: #fff; + --table-hover-background: #f8f8f8; + --table-border-color: #e5e5e5; + --input-background: #fff; + --heading-color: #333; +} + +[data-theme="dark"] { + /* Dark theme variables with improved contrast */ + --background-color: #121212; + --text-color: #ffffff; + --heading-color: #ffffff; + --card-background: #1e1e1e; + --border-color: #404040; + --table-background: #1e1e1e; + --table-hover-background: #2d2d2d; + --table-border-color: #404040; + --input-background: #2d2d2d; } * { @@ -17,11 +37,26 @@ } /* Typography */ +html, body { + min-height: 100vh; + margin: 0; + padding: 0; +} + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: var(--text-color); background: var(--background-color); + transition: background-color 0.3s ease, color 0.3s ease; + display: flex; + flex-direction: column; +} + +main { + flex: 1; + min-height: 100%; + background: var(--background-color); } /* Layout */ @@ -36,9 +71,9 @@ header { footer { text-align: center; padding: 1rem; - margin-top: 2rem; background: var(--header-bg); color: var(--header-text); + margin-top: auto; } /* Search Section */ @@ -341,3 +376,169 @@ th[data-sort="index"] { text-align: center; padding: 1rem !important; } + +/* Dark mode specific styles */ +[data-theme="dark"] .uk-card, +[data-theme="dark"] .uk-modal-dialog { + background-color: var(--card-background); + color: var(--text-color); +} + +[data-theme="dark"] .uk-table { + color: var(--text-color); +} + +[data-theme="dark"] .uk-table-hover tbody tr:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +[data-theme="dark"] .uk-button-default { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +[data-theme="dark"] .uk-search-input { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +[data-theme="dark"] .uk-select { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Table styles */ +.uk-table { + background-color: var(--table-background); + color: var(--text-color); + border-color: var(--table-border-color); +} + +.uk-table th { + color: var(--text-color); + background-color: var(--table-background); + border-bottom-color: var(--table-border-color); +} + +.uk-table td { + color: var(--text-color); + background-color: var(--table-background); + border-bottom-color: var(--table-border-color); +} + +.uk-table-hover tbody tr:hover, +.uk-table-hover tbody tr:hover td { + background-color: var(--table-hover-background); +} + +/* Input styles */ +.uk-search-input, +.uk-input, +.uk-select, +.uk-textarea { + background-color: var(--input-background); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Button styles */ +.uk-button-default { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +.uk-button-default:hover { + background-color: var(--table-hover-background); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Dropdown styles */ +.uk-dropdown { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +.uk-dropdown-nav > li > a { + color: var(--text-color); +} + +.uk-dropdown-nav > li > a:hover { + background-color: var(--table-hover-background); +} + +/* Modal styles */ +.modal-overlay { + background-color: rgba(0, 0, 0, 0.5); +} + +.details-container { + background-color: var(--card-background); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Status colors - ensure they remain visible in dark mode */ +.status-downloading { + color: #4CAF50 !important; +} + +.status-available { + color: #2196F3 !important; +} + +.status-error { + color: #f44336 !important; +} + +/* Header specific button styles */ +header .uk-button-default { + color: var(--header-text) !important; + background-color: transparent; + border-color: var(--header-text); +} + +header .uk-button-default:hover { + background-color: rgba(255, 255, 255, 0.1); + border-color: var(--header-text); + color: var(--header-text) !important; +} + +/* Ensure dropdown text is visible when opened */ +header .uk-dropdown { + background-color: var(--card-background); +} + +header .uk-dropdown-nav > li > a { + color: var(--text-color); +} + +header .uk-dropdown-nav > li > a:hover { + color: var(--text-color); + background-color: var(--table-hover-background); +} + +/* Update headings to use heading color */ +h1, h2, h3, h4, h5, h6, +.uk-heading-small, +.uk-heading-medium, +.uk-heading-large, +.uk-heading-xlarge, +.uk-heading-2xlarge { + color: var(--heading-color) !important; +} + +/* Ensure accordion titles are visible */ +.uk-accordion-title { + color: var(--heading-color) !important; +} + +/* Ensure search results heading is visible */ +#results-section-accordion .uk-accordion-title h1 { + color: var(--heading-color) !important; +} diff --git a/static/js/main.js b/static/js/main.js index cc138db..257e870 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -22,7 +22,12 @@ document.addEventListener('DOMContentLoaded', () => { statusTable: document.getElementById('status-table'), statusTableBody: document.querySelector('#status-table tbody'), modalOverlay: document.getElementById('modal-overlay'), - detailsContainer: document.getElementById('details-container') + detailsContainer: document.getElementById('details-container'), + theme: { + toggle: document.getElementById('theme-toggle'), + text: document.getElementById('theme-text'), + dropdown: document.querySelector('[uk-dropdown]'), + } }; // State @@ -596,6 +601,61 @@ document.addEventListener('DOMContentLoaded', () => { } }; + // Theme Management + const theme = { + STORAGE_KEY: 'preferred-theme', + + init() { + this.loadTheme(); + this.setupListeners(); + }, + + loadTheme() { + const savedTheme = localStorage.getItem(this.STORAGE_KEY) || 'auto'; + this.applyThemePreference(savedTheme); + this.updateButtonText(savedTheme); + }, + + applyThemePreference(preference) { + if (preference === 'auto') { + const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; + document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light'); + } else { + document.documentElement.setAttribute('data-theme', preference); + } + }, + + setupListeners() { + // Listen for system theme changes + const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + darkModeMediaQuery.addEventListener('change', (e) => { + if (localStorage.getItem(this.STORAGE_KEY) === 'auto') { + document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light'); + } + }); + + // Theme selection listeners + const themeLinks = document.querySelectorAll('.uk-dropdown-nav a[data-theme]'); + themeLinks.forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const selectedTheme = e.target.getAttribute('data-theme'); + localStorage.setItem(this.STORAGE_KEY, selectedTheme); + this.applyThemePreference(selectedTheme); + this.updateButtonText(selectedTheme); + }); + }); + }, + + updateButtonText(currentTheme) { + const themeText = currentTheme.charAt(0).toUpperCase() + currentTheme.slice(1); + const themeTextElement = document.getElementById('theme-text'); + if (themeTextElement) { + themeTextElement.textContent = themeText; + } + } + }; + // Event Listeners function setupEventListeners() { // Search events @@ -671,6 +731,7 @@ document.addEventListener('DOMContentLoaded', () => { // Initialize function init() { setupEventListeners(); + theme.init(); // Initialize theme management status.fetch(); setInterval(() => status.fetch(), REFRESH_INTERVAL); } diff --git a/templates/index.html b/templates/index.html index 6fc6b75..70bfa95 100644 --- a/templates/index.html +++ b/templates/index.html @@ -18,7 +18,22 @@
-

Book Search & Download

+
+

Book Search & Download

+
+ +
+ +
+
+