-
Notifications
You must be signed in to change notification settings - Fork 0
CDB-113 Feat/dark mode #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,146 @@ | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Dark Mode Toggle - Sistema Independente | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * Script vanilla JavaScript que: | ||||||||||||||||||||||
| * - Cria um botão no navbar com ícone Bootstrap Icons | ||||||||||||||||||||||
| * - Gerencia o estado do tema (light/dark) | ||||||||||||||||||||||
| * - Persiste a escolha no localStorage | ||||||||||||||||||||||
| * - Detecta preferência do sistema | ||||||||||||||||||||||
| * - Não interfere com código React existente | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| (function() { | ||||||||||||||||||||||
| 'use strict'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Chave para localStorage | ||||||||||||||||||||||
| const STORAGE_KEY = 'theme-preference'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Função para obter o tema inicial | ||||||||||||||||||||||
| function getInitialTheme() { | ||||||||||||||||||||||
| // Primeiro, verifica localStorage | ||||||||||||||||||||||
| const saved = localStorage.getItem(STORAGE_KEY); | ||||||||||||||||||||||
| if (saved) { | ||||||||||||||||||||||
| return saved; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Depois, verifica preferência do sistema | ||||||||||||||||||||||
| if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { | ||||||||||||||||||||||
| return 'dark'; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Padrão: light | ||||||||||||||||||||||
| return 'light'; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Função para aplicar o tema | ||||||||||||||||||||||
| function applyTheme(theme) { | ||||||||||||||||||||||
| document.documentElement.setAttribute('data-theme', theme); | ||||||||||||||||||||||
| localStorage.setItem(STORAGE_KEY, theme); | ||||||||||||||||||||||
| updateToggleButton(theme); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Função para alternar tema | ||||||||||||||||||||||
| function toggleTheme() { | ||||||||||||||||||||||
| const current = document.documentElement.getAttribute('data-theme') || 'light'; | ||||||||||||||||||||||
| const next = current === 'light' ? 'dark' : 'light'; | ||||||||||||||||||||||
| applyTheme(next); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Função para criar o botão de toggle | ||||||||||||||||||||||
| function createToggleButton() { | ||||||||||||||||||||||
| // Verifica se já existe | ||||||||||||||||||||||
| if (document.getElementById('dark-mode-toggle')) { | ||||||||||||||||||||||
| return; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Encontra o navbar | ||||||||||||||||||||||
| const navbar = document.querySelector('.navbar-nav:last-child'); | ||||||||||||||||||||||
| if (!navbar) { | ||||||||||||||||||||||
| console.warn('Navbar não encontrado, botão não foi criado'); | ||||||||||||||||||||||
| return; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Cria o item de lista | ||||||||||||||||||||||
| const li = document.createElement('li'); | ||||||||||||||||||||||
| li.className = 'nav-item'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Cria o botão | ||||||||||||||||||||||
| const button = document.createElement('button'); | ||||||||||||||||||||||
| button.id = 'dark-mode-toggle'; | ||||||||||||||||||||||
| button.setAttribute('aria-label', 'Alternar modo escuro'); | ||||||||||||||||||||||
| button.innerHTML = '<i class="bi bi-moon-stars-fill"></i>'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Event listener | ||||||||||||||||||||||
| button.addEventListener('click', toggleTheme); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Adiciona ao navbar | ||||||||||||||||||||||
| li.appendChild(button); | ||||||||||||||||||||||
| navbar.appendChild(li); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Função para atualizar o ícone do botão | ||||||||||||||||||||||
| function updateToggleButton(theme) { | ||||||||||||||||||||||
| const button = document.getElementById('dark-mode-toggle'); | ||||||||||||||||||||||
| if (button) { | ||||||||||||||||||||||
| const icon = theme === 'light' | ||||||||||||||||||||||
| ? '<i class="bi bi-moon-stars-fill"></i>' | ||||||||||||||||||||||
| : '<i class="bi bi-sun-fill"></i>'; | ||||||||||||||||||||||
| const title = theme === 'light' ? 'Ativar modo escuro' : 'Ativar modo claro'; | ||||||||||||||||||||||
| button.innerHTML = icon; | ||||||||||||||||||||||
| button.setAttribute('title', title); | ||||||||||||||||||||||
|
Comment on lines
+88
to
+90
|
||||||||||||||||||||||
| const title = theme === 'light' ? 'Ativar modo escuro' : 'Ativar modo claro'; | |
| button.innerHTML = icon; | |
| button.setAttribute('title', title); | |
| const title = theme === 'light' ? 'Ativar modo escuro' : 'Ativar modo claro'; | |
| const ariaLabel = title; // Use the same string for aria-label | |
| const ariaPressed = theme === 'dark' ? 'true' : 'false'; | |
| button.innerHTML = icon; | |
| button.setAttribute('title', title); | |
| button.setAttribute('aria-label', ariaLabel); | |
| button.setAttribute('aria-pressed', ariaPressed); |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tryCreateButton function will attempt to find the navbar up to 20 times with 100ms intervals (total 2 seconds). However, if the navbar structure changes or the selector .navbar-nav:last-child doesn't match the actual DOM structure, this will silently fail after all attempts. Consider adding a console warning or error after all attempts are exhausted to help with debugging, or verify that this selector accurately matches your navbar structure.
| setTimeout(() => tryCreateButton(attempts + 1), 100); | |
| setTimeout(() => tryCreateButton(attempts + 1), 100); | |
| } else { | |
| console.warn( | |
| "[DarkModeToggle] Failed to find navbar with selector '.navbar-nav:last-child' after 20 attempts. " + | |
| "Button not created. Please check if the selector matches your DOM structure." | |
| ); |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The window.darkMode object is exposed globally for debugging purposes, but this could lead to unintended side effects if other code modifies these functions or if there are naming conflicts. Consider either removing this exposure in production code, or namespacing it more specifically (e.g., window.__DESCRIPTUM_DARK_MODE__) to avoid potential conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The button has an
aria-labelattribute but it's static ("Alternar modo escuro"). Consider usingaria-pressedattribute to indicate the current state of the toggle button for better accessibility. The button should havearia-pressed="false"in light mode andaria-pressed="true"in dark mode to communicate the toggle state to screen readers.