]> danix's work - danix.xyz-2.git/commitdiff
Fix design system compliance: CSS variables, accessibility, and theme system
authorDanilo M. <redacted>
Wed, 15 Apr 2026 19:42:04 +0000 (21:42 +0200)
committerDanilo M. <redacted>
Wed, 15 Apr 2026 19:42:04 +0000 (21:42 +0200)
Core CSS improvements:
- Add --surface-rgb, --type-* (tech, life, quote, link, photo) custom properties
- Add --type-* semantic color classes for article badges
- Convert article badges from inline styles to CSS variable system
- Add prefers-color-scheme light fallback for no-JS users
- Add prefers-reduced-motion support to respect user accessibility settings
- Replace *:focus with *:focus-visible (keyboard-only outlines)
- Add clamp() fluid typography for hero-title and section-title
- Refactor container rules to mobile-first with 1060px breakpoint

Theme & Icon fixes:
- Fix theme toggle icon display with Alpine.js (was broken with Tailwind dark: classes)
- Add aria-hidden="true" to icon elements
- Update header with proper ARIA attributes on menu toggle

Accessibility enhancements:
- Add skip-to-main-content link in baseof.html
- Update hamburger menu with aria-expanded, aria-controls, aria-hidden
- Implement focus trap (Tab loops) within mobile menu
- Return focus to trigger button on menu close
- Add menu open/close state management with proper ARIA

Semantic HTML:
- Wrap article pages in <article> element (articles/single.html, _default/single.html)
- Fix quote article to use --type-quote border color instead of generic accent

Image optimization:
- Add loading="lazy" to profile image in index.html
- Add loading="lazy" to featured image in photo.html

Template fixes:
- Remove broken os.Getenv "THEME" runtime check from article-list-item.html
- Replace inline color styles with semantic .type-* classes
- Add 1060px lg: breakpoint to tailwind.config.js

i18n updates:
- Add skipToContent translations (en, it)

Co-Authored-By: Claude Haiku 4.5 <redacted>
16 files changed:
i18n/en.yaml
i18n/it.yaml
tailwind.config.js
themes/danix-xyz-hacker/assets/css/main.css
themes/danix-xyz-hacker/assets/css/main.min.css
themes/danix-xyz-hacker/assets/js/menu.js
themes/danix-xyz-hacker/layouts/_default/baseof.html
themes/danix-xyz-hacker/layouts/_default/single.html
themes/danix-xyz-hacker/layouts/articles/single.html
themes/danix-xyz-hacker/layouts/index.html
themes/danix-xyz-hacker/layouts/partials/article-header.html
themes/danix-xyz-hacker/layouts/partials/article-list-item.html
themes/danix-xyz-hacker/layouts/partials/article-types/photo.html
themes/danix-xyz-hacker/layouts/partials/article-types/quote.html
themes/danix-xyz-hacker/layouts/partials/hamburger-menu.html
themes/danix-xyz-hacker/layouts/partials/header.html

index cd061c0a6336d7034c17b923eeb99e77f14d4f75..2bcf5792b568654753f6c1f2713211897c658bb0 100644 (file)
@@ -7,6 +7,7 @@ language: "Language"
 toggleTheme: "Theme"
 toggleMenu: "Menu"
 closeMenu: "Close"
+skipToContent: "Skip to main content"
 email: "Email"
 contact: "Contact"
 links: "Links"
index 92406d90c67ad44c239139271cffbeb622167253..e22c0c50c3ae5b441162edf201e7ad813adb5c1b 100644 (file)
@@ -7,6 +7,7 @@ language: "Lingua"
 toggleTheme: "Tema"
 toggleMenu: "Menu"
 closeMenu: "Chiudi"
+skipToContent: "Salta al contenuto principale"
 email: "Email"
 contact: "Contatti"
 links: "Link"
index d36b0a80ffb34d4773c9cd29925dfb4f0a4c79af..978bba7e4fa145d5f3db0d2c6b87ecadecb3fdf9 100644 (file)
@@ -7,6 +7,9 @@ module.exports = {
   ],
   theme: {
     extend: {
+      screens: {
+        'lg': '1060px',
+      },
       colors: {
         bg: 'var(--bg)',
         'bg2': 'var(--bg2)',
index c66092b1e95183aebdd91d84e9b4cc6d02a02707..2b8cbdeb2a00b32161c70a326790c3b976fe9ec8 100644 (file)
@@ -7,6 +7,7 @@
   --bg: #060b10;
   --bg2: #0c1520;
   --surface: #101e2d;
+  --surface-rgb: 16, 30, 45;
   --border: #182840;
   --accent: #a855f7;
   --accent2: #00ff88;
   --text: #c4d6e8;
   --text-dim: #7a9bb8;
   --muted: #304860;
+  /* Article type colors - dark */
+  --type-tech: #a855f7;
+  --type-life: #f59e0b;
+  --type-quote: #00ff88;
+  --type-link: #38bdf8;
+  --type-photo: #ec4899;
 }
 
 /* Light theme overrides */
@@ -21,6 +28,7 @@ html.theme-light {
   --bg: #ffffff;
   --bg2: #f8f9fa;
   --surface: #f0f3f7;
+  --surface-rgb: 240, 243, 247;
   --border: #d9dfe8;
   --accent: #9333ea;
   --accent2: #10b981;
@@ -28,6 +36,34 @@ html.theme-light {
   --text: #1f2937;
   --text-dim: #374151;
   --muted: #d1d5db;
+  /* Article type colors - light */
+  --type-tech: #7c3aed;
+  --type-life: #d97706;
+  --type-quote: #008f5a;
+  --type-link: #0284c7;
+  --type-photo: #be185d;
+}
+
+/* No-JS fallback: prefers-color-scheme light */
+@media (prefers-color-scheme: light) {
+  html:not(.theme-dark) {
+    --bg: #ffffff;
+    --bg2: #f8f9fa;
+    --surface: #f0f3f7;
+    --surface-rgb: 240, 243, 247;
+    --border: #d9dfe8;
+    --accent: #9333ea;
+    --accent2: #10b981;
+    --accent-glow: rgba(147, 51, 234, 0.1);
+    --text: #1f2937;
+    --text-dim: #374151;
+    --muted: #d1d5db;
+    --type-tech: #7c3aed;
+    --type-life: #d97706;
+    --type-quote: #008f5a;
+    --type-link: #0284c7;
+    --type-photo: #be185d;
+  }
 }
 
 @layer base {
@@ -66,19 +102,15 @@ html.theme-light {
   }
 
   pre {
-    background-color: rgba(16, 30, 45, 0.8);
+    background-color: rgba(var(--surface-rgb), 0.8);
     @apply p-4 rounded border border-border overflow-x-auto;
   }
 
-  html.theme-light pre {
-    background-color: rgba(240, 243, 247, 0.8);
-  }
-
   pre code {
     @apply bg-transparent border-0 p-0 text-text;
   }
 
-  *:focus {
+  *:focus-visible {
     @apply ring-2 ring-accent ring-offset-2;
     ring-offset-color: var(--bg);
   }
@@ -144,6 +176,41 @@ html.theme-light {
   .glow-accent {
     box-shadow: 0 0 20px var(--accent-glow);
   }
+
+  /* Article type badge styles */
+  .type-tech {
+    color: var(--type-tech);
+    background-color: rgba(168, 85, 247, 0.1);
+  }
+
+  .type-life {
+    color: var(--type-life);
+    background-color: rgba(245, 158, 11, 0.1);
+  }
+
+  .type-quote {
+    color: var(--type-quote);
+    background-color: rgba(0, 255, 136, 0.1);
+  }
+
+  .type-link {
+    color: var(--type-link);
+    background-color: rgba(56, 189, 248, 0.1);
+  }
+
+  .type-photo {
+    color: var(--type-photo);
+    background-color: rgba(236, 72, 153, 0.1);
+  }
+
+  /* Hero typography with fluid sizing */
+  .hero-title {
+    font-size: clamp(2rem, 5vw + 1rem, 4.5rem);
+  }
+
+  .section-title {
+    font-size: clamp(1.5rem, 3vw + 0.5rem, 2.5rem);
+  }
 }
 
 /* Prose overrides for light theme */
@@ -194,15 +261,28 @@ html.theme-light .prose-invert blockquote {
   border-left-color: var(--accent);
 }
 
-/* Responsive utilities */
-@media (max-width: 768px) {
-  .sm\:container {
-    @apply max-w-full px-4;
+/* Responsive container utilities - mobile-first */
+.container {
+  @apply max-w-full px-4;
+}
+
+@media (min-width: 768px) {
+  .container {
+    @apply max-w-4xl px-6;
+  }
+}
+
+@media (min-width: 1060px) {
+  .container {
+    @apply max-w-5xl px-8;
   }
 }
 
-@media (min-width: 769px) {
-  .md\:container {
-    @apply max-w-4xl;
+/* Respect user's motion preferences */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
   }
 }
index 3ab93b79c46a4db0bf49787902c2da8c1cf7010e..858df863267d84c729bf9a0c3ddcaccc74463aed 100644 (file)
@@ -631,7 +631,7 @@ code {
 }
 
 pre {
-  background-color: rgba(16, 30, 45, 0.8);
+  background-color: rgba(var(--surface-rgb), 0.8);
   overflow-x: auto;
   border-radius: 0.25rem;
   border-width: 1px;
@@ -639,10 +639,6 @@ pre {
   padding: 1rem;
 }
 
-html.theme-light pre {
-  background-color: rgba(240, 243, 247, 0.8);
-}
-
 pre code {
   border-width: 0px;
   background-color: transparent;
@@ -650,7 +646,7 @@ pre code {
   color: var(--text);
 }
 
-*:focus {
+*:focus-visible {
   --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
   --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
   box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
@@ -684,9 +680,9 @@ button,
   }
 }
 
-@media (min-width: 1024px) {
+@media (min-width: 1060px) {
   .container {
-    max-width: 1024px;
+    max-width: 1060px;
   }
 }
 
@@ -1268,6 +1264,22 @@ button,
 
 /* Glow effect utility */
 
+/* Article type badge styles */
+
+/* Hero typography with fluid sizing */
+
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
 .pointer-events-none {
   pointer-events: none;
 }
@@ -1556,6 +1568,10 @@ button,
   border-top-width: 1px;
 }
 
+.border-\[--type-quote\] {
+  border-color: var(--type-quote);
+}
+
 .border-accent {
   border-color: var(--accent);
 }
@@ -1812,6 +1828,7 @@ button,
   --bg: #060b10;
   --bg2: #0c1520;
   --surface: #101e2d;
+  --surface-rgb: 16, 30, 45;
   --border: #182840;
   --accent: #a855f7;
   --accent2: #00ff88;
@@ -1819,6 +1836,12 @@ button,
   --text: #c4d6e8;
   --text-dim: #7a9bb8;
   --muted: #304860;
+  /* Article type colors - dark */
+  --type-tech: #a855f7;
+  --type-life: #f59e0b;
+  --type-quote: #00ff88;
+  --type-link: #38bdf8;
+  --type-photo: #ec4899;
 }
 
 /* Light theme overrides */
@@ -1827,6 +1850,7 @@ html.theme-light {
   --bg: #ffffff;
   --bg2: #f8f9fa;
   --surface: #f0f3f7;
+  --surface-rgb: 240, 243, 247;
   --border: #d9dfe8;
   --accent: #9333ea;
   --accent2: #10b981;
@@ -1834,6 +1858,35 @@ html.theme-light {
   --text: #1f2937;
   --text-dim: #374151;
   --muted: #d1d5db;
+  /* Article type colors - light */
+  --type-tech: #7c3aed;
+  --type-life: #d97706;
+  --type-quote: #008f5a;
+  --type-link: #0284c7;
+  --type-photo: #be185d;
+}
+
+/* No-JS fallback: prefers-color-scheme light */
+
+@media (prefers-color-scheme: light) {
+  html:not(.theme-dark) {
+    --bg: #ffffff;
+    --bg2: #f8f9fa;
+    --surface: #f0f3f7;
+    --surface-rgb: 240, 243, 247;
+    --border: #d9dfe8;
+    --accent: #9333ea;
+    --accent2: #10b981;
+    --accent-glow: rgba(147, 51, 234, 0.1);
+    --text: #1f2937;
+    --text-dim: #374151;
+    --muted: #d1d5db;
+    --type-tech: #7c3aed;
+    --type-life: #d97706;
+    --type-quote: #008f5a;
+    --type-link: #0284c7;
+    --type-photo: #be185d;
+  }
 }
 
 /* Prose overrides for light theme */
@@ -1885,19 +1938,37 @@ html.theme-light .prose-invert blockquote {
   border-left-color: var(--accent);
 }
 
-/* Responsive utilities */
+/* Responsive container utilities - mobile-first */
 
-@media (max-width: 768px) {
-  .sm\:container {
-    max-width: 100%;
-    padding-left: 1rem;
-    padding-right: 1rem;
-  }
+.container {
+  max-width: 100%;
+  padding-left: 1rem;
+  padding-right: 1rem;
 }
 
-@media (min-width: 769px) {
-  .md\:container {
+@media (min-width: 768px) {
+  .container {
     max-width: 56rem;
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+}
+
+@media (min-width: 1060px) {
+  .container {
+    max-width: 64rem;
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+}
+
+/* Respect user's motion preferences */
+
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
   }
 }
 
@@ -1929,10 +2000,60 @@ html.theme-light .prose-invert blockquote {
   opacity: 0.9;
 }
 
+.focus\:not-sr-only:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  padding: 0;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+  white-space: normal;
+}
+
+.focus\:fixed:focus {
+  position: fixed;
+}
+
+.focus\:left-4:focus {
+  left: 1rem;
+}
+
+.focus\:top-4:focus {
+  top: 1rem;
+}
+
+.focus\:z-50:focus {
+  z-index: 50;
+}
+
+.focus\:rounded:focus {
+  border-radius: 0.25rem;
+}
+
 .focus\:border-accent:focus {
   border-color: var(--accent);
 }
 
+.focus\:bg-accent:focus {
+  background-color: var(--accent);
+}
+
+.focus\:px-4:focus {
+  padding-left: 1rem;
+  padding-right: 1rem;
+}
+
+.focus\:py-2:focus {
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
+
+.focus\:text-white:focus {
+  --tw-text-opacity: 1;
+  color: rgb(255 255 255 / var(--tw-text-opacity, 1));
+}
+
 .focus\:outline-none:focus {
   outline: 2px solid transparent;
   outline-offset: 2px;
@@ -2016,13 +2137,3 @@ html.theme-light .prose-invert blockquote {
     line-height: 1;
   }
 }
-
-@media (prefers-color-scheme: dark) {
-  .dark\:block {
-    display: block;
-  }
-
-  .dark\:hidden {
-    display: none;
-  }
-}
index f61e60b0be59e2c391ad7bfb44a1e6ea89bf83c9..3f326425e214474291940fbc537d64c79eaf68a0 100644 (file)
@@ -1,26 +1,59 @@
 document.addEventListener('DOMContentLoaded', () => {
   const menuToggle = document.getElementById('menu-toggle');
   const menuOverlay = document.getElementById('menu-overlay');
-  const menuPanel = document.getElementById('menu-panel');
+  const menuPanel = document.getElementById('hamburger-menu');
 
-  function toggleMenu() {
+  function openMenu() {
     if (!menuOverlay || !menuPanel) return;
 
-    // Toggle overlay visibility classes
-    menuOverlay.classList.toggle('opacity-0');
-    menuOverlay.classList.toggle('invisible');
+    // Show overlay
+    menuOverlay.classList.remove('opacity-0');
+    menuOverlay.classList.remove('invisible');
+
+    // Slide menu in
+    menuPanel.classList.remove('translate-x-full');
 
-    // Toggle menu panel translation
-    menuPanel.classList.toggle('translate-x-full');
+    // Manage accessibility
+    menuToggle.setAttribute('aria-expanded', 'true');
+    menuPanel.removeAttribute('aria-hidden');
 
     // Control body overflow
-    const isMenuOpen = !menuOverlay.classList.contains('opacity-0');
-    document.body.style.overflow = isMenuOpen ? 'hidden' : '';
+    document.body.style.overflow = 'hidden';
+
+    // Focus first focusable element in menu
+    const firstFocusable = menuPanel.querySelector('a, button');
+    if (firstFocusable) {
+      setTimeout(() => firstFocusable.focus(), 50);
+    }
   }
 
   function closeMenu() {
     if (!menuOverlay || menuOverlay.classList.contains('opacity-0')) return;
-    toggleMenu();
+
+    // Hide overlay
+    menuOverlay.classList.add('opacity-0');
+    menuOverlay.classList.add('invisible');
+
+    // Slide menu out
+    menuPanel.classList.add('translate-x-full');
+
+    // Manage accessibility
+    menuToggle.setAttribute('aria-expanded', 'false');
+    menuPanel.setAttribute('aria-hidden', 'true');
+
+    // Restore body overflow
+    document.body.style.overflow = '';
+
+    // Return focus to toggle button
+    menuToggle.focus();
+  }
+
+  function toggleMenu() {
+    if (menuOverlay && menuOverlay.classList.contains('opacity-0')) {
+      openMenu();
+    } else {
+      closeMenu();
+    }
   }
 
   // Toggle menu when clicking the hamburger button
@@ -38,7 +71,7 @@ document.addEventListener('DOMContentLoaded', () => {
   }
 
   // Close menu when clicking menu items
-  const menuLinks = document.querySelectorAll('#menu-panel a, #menu-panel button');
+  const menuLinks = document.querySelectorAll('#hamburger-menu a, #hamburger-menu button');
   menuLinks.forEach(link => {
     link.addEventListener('click', closeMenu);
   });
@@ -49,4 +82,31 @@ document.addEventListener('DOMContentLoaded', () => {
       closeMenu();
     }
   });
+
+  // Focus trap: keep tab within menu when open
+  if (menuPanel) {
+    menuPanel.addEventListener('keydown', (e) => {
+      if (e.key !== 'Tab') return;
+
+      const focusableElements = menuPanel.querySelectorAll('a, button, [tabindex]:not([tabindex="-1"])');
+      if (focusableElements.length === 0) return;
+
+      const firstElement = focusableElements[0];
+      const lastElement = focusableElements[focusableElements.length - 1];
+      const isMenuOpen = !menuOverlay.classList.contains('opacity-0');
+
+      if (!isMenuOpen) return;
+
+      // Shift+Tab on first element: move to last
+      if (e.shiftKey && document.activeElement === firstElement) {
+        e.preventDefault();
+        lastElement.focus();
+      }
+      // Tab on last element: move to first
+      else if (!e.shiftKey && document.activeElement === lastElement) {
+        e.preventDefault();
+        firstElement.focus();
+      }
+    });
+  }
 });
index f0a928362f72faaf02843fd8c353d1b536a9f903..28495625c3ef0c1d51f44b140cc918f1f387d6b9 100644 (file)
   <link rel="stylesheet" href="{{ $chroma.RelPermalink }}">
 </head>
 <body class="bg-bg text-text antialiased">
+  <!-- Skip to main content link -->
+  <a href="#main" class="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-accent focus:text-white focus:rounded">
+    {{ i18n "skipToContent" }}
+  </a>
+
   <!-- Dot grid background pattern -->
   <div class="fixed inset-0 pointer-events-none opacity-5 dot-grid" style="
     background-image: radial-gradient(circle, currentColor 1px, transparent 1px);
index 3269decd38af4cfb286bad48fe6000f99c7a83ae..60cf03d105554d0a0cbb1fbeb6faa9306b23583d 100644 (file)
@@ -1,5 +1,5 @@
 {{ define "main" }}
-<div class="mx-auto px-4 py-12">
+<article class="mx-auto px-4 py-12">
   <div class="grid md:grid-cols-3 gap-8">
     <!-- Article section -->
     <div class="md:col-span-2">
@@ -37,5 +37,5 @@
     <!-- Sidebar -->
     {{ partial "sidebar.html" . }}
   </div>
-</div>
+</article>
 {{ end }}
index 67e8a2fa0c31a507d8691d6cbb08147f7085b4c0..c5d51e7916dc131794af4dd59d7134d9f4d5cad8 100644 (file)
@@ -1,7 +1,7 @@
 {{ define "main" }}
 {{ $articleType := .Params.type | default "life" }}
 {{ $template := printf "article-types/%s.html" $articleType }}
-<div class="mx-auto px-4 py-12">
+<article class="mx-auto px-4 py-12">
   <div class="grid md:grid-cols-3 gap-8">
     <!-- Article section -->
     <div class="md:col-span-2">
@@ -37,5 +37,5 @@
     <!-- Sidebar -->
     {{ partial "sidebar.html" . }}
   </div>
-</div>
+</article>
 {{ end }}
index ed151cb2be3a22f22181e782bd52849cbe3f66aa..d1efc53518bc6464d40954ab154bce9653003927 100644 (file)
@@ -7,6 +7,7 @@
       <img
         src="{{ .Params.image }}"
         alt="{{ .Site.Params.author }}"
+        loading="lazy"
         class="w-32 h-32 md:w-48 md:h-48 rounded-full border-4 border-accent object-cover"
       >
     </div>
index 10808f96c9a8a5118ef49220badfefec546cf3fa..b7599096962f437fbe1094a2e8ef729f7c2318cc 100644 (file)
@@ -6,9 +6,7 @@
   <!-- Type badge -->
   {{ if $typeData }}
   <span
-    class="inline-flex items-center px-3 py-1 rounded text-sm font-semibold mb-4 transition-colors"
-    style="color: {{ $typeData.color_light }}; background-color: {{ $typeData.color_light }}20;"
-    data-theme-dark-color="{{ $typeData.color_dark }}"
+    class="inline-flex items-center px-3 py-1 rounded text-sm font-semibold mb-4 transition-colors type-{{ $articleType }}"
   >
     {{ i18n $articleType }}
   </span>
index 652e171596188ba7e8fdcccf9b3f174cee0a1d22..1065f0b8859f96504bb0762ecfed262c6dd5e192 100644 (file)
@@ -1,8 +1,6 @@
 {{ $articleType := .Params.type | default "life" }}
 {{ $typeConfig := .Site.Params.articleTypes }}
 {{ $typeData := index $typeConfig $articleType }}
-{{ $isDark := strings.Contains (os.Getenv "THEME") "dark" }}
-{{ $color := cond $isDark $typeData.color_dark $typeData.color_light }}
 
 <a
   href="{{ .RelPermalink }}"
@@ -30,8 +28,7 @@
     <!-- Type badge -->
     {{ if $typeData }}
     <span
-      class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium transition-colors"
-      style="color: {{ $color }}; background-color: {{ $color }}20;"
+      class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium transition-colors type-{{ $articleType }}"
     >
       {{ i18n $articleType }}
     </span>
index f4ccf0676d5b55631f91d9c045bfd7b113d2214c..743e02a2a1f8a6efd3ee54b4a6cde71db000db02 100644 (file)
@@ -3,6 +3,7 @@
   <img
     src="{{ .Params.featured_image }}"
     alt="{{ .Title }}"
+    loading="lazy"
     class="w-full h-auto rounded-lg border border-border/30"
   />
   {{ if .Params.featured_image_caption }}
index 3df7327a0d5466e8c3ed40f2bb057328e71eb9c9..f27d189560674ffffa09b0ded983b35a4c8d849e 100644 (file)
@@ -1,4 +1,4 @@
-<blockquote class="mb-8 pl-6 border-l-4 border-accent italic text-2xl text-text">
+<blockquote class="mb-8 pl-6 border-l-4 border-[--type-quote] italic text-2xl text-text">
   "{{ .Params.quote_text }}"
 </blockquote>
 
index bdcbca9a97a83b068d9a37f5596858d943f9aa7f..666db78c3db7f18dfd0d8d1b8f9a0815d0c0a848 100644 (file)
@@ -3,7 +3,8 @@
   class="fixed inset-0 bg-black/50 backdrop-blur opacity-0 invisible transition-all duration-200 z-40"
 >
   <div
-    id="menu-panel"
+    id="hamburger-menu"
+    aria-hidden="true"
     class="fixed top-0 right-0 h-screen w-full max-w-sm bg-bg border-l border-border overflow-y-auto transform translate-x-full transition-transform duration-300 z-50"
   >
     <!-- Close button -->
index 2ddb2e3b88e17a3a5a5c6939ed558fb09c4241f1..7f9974885956188a2e5cf32ef9f082452cd89c1a 100644 (file)
       <!-- Theme toggle button -->
       <button
         id="theme-toggle"
+        x-data="{ theme: localStorage.getItem('theme') || 'dark' }"
+        @click="theme = theme === 'dark' ? 'light' : 'dark'; document.documentElement.className = 'theme-' + theme; localStorage.setItem('theme', theme)"
         aria-label="{{ i18n "toggleTheme" }}"
         class="p-2 rounded hover:bg-surface transition-colors"
       >
-        <i data-feather="sun" class="w-5 h-5 hidden dark:block"></i>
-        <i data-feather="moon" class="w-5 h-5 block dark:hidden"></i>
+        <i x-show="theme === 'dark'" data-feather="sun" class="w-5 h-5" aria-hidden="true"></i>
+        <i x-show="theme === 'light'" data-feather="moon" class="w-5 h-5" aria-hidden="true"></i>
       </button>
 
       <!-- Hamburger menu button (mobile only) -->
       <button
         id="menu-toggle"
         aria-label="{{ i18n "toggleMenu" }}"
+        aria-expanded="false"
+        aria-controls="hamburger-menu"
         class="md:hidden p-2 rounded hover:bg-surface transition-colors"
       >
         <i data-feather="menu" class="w-5 h-5"></i>