From 756cda06f68ba6f3d88143b47453f31fe37a2644 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Mon, 1 Dec 2025 00:09:23 +0100 Subject: [PATCH] Start adding dark theme to popups on MapLibre maps --- app/assets/stylesheets/maps_v2.css | 67 +++++++++++ app/javascript/maps_v2/utils/popup_theme.js | 120 ++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 app/javascript/maps_v2/utils/popup_theme.js diff --git a/app/assets/stylesheets/maps_v2.css b/app/assets/stylesheets/maps_v2.css index dab869d4..5e6ef007 100644 --- a/app/assets/stylesheets/maps_v2.css +++ b/app/assets/stylesheets/maps_v2.css @@ -66,6 +66,73 @@ color: #111827; } +/* MapLibre Popup Theme Support */ +.maplibregl-popup-content { + padding: 16px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +/* Larger close button */ +.maplibregl-popup-close-button { + width: 32px; + height: 32px; + font-size: 24px; + line-height: 32px; + right: 4px; + top: 4px; + padding: 0; + border-radius: 4px; + transition: background-color 0.2s; +} + +.maplibregl-popup-close-button:hover { + background-color: rgba(0, 0, 0, 0.08); +} + +/* Light theme (default) */ +.maplibregl-popup-content { + background-color: #ffffff; + color: #111827; +} + +.maplibregl-popup-close-button { + color: #6b7280; +} + +.maplibregl-popup-close-button:hover { + background-color: #f3f4f6; + color: #111827; +} + +.maplibregl-popup-tip { + border-top-color: #ffffff; +} + +/* Dark theme */ +html[data-theme="dark"] .maplibregl-popup-content, +html.dark .maplibregl-popup-content { + background-color: #1f2937; + color: #f9fafb; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); +} + +html[data-theme="dark"] .maplibregl-popup-close-button, +html.dark .maplibregl-popup-close-button { + color: #d1d5db; +} + +html[data-theme="dark"] .maplibregl-popup-close-button:hover, +html.dark .maplibregl-popup-close-button:hover { + background-color: #374151; + color: #f9fafb; +} + +html[data-theme="dark"] .maplibregl-popup-tip, +html.dark .maplibregl-popup-tip { + border-top-color: #1f2937; +} + /* Connection Indicator */ .connection-indicator { position: absolute; diff --git a/app/javascript/maps_v2/utils/popup_theme.js b/app/javascript/maps_v2/utils/popup_theme.js new file mode 100644 index 00000000..95e8777c --- /dev/null +++ b/app/javascript/maps_v2/utils/popup_theme.js @@ -0,0 +1,120 @@ +/** + * Theme utilities for MapLibre popups + * Provides consistent theming across all popup types + */ + +/** + * Get current theme from document + * @returns {string} 'dark' or 'light' + */ +export function getCurrentTheme() { + if (document.documentElement.getAttribute('data-theme') === 'dark' || + document.documentElement.classList.contains('dark')) { + return 'dark' + } + return 'light' +} + +/** + * Get theme-aware color values + * @param {string} theme - 'dark' or 'light' + * @returns {Object} Color values for the theme + */ +export function getThemeColors(theme = getCurrentTheme()) { + if (theme === 'dark') { + return { + // Background colors + background: '#1f2937', + backgroundAlt: '#374151', + + // Text colors + textPrimary: '#f9fafb', + textSecondary: '#d1d5db', + textMuted: '#9ca3af', + + // Border colors + border: '#4b5563', + borderLight: '#374151', + + // Accent colors + accent: '#3b82f6', + accentHover: '#2563eb', + + // Badge colors + badgeSuggested: { bg: '#713f12', text: '#fef3c7' }, + badgeConfirmed: { bg: '#065f46', text: '#d1fae5' } + } + } else { + return { + // Background colors + background: '#ffffff', + backgroundAlt: '#f9fafb', + + // Text colors + textPrimary: '#111827', + textSecondary: '#374151', + textMuted: '#6b7280', + + // Border colors + border: '#e5e7eb', + borderLight: '#f3f4f6', + + // Accent colors + accent: '#3b82f6', + accentHover: '#2563eb', + + // Badge colors + badgeSuggested: { bg: '#fef3c7', text: '#92400e' }, + badgeConfirmed: { bg: '#d1fae5', text: '#065f46' } + } + } +} + +/** + * Get base popup styles as inline CSS + * @param {string} theme - 'dark' or 'light' + * @returns {string} CSS string for inline styles + */ +export function getPopupBaseStyles(theme = getCurrentTheme()) { + const colors = getThemeColors(theme) + + return ` + font-family: system-ui, -apple-system, sans-serif; + background-color: ${colors.background}; + color: ${colors.textPrimary}; + ` +} + +/** + * Get popup container class with theme + * @param {string} baseClass - Base CSS class name + * @param {string} theme - 'dark' or 'light' + * @returns {string} Class name with theme + */ +export function getPopupClass(baseClass, theme = getCurrentTheme()) { + return `${baseClass} ${baseClass}--${theme}` +} + +/** + * Listen for theme changes and update popup if needed + * @param {Function} callback - Callback to execute on theme change + * @returns {Function} Cleanup function to remove listener + */ +export function onThemeChange(callback) { + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'attributes' && + (mutation.attributeName === 'data-theme' || + mutation.attributeName === 'class')) { + callback(getCurrentTheme()) + } + }) + }) + + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-theme', 'class'] + }) + + return () => observer.disconnect() +}