colorModeBootstrap.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /*!
  2. * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
  3. * Copyright 2011-2026 The Bootstrap Authors
  4. * Licensed under the Creative Commons Attribution 3.0 Unported License.
  5. */
  6. (() => {
  7. 'use strict'
  8. const getStoredTheme = () => localStorage.getItem('theme')
  9. const setStoredTheme = theme => localStorage.setItem('theme', theme)
  10. const getPreferredTheme = () => {
  11. const storedTheme = getStoredTheme()
  12. if (storedTheme) {
  13. return storedTheme
  14. }
  15. return 'auto'
  16. }
  17. const resolveTheme = theme => {
  18. if (theme === 'auto') {
  19. return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
  20. }
  21. return theme
  22. }
  23. const setTheme = theme => {
  24. const resolved = resolveTheme(theme)
  25. if (document.documentElement.getAttribute('data-bs-theme') !== resolved) {
  26. document.documentElement.setAttribute('data-bs-theme', resolved)
  27. }
  28. }
  29. setTheme(getPreferredTheme())
  30. const showActiveTheme = (theme, focus = false) => {
  31. const themeSwitcher = document.querySelector('#bd-theme')
  32. if (!themeSwitcher) {
  33. return
  34. }
  35. const activeThemeIcon = document.querySelector('.theme-icon-active use')
  36. const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
  37. const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href')
  38. document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
  39. element.classList.remove('active')
  40. element.setAttribute('aria-pressed', 'false')
  41. })
  42. btnToActive.classList.add('active')
  43. btnToActive.setAttribute('aria-pressed', 'true')
  44. activeThemeIcon.setAttribute('href', svgOfActiveBtn)
  45. themeSwitcher.setAttribute('aria-label', `Toggle theme (${btnToActive.dataset.bsThemeValue})`)
  46. if (focus) {
  47. themeSwitcher.focus()
  48. }
  49. }
  50. if (!window.__colorModeMediaBound) {
  51. window.__colorModeMediaBound = true;
  52. window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
  53. const storedTheme = getStoredTheme()
  54. if (storedTheme !== 'light' && storedTheme !== 'dark') {
  55. setTheme(getPreferredTheme())
  56. }
  57. })
  58. }
  59. const initColorMode = () => {
  60. setTheme(getPreferredTheme())
  61. showActiveTheme(getPreferredTheme())
  62. document.querySelectorAll('[data-bs-theme-value]')
  63. .forEach(toggle => {
  64. if (toggle.dataset.colorModeBound) {
  65. return
  66. }
  67. toggle.dataset.colorModeBound = 'true'
  68. toggle.addEventListener('click', () => {
  69. const theme = toggle.getAttribute('data-bs-theme-value')
  70. setStoredTheme(theme)
  71. requestAnimationFrame(() => {
  72. setTheme(theme)
  73. showActiveTheme(theme)
  74. })
  75. })
  76. })
  77. }
  78. initColorMode()
  79. document.addEventListener('turbo:load', initColorMode)
  80. })()