From eae4dafd1214ea72cdaa7880291fd8b77e066bc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:44:48 +0000 Subject: [PATCH 1/3] Initial plan From 09da5a4a97ecb1bc10b91a7323352cee0759b2da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:54:56 +0000 Subject: [PATCH 2/3] Implement dark/light mode toggle with WHO color palette Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- input/includes/dmn.css | 165 +++++++++++- input/scripts/includes/dmn.css | 165 +++++++++++- local-template/DARK_MODE.md | 72 +++++ .../package/content/assets/css/dmn.css | 249 ++++++++++++++---- .../package/content/assets/js/theme-toggle.js | 136 ++++++++++ .../includes/_append.fragment-script.html | 1 + 6 files changed, 725 insertions(+), 63 deletions(-) create mode 100644 local-template/DARK_MODE.md create mode 100644 local-template/package/content/assets/js/theme-toggle.js create mode 100644 local-template/package/includes/_append.fragment-script.html diff --git a/input/includes/dmn.css b/input/includes/dmn.css index ac1e983182..7717d147f5 100644 --- a/input/includes/dmn.css +++ b/input/includes/dmn.css @@ -3,6 +3,119 @@ --who-blue: #0093d0; --who-header: #f0f9fc; --who-table-border: #007ab7; + + /* Light mode colors (default) */ + --bg-color: #ffffff; + --text-color: #222222; + --border-color: #007ab7; + --table-stripe: #f7fafc; + --row-label-bg: #f6fafd; + --row-label-text: #555555; + --input-bg: #e5f4fa; + --output-bg: #dff7ea; + --annotation-bg: #fffbe5; + --link-color: #0066cc; + --link-hover-color: #004499; +} + +/* Dark mode colors */ +[data-theme="dark"] { + --who-blue: #0093d0; + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; +} + +/* Apply dark mode based on system preference if no explicit theme is set */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; + } +} + +/* General dark mode styles for common elements */ +body { + background-color: var(--bg-color); + color: var(--text-color); + transition: background-color 0.3s ease, color 0.3s ease; +} + +a { + color: var(--link-color); + transition: color 0.3s ease; +} + +a:hover { + color: var(--link-hover-color); +} + +/* Apply to common Bootstrap/FHIR IG elements */ +.table { + background-color: var(--bg-color); + color: var(--text-color); +} + +.table th, +.table td { + border-color: var(--border-color); +} + +.table-bordered { + border-color: var(--border-color); +} + +.thead-dark th { + background-color: var(--who-blue); + border-color: var(--border-color); +} + +/* Code blocks and pre elements */ +pre, code { + background-color: var(--table-stripe); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Cards and panels */ +.card { + background-color: var(--bg-color); + border-color: var(--border-color); +} + +.card-header { + background-color: var(--who-header); + border-color: var(--border-color); +} + +/* Navbar elements */ +.navbar { + background-color: var(--who-header) !important; +} + +.navbar-light .navbar-nav .nav-link { + color: var(--text-color); } table.decision { @@ -10,6 +123,8 @@ table.decision { width: 100%; margin: 2em 0; font-family: Arial, sans-serif; + background-color: var(--bg-color); + color: var(--text-color); } table.decision tr.decision-header { @@ -20,7 +135,7 @@ table.decision tr.decision-header { table.decision tr.io-row { background: var(--who-header); - color: #222; + color: var(--text-color); font-weight: 600; } @@ -31,22 +146,58 @@ table.decision td, table.decision th { .row-label { font-weight: bold; - background: #f6fafd; - color: #555; + background: var(--row-label-bg); + color: var(--row-label-text); } .input, .inputEntry { - background: #e5f4fa; + background: var(--input-bg); + color: var(--text-color); } .output, .outputEntry { - background: #dff7ea; + background: var(--output-bg); + color: var(--text-color); } .annotation, .annotationEntry { - background: #fffbe5; + background: var(--annotation-bg); + color: var(--text-color); } tr.rule:nth-child(even) { - background: #f7fafc; + background: var(--table-stripe); +} + +/* Dark mode image handling - set white background for images that may be transparent */ +[data-theme="dark"] img, +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) img { + background-color: white; + border-radius: 4px; + padding: 2px; + } +} + +/* Dark mode toggle button styling */ +.theme-toggle { + background: none; + border: 1px solid var(--border-color, #007ab7); + border-radius: 4px; + color: var(--text-color, #222); + cursor: pointer; + font-size: 14px; + margin-left: 10px; + padding: 5px 10px; + transition: all 0.3s ease; +} + +.theme-toggle:hover { + background-color: var(--who-blue, #0093d0); + color: white; +} + +.theme-toggle:focus { + outline: 2px solid var(--who-blue, #0093d0); + outline-offset: 2px; } \ No newline at end of file diff --git a/input/scripts/includes/dmn.css b/input/scripts/includes/dmn.css index ac1e983182..7717d147f5 100644 --- a/input/scripts/includes/dmn.css +++ b/input/scripts/includes/dmn.css @@ -3,6 +3,119 @@ --who-blue: #0093d0; --who-header: #f0f9fc; --who-table-border: #007ab7; + + /* Light mode colors (default) */ + --bg-color: #ffffff; + --text-color: #222222; + --border-color: #007ab7; + --table-stripe: #f7fafc; + --row-label-bg: #f6fafd; + --row-label-text: #555555; + --input-bg: #e5f4fa; + --output-bg: #dff7ea; + --annotation-bg: #fffbe5; + --link-color: #0066cc; + --link-hover-color: #004499; +} + +/* Dark mode colors */ +[data-theme="dark"] { + --who-blue: #0093d0; + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; +} + +/* Apply dark mode based on system preference if no explicit theme is set */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; + } +} + +/* General dark mode styles for common elements */ +body { + background-color: var(--bg-color); + color: var(--text-color); + transition: background-color 0.3s ease, color 0.3s ease; +} + +a { + color: var(--link-color); + transition: color 0.3s ease; +} + +a:hover { + color: var(--link-hover-color); +} + +/* Apply to common Bootstrap/FHIR IG elements */ +.table { + background-color: var(--bg-color); + color: var(--text-color); +} + +.table th, +.table td { + border-color: var(--border-color); +} + +.table-bordered { + border-color: var(--border-color); +} + +.thead-dark th { + background-color: var(--who-blue); + border-color: var(--border-color); +} + +/* Code blocks and pre elements */ +pre, code { + background-color: var(--table-stripe); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Cards and panels */ +.card { + background-color: var(--bg-color); + border-color: var(--border-color); +} + +.card-header { + background-color: var(--who-header); + border-color: var(--border-color); +} + +/* Navbar elements */ +.navbar { + background-color: var(--who-header) !important; +} + +.navbar-light .navbar-nav .nav-link { + color: var(--text-color); } table.decision { @@ -10,6 +123,8 @@ table.decision { width: 100%; margin: 2em 0; font-family: Arial, sans-serif; + background-color: var(--bg-color); + color: var(--text-color); } table.decision tr.decision-header { @@ -20,7 +135,7 @@ table.decision tr.decision-header { table.decision tr.io-row { background: var(--who-header); - color: #222; + color: var(--text-color); font-weight: 600; } @@ -31,22 +146,58 @@ table.decision td, table.decision th { .row-label { font-weight: bold; - background: #f6fafd; - color: #555; + background: var(--row-label-bg); + color: var(--row-label-text); } .input, .inputEntry { - background: #e5f4fa; + background: var(--input-bg); + color: var(--text-color); } .output, .outputEntry { - background: #dff7ea; + background: var(--output-bg); + color: var(--text-color); } .annotation, .annotationEntry { - background: #fffbe5; + background: var(--annotation-bg); + color: var(--text-color); } tr.rule:nth-child(even) { - background: #f7fafc; + background: var(--table-stripe); +} + +/* Dark mode image handling - set white background for images that may be transparent */ +[data-theme="dark"] img, +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) img { + background-color: white; + border-radius: 4px; + padding: 2px; + } +} + +/* Dark mode toggle button styling */ +.theme-toggle { + background: none; + border: 1px solid var(--border-color, #007ab7); + border-radius: 4px; + color: var(--text-color, #222); + cursor: pointer; + font-size: 14px; + margin-left: 10px; + padding: 5px 10px; + transition: all 0.3s ease; +} + +.theme-toggle:hover { + background-color: var(--who-blue, #0093d0); + color: white; +} + +.theme-toggle:focus { + outline: 2px solid var(--who-blue, #0093d0); + outline-offset: 2px; } \ No newline at end of file diff --git a/local-template/DARK_MODE.md b/local-template/DARK_MODE.md new file mode 100644 index 0000000000..6830ea0da5 --- /dev/null +++ b/local-template/DARK_MODE.md @@ -0,0 +1,72 @@ +# Dark Mode Implementation + +This document describes the dark/light mode toggle feature implemented for the SMART Guidelines base template. + +## Features + +- **Automatic Detection**: Respects user's system preference (`prefers-color-scheme`) +- **Manual Toggle**: Button in the top-right corner to manually switch themes +- **Persistence**: Theme choice is saved in localStorage and persists across page reloads +- **WHO Color Palette**: Maintains WHO branding while providing optimal contrast for dark mode +- **Transparent Image Handling**: Automatically adds white background to images in dark mode for better visibility + +## Implementation + +### CSS Custom Properties + +The implementation uses CSS custom properties (variables) to create a flexible theming system: + +```css +:root { + /* Light mode colors (default) */ + --bg-color: #ffffff; + --text-color: #222222; + --who-blue: #0093d0; + /* ... more variables */ +} + +[data-theme="dark"] { + /* Dark mode colors */ + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + /* ... dark mode overrides */ +} +``` + +### JavaScript Toggle + +The theme toggle is implemented with vanilla JavaScript that: + +1. Detects system preference on page load +2. Applies saved theme from localStorage if available +3. Creates and positions the toggle button dynamically +4. Handles theme switching and persistence + +### Template Integration + +The feature is integrated into the FHIR IG template system via: + +- `_append.fragment-css.html`: Includes the dark mode CSS +- `_append.fragment-script.html`: Includes the theme toggle JavaScript +- CSS files in multiple locations for compatibility: + - `local-template/package/content/assets/css/dmn.css` + - `input/includes/dmn.css` + - `input/scripts/includes/dmn.css` + +## Usage + +The dark mode toggle appears automatically in the top-right corner of pages. Users can: + +1. Click the toggle button to switch between light and dark modes +2. The system will remember their choice +3. If no choice is made, the system preference is used + +## Customization + +To customize colors, modify the CSS custom properties in the `:root` and `[data-theme="dark"]` selectors in the CSS files. + +## Browser Support + +- Modern browsers with CSS custom properties support +- Graceful fallback to light mode for older browsers +- LocalStorage support for theme persistence \ No newline at end of file diff --git a/local-template/package/content/assets/css/dmn.css b/local-template/package/content/assets/css/dmn.css index 623a2286ef..b875d11d6d 100644 --- a/local-template/package/content/assets/css/dmn.css +++ b/local-template/package/content/assets/css/dmn.css @@ -1,52 +1,203 @@ -/* WHO color palette: Example blue */ -:root { - --who-blue: #0093d0; - --who-header: #f0f9fc; - --who-table-border: #007ab7; +/* WHO color palette: Example blue */ +:root { + --who-blue: #0093d0; + --who-header: #f0f9fc; + --who-table-border: #007ab7; + + /* Light mode colors (default) */ + --bg-color: #ffffff; + --text-color: #222222; + --border-color: #007ab7; + --table-stripe: #f7fafc; + --row-label-bg: #f6fafd; + --row-label-text: #555555; + --input-bg: #e5f4fa; + --output-bg: #dff7ea; + --annotation-bg: #fffbe5; + --link-color: #0066cc; + --link-hover-color: #004499; +} + +/* Dark mode colors */ +[data-theme="dark"] { + --who-blue: #0093d0; + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; +} + +/* Apply dark mode based on system preference if no explicit theme is set */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --who-header: #1a2332; + --who-table-border: #4a90a4; + --bg-color: #1a1a1a; + --text-color: #e0e0e0; + --border-color: #4a90a4; + --table-stripe: #2a2a2a; + --row-label-bg: #2d3748; + --row-label-text: #cbd5e0; + --input-bg: #2c5282; + --output-bg: #2f855a; + --annotation-bg: #744210; + --link-color: #66b3ff; + --link-hover-color: #99ccff; + } +} + +/* General dark mode styles for common elements */ +body { + background-color: var(--bg-color); + color: var(--text-color); + transition: background-color 0.3s ease, color 0.3s ease; +} + +a { + color: var(--link-color); + transition: color 0.3s ease; +} + +a:hover { + color: var(--link-hover-color); +} + +/* Apply to common Bootstrap/FHIR IG elements */ +.table { + background-color: var(--bg-color); + color: var(--text-color); +} + +.table th, +.table td { + border-color: var(--border-color); +} + +.table-bordered { + border-color: var(--border-color); +} + +.thead-dark th { + background-color: var(--who-blue); + border-color: var(--border-color); +} + +/* Code blocks and pre elements */ +pre, code { + background-color: var(--table-stripe); + color: var(--text-color); + border-color: var(--border-color); +} + +/* Cards and panels */ +.card { + background-color: var(--bg-color); + border-color: var(--border-color); +} + +.card-header { + background-color: var(--who-header); + border-color: var(--border-color); +} + +/* Navbar elements */ +.navbar { + background-color: var(--who-header) !important; +} + +.navbar-light .navbar-nav .nav-link { + color: var(--text-color); } -table.decision { - border-collapse: collapse; - width: 100%; - margin: 2em 0; - font-family: Arial, sans-serif; -} - -table.decision tr.decision-header { - background: var(--who-blue); - color: white; - font-weight: bold; -} - -table.decision tr.io-row { - background: var(--who-header); - color: #222; - font-weight: 600; -} - -table.decision td, table.decision th { - border: 1px solid var(--who-table-border); - padding: 0.5em 1em; -} - -.row-label { - font-weight: bold; - background: #f6fafd; - color: #555; -} - -.input, .inputEntry { - background: #e5f4fa; -} - -.output, .outputEntry { - background: #dff7ea; -} - -.annotation, .annotationEntry { - background: #fffbe5; -} - -tr.rule:nth-child(even) { - background: #f7fafc; +table.decision { + border-collapse: collapse; + width: 100%; + margin: 2em 0; + font-family: Arial, sans-serif; + background-color: var(--bg-color); + color: var(--text-color); +} + +table.decision tr.decision-header { + background: var(--who-blue); + color: white; + font-weight: bold; +} + +table.decision tr.io-row { + background: var(--who-header); + color: var(--text-color); + font-weight: 600; +} + +table.decision td, table.decision th { + border: 1px solid var(--who-table-border); + padding: 0.5em 1em; +} + +.row-label { + font-weight: bold; + background: var(--row-label-bg); + color: var(--row-label-text); +} + +.input, .inputEntry { + background: var(--input-bg); + color: var(--text-color); +} + +.output, .outputEntry { + background: var(--output-bg); + color: var(--text-color); +} + +.annotation, .annotationEntry { + background: var(--annotation-bg); + color: var(--text-color); +} + +tr.rule:nth-child(even) { + background: var(--table-stripe); +} + +/* Dark mode image handling - set white background for images that may be transparent */ +[data-theme="dark"] img, +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) img { + background-color: white; + border-radius: 4px; + padding: 2px; + } +} + +/* Dark mode toggle button styling */ +.theme-toggle { + background: none; + border: 1px solid var(--border-color, #007ab7); + border-radius: 4px; + color: var(--text-color, #222); + cursor: pointer; + font-size: 14px; + margin-left: 10px; + padding: 5px 10px; + transition: all 0.3s ease; +} + +.theme-toggle:hover { + background-color: var(--who-blue, #0093d0); + color: white; +} + +.theme-toggle:focus { + outline: 2px solid var(--who-blue, #0093d0); + outline-offset: 2px; } \ No newline at end of file diff --git a/local-template/package/content/assets/js/theme-toggle.js b/local-template/package/content/assets/js/theme-toggle.js new file mode 100644 index 0000000000..a4dfdc09a1 --- /dev/null +++ b/local-template/package/content/assets/js/theme-toggle.js @@ -0,0 +1,136 @@ +/** + * Dark/Light mode theme toggle functionality + * Handles theme switching and persistence across page loads + */ +(function() { + 'use strict'; + + // Get current theme from localStorage or detect system preference + function getCurrentTheme() { + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) { + return savedTheme; + } + + // Check system preference + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 'dark'; + } + + return 'light'; + } + + // Apply theme to the document + function applyTheme(theme) { + if (theme === 'dark') { + document.documentElement.setAttribute('data-theme', 'dark'); + } else { + document.documentElement.removeAttribute('data-theme'); + } + } + + // Toggle between light and dark themes + function toggleTheme() { + const currentTheme = getCurrentTheme(); + const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; + + localStorage.setItem('theme', newTheme); + applyTheme(newTheme); + updateToggleButton(); + } + + // Update the toggle button text/icon + function updateToggleButton() { + const button = document.getElementById('theme-toggle'); + if (button) { + const currentTheme = getCurrentTheme(); + button.textContent = currentTheme === 'dark' ? '☀️ Light' : '🌙 Dark'; + button.setAttribute('aria-label', `Switch to ${currentTheme === 'dark' ? 'light' : 'dark'} mode`); + } + } + + // Create and insert the theme toggle button + function createToggleButton() { + const button = document.createElement('button'); + button.id = 'theme-toggle'; + button.className = 'theme-toggle'; + button.type = 'button'; + button.addEventListener('click', toggleTheme); + + // Try to find a good location for the button (near language selector or in header) + const possibleLocations = [ + '.navbar-nav', + '.navbar', + '#segment-header', + '.segment-header', + 'header', + '.container-fluid', + 'body' + ]; + + let inserted = false; + for (const selector of possibleLocations) { + const container = document.querySelector(selector); + if (container && !inserted) { + // If it's a nav container, append to it + if (selector.includes('nav')) { + container.appendChild(button); + } else { + // For other containers, create a wrapper div and position it appropriately + const wrapper = document.createElement('div'); + wrapper.style.cssText = 'position: absolute; top: 10px; right: 10px; z-index: 1000;'; + wrapper.appendChild(button); + + if (container.style.position === '' || container.style.position === 'static') { + container.style.position = 'relative'; + } + container.appendChild(wrapper); + } + inserted = true; + break; + } + } + + // Fallback: create a floating button if no suitable container found + if (!inserted) { + const wrapper = document.createElement('div'); + wrapper.style.cssText = 'position: fixed; top: 10px; right: 10px; z-index: 9999;'; + wrapper.appendChild(button); + document.body.appendChild(wrapper); + } + + return button; + } + + // Initialize theme functionality + function initTheme() { + // Apply the current theme immediately + const currentTheme = getCurrentTheme(); + applyTheme(currentTheme); + + // Create the toggle button when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + createToggleButton(); + updateToggleButton(); + }); + } else { + createToggleButton(); + updateToggleButton(); + } + + // Listen for system theme changes + if (window.matchMedia) { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { + // Only update if user hasn't explicitly set a theme + if (!localStorage.getItem('theme')) { + applyTheme(e.matches ? 'dark' : 'light'); + updateToggleButton(); + } + }); + } + } + + // Start initialization + initTheme(); +})(); \ No newline at end of file diff --git a/local-template/package/includes/_append.fragment-script.html b/local-template/package/includes/_append.fragment-script.html new file mode 100644 index 0000000000..5ec637cdc2 --- /dev/null +++ b/local-template/package/includes/_append.fragment-script.html @@ -0,0 +1 @@ + \ No newline at end of file From 844d6750e1284703f16faea77526fdf0f5ee84f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:42:50 +0000 Subject: [PATCH 3/3] Refactor implementation to complement WHO template dark mode Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- input/includes/dmn.css | 203 ----------------- input/scripts/includes/dmn.css | 203 ----------------- local-template/DARK_MODE.md | 72 ------ local-template/DMN_STYLING.md | 68 ++++++ .../package/content/assets/css/dmn.css | 205 ++++-------------- .../package/content/assets/js/theme-toggle.js | 136 ------------ .../includes/_append.fragment-script.html | 1 - 7 files changed, 115 insertions(+), 773 deletions(-) delete mode 100644 input/includes/dmn.css delete mode 100644 input/scripts/includes/dmn.css delete mode 100644 local-template/DARK_MODE.md create mode 100644 local-template/DMN_STYLING.md delete mode 100644 local-template/package/content/assets/js/theme-toggle.js delete mode 100644 local-template/package/includes/_append.fragment-script.html diff --git a/input/includes/dmn.css b/input/includes/dmn.css deleted file mode 100644 index 7717d147f5..0000000000 --- a/input/includes/dmn.css +++ /dev/null @@ -1,203 +0,0 @@ -/* WHO color palette: Example blue */ -:root { - --who-blue: #0093d0; - --who-header: #f0f9fc; - --who-table-border: #007ab7; - - /* Light mode colors (default) */ - --bg-color: #ffffff; - --text-color: #222222; - --border-color: #007ab7; - --table-stripe: #f7fafc; - --row-label-bg: #f6fafd; - --row-label-text: #555555; - --input-bg: #e5f4fa; - --output-bg: #dff7ea; - --annotation-bg: #fffbe5; - --link-color: #0066cc; - --link-hover-color: #004499; -} - -/* Dark mode colors */ -[data-theme="dark"] { - --who-blue: #0093d0; - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; -} - -/* Apply dark mode based on system preference if no explicit theme is set */ -@media (prefers-color-scheme: dark) { - :root:not([data-theme]) { - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; - } -} - -/* General dark mode styles for common elements */ -body { - background-color: var(--bg-color); - color: var(--text-color); - transition: background-color 0.3s ease, color 0.3s ease; -} - -a { - color: var(--link-color); - transition: color 0.3s ease; -} - -a:hover { - color: var(--link-hover-color); -} - -/* Apply to common Bootstrap/FHIR IG elements */ -.table { - background-color: var(--bg-color); - color: var(--text-color); -} - -.table th, -.table td { - border-color: var(--border-color); -} - -.table-bordered { - border-color: var(--border-color); -} - -.thead-dark th { - background-color: var(--who-blue); - border-color: var(--border-color); -} - -/* Code blocks and pre elements */ -pre, code { - background-color: var(--table-stripe); - color: var(--text-color); - border-color: var(--border-color); -} - -/* Cards and panels */ -.card { - background-color: var(--bg-color); - border-color: var(--border-color); -} - -.card-header { - background-color: var(--who-header); - border-color: var(--border-color); -} - -/* Navbar elements */ -.navbar { - background-color: var(--who-header) !important; -} - -.navbar-light .navbar-nav .nav-link { - color: var(--text-color); -} - -table.decision { - border-collapse: collapse; - width: 100%; - margin: 2em 0; - font-family: Arial, sans-serif; - background-color: var(--bg-color); - color: var(--text-color); -} - -table.decision tr.decision-header { - background: var(--who-blue); - color: white; - font-weight: bold; -} - -table.decision tr.io-row { - background: var(--who-header); - color: var(--text-color); - font-weight: 600; -} - -table.decision td, table.decision th { - border: 1px solid var(--who-table-border); - padding: 0.5em 1em; -} - -.row-label { - font-weight: bold; - background: var(--row-label-bg); - color: var(--row-label-text); -} - -.input, .inputEntry { - background: var(--input-bg); - color: var(--text-color); -} - -.output, .outputEntry { - background: var(--output-bg); - color: var(--text-color); -} - -.annotation, .annotationEntry { - background: var(--annotation-bg); - color: var(--text-color); -} - -tr.rule:nth-child(even) { - background: var(--table-stripe); -} - -/* Dark mode image handling - set white background for images that may be transparent */ -[data-theme="dark"] img, -@media (prefers-color-scheme: dark) { - :root:not([data-theme]) img { - background-color: white; - border-radius: 4px; - padding: 2px; - } -} - -/* Dark mode toggle button styling */ -.theme-toggle { - background: none; - border: 1px solid var(--border-color, #007ab7); - border-radius: 4px; - color: var(--text-color, #222); - cursor: pointer; - font-size: 14px; - margin-left: 10px; - padding: 5px 10px; - transition: all 0.3s ease; -} - -.theme-toggle:hover { - background-color: var(--who-blue, #0093d0); - color: white; -} - -.theme-toggle:focus { - outline: 2px solid var(--who-blue, #0093d0); - outline-offset: 2px; -} \ No newline at end of file diff --git a/input/scripts/includes/dmn.css b/input/scripts/includes/dmn.css deleted file mode 100644 index 7717d147f5..0000000000 --- a/input/scripts/includes/dmn.css +++ /dev/null @@ -1,203 +0,0 @@ -/* WHO color palette: Example blue */ -:root { - --who-blue: #0093d0; - --who-header: #f0f9fc; - --who-table-border: #007ab7; - - /* Light mode colors (default) */ - --bg-color: #ffffff; - --text-color: #222222; - --border-color: #007ab7; - --table-stripe: #f7fafc; - --row-label-bg: #f6fafd; - --row-label-text: #555555; - --input-bg: #e5f4fa; - --output-bg: #dff7ea; - --annotation-bg: #fffbe5; - --link-color: #0066cc; - --link-hover-color: #004499; -} - -/* Dark mode colors */ -[data-theme="dark"] { - --who-blue: #0093d0; - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; -} - -/* Apply dark mode based on system preference if no explicit theme is set */ -@media (prefers-color-scheme: dark) { - :root:not([data-theme]) { - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; - } -} - -/* General dark mode styles for common elements */ -body { - background-color: var(--bg-color); - color: var(--text-color); - transition: background-color 0.3s ease, color 0.3s ease; -} - -a { - color: var(--link-color); - transition: color 0.3s ease; -} - -a:hover { - color: var(--link-hover-color); -} - -/* Apply to common Bootstrap/FHIR IG elements */ -.table { - background-color: var(--bg-color); - color: var(--text-color); -} - -.table th, -.table td { - border-color: var(--border-color); -} - -.table-bordered { - border-color: var(--border-color); -} - -.thead-dark th { - background-color: var(--who-blue); - border-color: var(--border-color); -} - -/* Code blocks and pre elements */ -pre, code { - background-color: var(--table-stripe); - color: var(--text-color); - border-color: var(--border-color); -} - -/* Cards and panels */ -.card { - background-color: var(--bg-color); - border-color: var(--border-color); -} - -.card-header { - background-color: var(--who-header); - border-color: var(--border-color); -} - -/* Navbar elements */ -.navbar { - background-color: var(--who-header) !important; -} - -.navbar-light .navbar-nav .nav-link { - color: var(--text-color); -} - -table.decision { - border-collapse: collapse; - width: 100%; - margin: 2em 0; - font-family: Arial, sans-serif; - background-color: var(--bg-color); - color: var(--text-color); -} - -table.decision tr.decision-header { - background: var(--who-blue); - color: white; - font-weight: bold; -} - -table.decision tr.io-row { - background: var(--who-header); - color: var(--text-color); - font-weight: 600; -} - -table.decision td, table.decision th { - border: 1px solid var(--who-table-border); - padding: 0.5em 1em; -} - -.row-label { - font-weight: bold; - background: var(--row-label-bg); - color: var(--row-label-text); -} - -.input, .inputEntry { - background: var(--input-bg); - color: var(--text-color); -} - -.output, .outputEntry { - background: var(--output-bg); - color: var(--text-color); -} - -.annotation, .annotationEntry { - background: var(--annotation-bg); - color: var(--text-color); -} - -tr.rule:nth-child(even) { - background: var(--table-stripe); -} - -/* Dark mode image handling - set white background for images that may be transparent */ -[data-theme="dark"] img, -@media (prefers-color-scheme: dark) { - :root:not([data-theme]) img { - background-color: white; - border-radius: 4px; - padding: 2px; - } -} - -/* Dark mode toggle button styling */ -.theme-toggle { - background: none; - border: 1px solid var(--border-color, #007ab7); - border-radius: 4px; - color: var(--text-color, #222); - cursor: pointer; - font-size: 14px; - margin-left: 10px; - padding: 5px 10px; - transition: all 0.3s ease; -} - -.theme-toggle:hover { - background-color: var(--who-blue, #0093d0); - color: white; -} - -.theme-toggle:focus { - outline: 2px solid var(--who-blue, #0093d0); - outline-offset: 2px; -} \ No newline at end of file diff --git a/local-template/DARK_MODE.md b/local-template/DARK_MODE.md deleted file mode 100644 index 6830ea0da5..0000000000 --- a/local-template/DARK_MODE.md +++ /dev/null @@ -1,72 +0,0 @@ -# Dark Mode Implementation - -This document describes the dark/light mode toggle feature implemented for the SMART Guidelines base template. - -## Features - -- **Automatic Detection**: Respects user's system preference (`prefers-color-scheme`) -- **Manual Toggle**: Button in the top-right corner to manually switch themes -- **Persistence**: Theme choice is saved in localStorage and persists across page reloads -- **WHO Color Palette**: Maintains WHO branding while providing optimal contrast for dark mode -- **Transparent Image Handling**: Automatically adds white background to images in dark mode for better visibility - -## Implementation - -### CSS Custom Properties - -The implementation uses CSS custom properties (variables) to create a flexible theming system: - -```css -:root { - /* Light mode colors (default) */ - --bg-color: #ffffff; - --text-color: #222222; - --who-blue: #0093d0; - /* ... more variables */ -} - -[data-theme="dark"] { - /* Dark mode colors */ - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - /* ... dark mode overrides */ -} -``` - -### JavaScript Toggle - -The theme toggle is implemented with vanilla JavaScript that: - -1. Detects system preference on page load -2. Applies saved theme from localStorage if available -3. Creates and positions the toggle button dynamically -4. Handles theme switching and persistence - -### Template Integration - -The feature is integrated into the FHIR IG template system via: - -- `_append.fragment-css.html`: Includes the dark mode CSS -- `_append.fragment-script.html`: Includes the theme toggle JavaScript -- CSS files in multiple locations for compatibility: - - `local-template/package/content/assets/css/dmn.css` - - `input/includes/dmn.css` - - `input/scripts/includes/dmn.css` - -## Usage - -The dark mode toggle appears automatically in the top-right corner of pages. Users can: - -1. Click the toggle button to switch between light and dark modes -2. The system will remember their choice -3. If no choice is made, the system preference is used - -## Customization - -To customize colors, modify the CSS custom properties in the `:root` and `[data-theme="dark"]` selectors in the CSS files. - -## Browser Support - -- Modern browsers with CSS custom properties support -- Graceful fallback to light mode for older browsers -- LocalStorage support for theme persistence \ No newline at end of file diff --git a/local-template/DMN_STYLING.md b/local-template/DMN_STYLING.md new file mode 100644 index 0000000000..ffbed5abf3 --- /dev/null +++ b/local-template/DMN_STYLING.md @@ -0,0 +1,68 @@ +# Decision Table Enhancements for SMART Guidelines + +This local template enhancement provides **decision table specific styling** that complements the WHO FHIR IG template's dark/light mode functionality. + +## Integration with WHO Template + +The WHO FHIR IG template (smart-ig-template) now includes comprehensive dark/light mode theming with: +- Theme toggle button in the navbar +- System preference detection +- Theme persistence +- General template theming + +This enhancement focuses specifically on **DMN decision table styling** with WHO color palette integration. + +## Features + +### 🎨 WHO Color Palette for Decision Tables +- **Input columns**: Light blue (`#e5f4fa` / `#2c5282`) +- **Output columns**: Light green (`#dff7ea` / `#2f855a`) +- **Annotation columns**: Light yellow (`#fffbe5` / `#744210`) +- **Header rows**: WHO blue (`#0093d0`) +- **Border colors**: WHO blue tones + +### 🌓 Automatic Theme Integration +- Respects the WHO template's theme system +- Uses `[data-theme="dark"]` selector for consistency +- Supports system preference via `@media (prefers-color-scheme: dark)` + +### 📊 Decision Table Enhancement +- Enhanced styling for `.decision` tables +- Color-coded columns for better readability +- Proper contrast ratios in both themes +- WHO branding compliance + +## Usage + +The enhancement automatically applies to decision tables when using these CSS classes: + +```html + + + + + + + + + + + + + +
RuleInput 1OutputNotes
1ConditionActionComment
+``` + +## Implementation + +The enhancement is implemented through: +- `local-template/package/content/assets/css/dmn.css` - Decision table specific styling +- `local-template/package/includes/_append.fragment-css.html` - CSS inclusion + +No JavaScript is included as theme functionality is provided by the WHO template. + +## Relationship to WHO Template + +This enhancement is designed to work seamlessly with the WHO template's dark mode implementation. When the WHO template is updated with dark mode support, this enhancement will automatically integrate without conflicts. + +**Note**: Once the WHO template includes comprehensive dark mode support, only the decision table specific enhancements in this local template will be necessary. \ No newline at end of file diff --git a/local-template/package/content/assets/css/dmn.css b/local-template/package/content/assets/css/dmn.css index b875d11d6d..31ef1605ee 100644 --- a/local-template/package/content/assets/css/dmn.css +++ b/local-template/package/content/assets/css/dmn.css @@ -1,203 +1,92 @@ -/* WHO color palette: Example blue */ -:root { - --who-blue: #0093d0; - --who-header: #f0f9fc; - --who-table-border: #007ab7; - - /* Light mode colors (default) */ - --bg-color: #ffffff; - --text-color: #222222; - --border-color: #007ab7; - --table-stripe: #f7fafc; - --row-label-bg: #f6fafd; - --row-label-text: #555555; - --input-bg: #e5f4fa; - --output-bg: #dff7ea; - --annotation-bg: #fffbe5; - --link-color: #0066cc; - --link-hover-color: #004499; -} +/* + * Decision Table Enhancements for SMART Guidelines + * Complements the WHO template dark/light mode theming + * Focuses on DMN-specific styling and WHO color integration + */ -/* Dark mode colors */ +/* Decision table specific WHO color palette */ +:root { + --dmn-who-blue: #0093d0; + --dmn-input-bg: #e5f4fa; + --dmn-output-bg: #dff7ea; + --dmn-annotation-bg: #fffbe5; + --dmn-header-bg: #f0f9fc; + --dmn-border-color: #007ab7; + --dmn-row-label-bg: #f6fafd; + --dmn-row-label-text: #555555; + --dmn-table-stripe: #f9f9f9; +} + +/* Decision table dark mode colors */ [data-theme="dark"] { - --who-blue: #0093d0; - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; + --dmn-input-bg: #2c5282; + --dmn-output-bg: #2f855a; + --dmn-annotation-bg: #744210; + --dmn-header-bg: #1a2332; + --dmn-border-color: #4a90a4; + --dmn-row-label-bg: #2d3748; + --dmn-row-label-text: #cbd5e0; + --dmn-table-stripe: #2a2a2a; } /* Apply dark mode based on system preference if no explicit theme is set */ @media (prefers-color-scheme: dark) { :root:not([data-theme]) { - --who-header: #1a2332; - --who-table-border: #4a90a4; - --bg-color: #1a1a1a; - --text-color: #e0e0e0; - --border-color: #4a90a4; - --table-stripe: #2a2a2a; - --row-label-bg: #2d3748; - --row-label-text: #cbd5e0; - --input-bg: #2c5282; - --output-bg: #2f855a; - --annotation-bg: #744210; - --link-color: #66b3ff; - --link-hover-color: #99ccff; + --dmn-input-bg: #2c5282; + --dmn-output-bg: #2f855a; + --dmn-annotation-bg: #744210; + --dmn-header-bg: #1a2332; + --dmn-border-color: #4a90a4; + --dmn-row-label-bg: #2d3748; + --dmn-row-label-text: #cbd5e0; + --dmn-table-stripe: #2a2a2a; } } -/* General dark mode styles for common elements */ -body { - background-color: var(--bg-color); - color: var(--text-color); - transition: background-color 0.3s ease, color 0.3s ease; -} - -a { - color: var(--link-color); - transition: color 0.3s ease; -} - -a:hover { - color: var(--link-hover-color); -} - -/* Apply to common Bootstrap/FHIR IG elements */ -.table { - background-color: var(--bg-color); - color: var(--text-color); -} - -.table th, -.table td { - border-color: var(--border-color); -} - -.table-bordered { - border-color: var(--border-color); -} - -.thead-dark th { - background-color: var(--who-blue); - border-color: var(--border-color); -} - -/* Code blocks and pre elements */ -pre, code { - background-color: var(--table-stripe); - color: var(--text-color); - border-color: var(--border-color); -} - -/* Cards and panels */ -.card { - background-color: var(--bg-color); - border-color: var(--border-color); -} - -.card-header { - background-color: var(--who-header); - border-color: var(--border-color); -} - -/* Navbar elements */ -.navbar { - background-color: var(--who-header) !important; -} - -.navbar-light .navbar-nav .nav-link { - color: var(--text-color); -} - +/* DMN Decision Table Specific Styling */ table.decision { border-collapse: collapse; width: 100%; margin: 2em 0; font-family: Arial, sans-serif; - background-color: var(--bg-color); - color: var(--text-color); } table.decision tr.decision-header { - background: var(--who-blue); + background: var(--dmn-who-blue); color: white; font-weight: bold; } table.decision tr.io-row { - background: var(--who-header); - color: var(--text-color); + background: var(--dmn-header-bg); font-weight: 600; } table.decision td, table.decision th { - border: 1px solid var(--who-table-border); + border: 1px solid var(--dmn-border-color); padding: 0.5em 1em; } +/* Decision table cell types with WHO color coding */ .row-label { font-weight: bold; - background: var(--row-label-bg); - color: var(--row-label-text); + background: var(--dmn-row-label-bg); + color: var(--dmn-row-label-text); } .input, .inputEntry { - background: var(--input-bg); - color: var(--text-color); + background: var(--dmn-input-bg); } .output, .outputEntry { - background: var(--output-bg); - color: var(--text-color); + background: var(--dmn-output-bg); } .annotation, .annotationEntry { - background: var(--annotation-bg); - color: var(--text-color); + background: var(--dmn-annotation-bg); } +/* Alternating row styling for decision tables */ tr.rule:nth-child(even) { - background: var(--table-stripe); -} - -/* Dark mode image handling - set white background for images that may be transparent */ -[data-theme="dark"] img, -@media (prefers-color-scheme: dark) { - :root:not([data-theme]) img { - background-color: white; - border-radius: 4px; - padding: 2px; - } -} - -/* Dark mode toggle button styling */ -.theme-toggle { - background: none; - border: 1px solid var(--border-color, #007ab7); - border-radius: 4px; - color: var(--text-color, #222); - cursor: pointer; - font-size: 14px; - margin-left: 10px; - padding: 5px 10px; - transition: all 0.3s ease; -} - -.theme-toggle:hover { - background-color: var(--who-blue, #0093d0); - color: white; -} - -.theme-toggle:focus { - outline: 2px solid var(--who-blue, #0093d0); - outline-offset: 2px; + background: var(--dmn-table-stripe); } \ No newline at end of file diff --git a/local-template/package/content/assets/js/theme-toggle.js b/local-template/package/content/assets/js/theme-toggle.js deleted file mode 100644 index a4dfdc09a1..0000000000 --- a/local-template/package/content/assets/js/theme-toggle.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Dark/Light mode theme toggle functionality - * Handles theme switching and persistence across page loads - */ -(function() { - 'use strict'; - - // Get current theme from localStorage or detect system preference - function getCurrentTheme() { - const savedTheme = localStorage.getItem('theme'); - if (savedTheme) { - return savedTheme; - } - - // Check system preference - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - return 'dark'; - } - - return 'light'; - } - - // Apply theme to the document - function applyTheme(theme) { - if (theme === 'dark') { - document.documentElement.setAttribute('data-theme', 'dark'); - } else { - document.documentElement.removeAttribute('data-theme'); - } - } - - // Toggle between light and dark themes - function toggleTheme() { - const currentTheme = getCurrentTheme(); - const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; - - localStorage.setItem('theme', newTheme); - applyTheme(newTheme); - updateToggleButton(); - } - - // Update the toggle button text/icon - function updateToggleButton() { - const button = document.getElementById('theme-toggle'); - if (button) { - const currentTheme = getCurrentTheme(); - button.textContent = currentTheme === 'dark' ? '☀️ Light' : '🌙 Dark'; - button.setAttribute('aria-label', `Switch to ${currentTheme === 'dark' ? 'light' : 'dark'} mode`); - } - } - - // Create and insert the theme toggle button - function createToggleButton() { - const button = document.createElement('button'); - button.id = 'theme-toggle'; - button.className = 'theme-toggle'; - button.type = 'button'; - button.addEventListener('click', toggleTheme); - - // Try to find a good location for the button (near language selector or in header) - const possibleLocations = [ - '.navbar-nav', - '.navbar', - '#segment-header', - '.segment-header', - 'header', - '.container-fluid', - 'body' - ]; - - let inserted = false; - for (const selector of possibleLocations) { - const container = document.querySelector(selector); - if (container && !inserted) { - // If it's a nav container, append to it - if (selector.includes('nav')) { - container.appendChild(button); - } else { - // For other containers, create a wrapper div and position it appropriately - const wrapper = document.createElement('div'); - wrapper.style.cssText = 'position: absolute; top: 10px; right: 10px; z-index: 1000;'; - wrapper.appendChild(button); - - if (container.style.position === '' || container.style.position === 'static') { - container.style.position = 'relative'; - } - container.appendChild(wrapper); - } - inserted = true; - break; - } - } - - // Fallback: create a floating button if no suitable container found - if (!inserted) { - const wrapper = document.createElement('div'); - wrapper.style.cssText = 'position: fixed; top: 10px; right: 10px; z-index: 9999;'; - wrapper.appendChild(button); - document.body.appendChild(wrapper); - } - - return button; - } - - // Initialize theme functionality - function initTheme() { - // Apply the current theme immediately - const currentTheme = getCurrentTheme(); - applyTheme(currentTheme); - - // Create the toggle button when DOM is ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', function() { - createToggleButton(); - updateToggleButton(); - }); - } else { - createToggleButton(); - updateToggleButton(); - } - - // Listen for system theme changes - if (window.matchMedia) { - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { - // Only update if user hasn't explicitly set a theme - if (!localStorage.getItem('theme')) { - applyTheme(e.matches ? 'dark' : 'light'); - updateToggleButton(); - } - }); - } - } - - // Start initialization - initTheme(); -})(); \ No newline at end of file diff --git a/local-template/package/includes/_append.fragment-script.html b/local-template/package/includes/_append.fragment-script.html deleted file mode 100644 index 5ec637cdc2..0000000000 --- a/local-template/package/includes/_append.fragment-script.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file