diff --git a/docker/resume/Caddyfile b/docker/resume/Caddyfile index ea2f27c..450afb0 100644 --- a/docker/resume/Caddyfile +++ b/docker/resume/Caddyfile @@ -26,7 +26,7 @@ Cross-Origin-Opener-Policy "same-origin" # Simplified CSP for static content - Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none';" + Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'; require-sri-for script;" } # Handle 404s diff --git a/docker/resume/index.html b/docker/resume/index.html index 7109a44..8169095 100644 --- a/docker/resume/index.html +++ b/docker/resume/index.html @@ -6,6 +6,7 @@ Colin Knapp Portfolio +
@@ -182,68 +183,5 @@

Accessibility: This website is designed and developed to meet WCAG 2.1 Level AAA standards, ensuring the highest level of accessibility for all users. Features include high contrast ratios, keyboard navigation, screen reader compatibility, and responsive design. The site supports both light and dark modes with automatic system preference detection.

- - \ No newline at end of file diff --git a/docker/resume/theme.js b/docker/resume/theme.js new file mode 100644 index 0000000..7ca9482 --- /dev/null +++ b/docker/resume/theme.js @@ -0,0 +1,62 @@ +document.addEventListener('DOMContentLoaded', function() { + const themeToggle = document.getElementById('themeToggle'); + const html = document.documentElement; + + // Check for saved theme preference, default to auto + const savedTheme = localStorage.getItem('theme') || 'auto'; + updateTheme(savedTheme); + + function updateTheme(theme) { + // Update button state and labels + const themeLabels = { + light: 'Theme mode: Light', + dark: 'Theme mode: Dark', + auto: 'Theme mode: Auto' + }; + + themeToggle.setAttribute('aria-label', themeLabels[theme]); + themeToggle.setAttribute('aria-pressed', theme !== 'auto'); + + // Update button icon + const themeIcons = { + light: '🌞', + dark: '🌙', + auto: '🌓' + }; + + themeToggle.textContent = themeIcons[theme]; + + if (theme === 'auto') { + html.removeAttribute('data-theme'); + } else { + html.setAttribute('data-theme', theme); + } + } + + themeToggle.addEventListener('click', () => { + const currentTheme = html.getAttribute('data-theme') || 'auto'; + let newTheme; + + switch(currentTheme) { + case 'light': + newTheme = 'dark'; + break; + case 'dark': + newTheme = 'auto'; + break; + default: + newTheme = 'light'; + } + + updateTheme(newTheme); + localStorage.setItem('theme', newTheme); + }); + + // Handle keyboard navigation + themeToggle.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + themeToggle.click(); + } + }); +});