From ac3d30d59713fe9a2dd9787a537da57b3580e796 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 31 Mar 2025 04:04:47 -0400 Subject: [PATCH] Fix CSP issues by moving inline script to external file and adding SRI --- docker/resume/Caddyfile | 2 +- docker/resume/index.html | 64 +--------------------------------------- docker/resume/theme.js | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 docker/resume/theme.js 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(); + } + }); +});