{"id":134,"date":"2026-09-01T18:19:24","date_gmt":"2026-09-01T21:19:24","guid":{"rendered":"https:\/\/aqgoes.com\/blog\/?p=134"},"modified":"2026-09-04T16:06:30","modified_gmt":"2026-09-04T19:06:30","slug":"dark-mode-menu-mobile-javascript-localstorage","status":"publish","type":"post","link":"https:\/\/aqgoes.com\/blog\/dark-mode-menu-mobile-javascript-localstorage\/","title":{"rendered":"Como Criar Dark Mode e Menu Mobile com JavaScript Puro e Tailwind"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">No artigo anterior, vimos como estruturar o visual <a href=\"https:\/\/www.w3schools.com\/html\/default.asp\" target=\"_blank\" rel=\"noopener\">HTML <\/a>e <a href=\"https:\/\/tailwindcss.com\/\" target=\"_blank\" rel=\"noopener\">Tailwind CSS<\/a> do header do <strong><a href=\"https:\/\/aqgoes.com\/blog\/\" target=\"_blank\" rel=\"noopener\">AqGoEs De<\/a>V<\/strong>. No entanto, um layout limpo s\u00f3 cumpre seu papel se a experi\u00eancia do usu\u00e1rio for fluida.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Nesta etapa, vamos adicionar a <strong>camada de comportamento (interatividade)<\/strong> com JavaScript puro (Vanilla JS). Vamos resolver dois problemas vitais de front-end: o evento de clique do menu hamb\u00farguer no celular e a navega\u00e7\u00e3o em Modo Escuro\/Claro persistida no navegador do usu\u00e1rio.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Por que Usar LocalStorage para o Tema?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quando um usu\u00e1rio escolhe o tema escuro em um site, ele espera que essa prefer\u00eancia seja mantida ao navegar entre as p\u00e1ginas ou ao retornar no dia seguinte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A API de <code>localStorage<\/code> do navegador permite salvar pares de chave-valor diretamente no dispositivo do leitor. Isso evita o inc\u00f4modo efeito de &#8220;tela piscando&#8221; (FOUC \u2014 <em>Flash of Unstyled Content<\/em>) no momento em que a p\u00e1gina recarrega.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">C\u00f3digo JavaScript Completo do Header<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Abaixo est\u00e1 o script estruturado para manipular exatamente os elementos do nosso HTML:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>document.addEventListener('DOMContentLoaded', () =&gt; {\n  \/\/ --- 1. ELEMENTOS DO DOM ---\n  const themeToggleBtn = document.getElementById('theme-toggle');\n  const lightIcon = document.getElementById('theme-toggle-light-icon');\n  const darkIcon = document.getElementById('theme-toggle-dark-icon');\n  \n  const menuToggleBtn = document.getElementById('menu-toggle');\n  const mobileMenu = document.getElementById('mobile-menu');\n\n  \/\/ --- 2. L\u00d3GICA DO TEMA (DARK \/ LIGHT) ---\n  const applyTheme = (isDark) =&gt; {\n    if (isDark) {\n      document.documentElement.classList.add('dark');\n      lightIcon.classList.remove('hidden');\n      darkIcon.classList.add('hidden');\n    } else {\n      document.documentElement.classList.remove('dark');\n      lightIcon.classList.add('hidden');\n      darkIcon.classList.remove('hidden');\n    }\n  };\n\n  \/\/ Verifica a prefer\u00eancia salva ou o padr\u00e3o do sistema operacional\n  const savedTheme = localStorage.getItem('theme');\n  const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n  \n  const initialIsDark = savedTheme ? savedTheme === 'dark' : systemPrefersDark;\n  applyTheme(initialIsDark);\n\n  \/\/ Evento de clique para trocar o tema\n  themeToggleBtn.addEventListener('click', () =&gt; {\n    const isCurrentlyDark = document.documentElement.classList.contains('dark');\n    const newThemeIsDark = !isCurrentlyDark;\n    \n    applyTheme(newThemeIsDark);\n    localStorage.setItem('theme', newThemeIsDark ? 'dark' : 'light');\n  });\n\n  \/\/ --- 3. L\u00d3GICA DO MENU MOBILE ---\n  menuToggleBtn.addEventListener('click', () =&gt; {\n    mobileMenu.classList.toggle('hidden');\n  });\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Entendendo a L\u00f3gica Passo a Passo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para garantir que a implementa\u00e7\u00e3o seja clara, analisaremos os blocos de c\u00f3digo principais:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Funcionalidade<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>M\u00e9todos\/Propriedades<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><strong>O que faz na pr\u00e1tica<\/strong><\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>O que faz na pr\u00e1tica<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><code>localStorage.getItem('theme')<\/code><\/td><td class=\"has-text-align-center\" data-align=\"center\">L\u00ea do navegador se o usu\u00e1rio j\u00e1 salvou o tema no passado.<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Prefer\u00eancia do Sistema<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><code>window.matchMedia('(prefers-color-scheme: dark)')<\/code><\/td><td class=\"has-text-align-center\" data-align=\"center\">Se n\u00e3o houver nada salvo, detecta se o celular\/PC do usu\u00e1rio j\u00e1 usa modo escuro.<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Troca de \u00cdcones<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><code>.classList.toggle('hidden')<\/code><\/td><td class=\"has-text-align-center\" data-align=\"center\">Alterna a visibilidade do Sol e da Lua sem usar bibliotecas externas.<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Menu Hamb\u00farguer<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\"><code>mobileMenu.classList.toggle('hidden')<\/code><\/td><td class=\"has-text-align-center\" data-align=\"center\">Exibe ou esconde o dropdown ao clicar no bot\u00e3o no celular.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Destaques de Execu\u00e7\u00e3o T\u00e9cnica<\/h2>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udca1 <strong>Dica de Ouro:<\/strong> Coloque o trecho que verifica a prefer\u00eancia do tema no <code>&lt;head&gt;<\/code> do seu HTML (ou execute antes do carregamento do body). Isso garante que a classe <code>dark<\/code> seja injetada na tag <code>&lt;html&gt;<\/code> antes mesmo da renderiza\u00e7\u00e3o visual, eliminando completamente a piscada de luz ao trocar de p\u00e1gina.<\/p>\n<\/blockquote>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">O Poder do <code>matchMedia<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Muitos desenvolvedores esquecem que o usu\u00e1rio j\u00e1 configurou o pr\u00f3prio sistema operacional com seu tema preferido. Ao usar <code>window.matchMedia<\/code>, seu blog respeita a escolha global do usu\u00e1rio logo no primeiro acesso.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">C\u00f3digo Enxerto e Perform\u00e1tico<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00e3o utilizamos bibliotecas pesadas de terceiros para controlar a l\u00f3gica da p\u00e1gina. Com menos de 50 linhas de JavaScript puro, garantimos m\u00e1xima velocidade de carregamento e pontua\u00e7\u00e3o alta no Google Lighthouse.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclus\u00e3o<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Com este script, o header do projeto <strong>AqGoEs DeV<\/strong> passa a ser interativo, acess\u00edvel e otimizado para a experi\u00eancia do usu\u00e1rio em qualquer tipo de tela ou momento do dia.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>No artigo anterior, vimos como estruturar o visual HTML e Tailwind CSS do header do AqGoEs DeV. No entanto, um layout limpo s\u00f3 cumpre seu papel se a experi\u00eancia do usu\u00e1rio for fluida. Nesta etapa, vamos adicionar a camada de comportamento (interatividade) com JavaScript puro (Vanilla JS). Vamos resolver dois problemas vitais de front-end: o [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":135,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[64],"tags":[19],"class_list":["post-134","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desenvolvimento-web","tag-front-end"],"_links":{"self":[{"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/posts\/134","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/comments?post=134"}],"version-history":[{"count":2,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/posts\/134\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/posts\/134\/revisions\/160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/media\/135"}],"wp:attachment":[{"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/media?parent=134"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/categories?post=134"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/aqgoes.com\/blog\/wp-json\/wp\/v2\/tags?post=134"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}