Skip to content

Avoid universal selector (*) for theme-switch transitions in dark-mode.sass #736

Description

@Pranav-IIITM

Problem

assets/sass/dark-mode.sass (lines 387, 396) applies transitions to background-color, color, and border-color using the universal selector *:

[data-theme="dark"],
[data-theme="light"]
  transition: background-color 0.3s ease, color 0.3s ease

  *
    transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease

This forces the browser to compute transition logic for every DOM node on theme toggle, including elements that never change color (wrappers, spans, hidden elements, SVGs). This is a known CSS performance anti-pattern and can cause jank/frame drops on complex pages or low-end devices.

Proposed fix

Replace * with an explicit list of elements/classes that actually change color on theme switch:

body, .navbar, .navbar-menu, .navbar-dropdown, .navbar-item, .navbar-link, .navbar-burger span, .footer, .hero, .card, .card-content, .button, code, pre, .content, a, .table, th, tr, td, .menu-list a, .tag, .modal-content, hr, input.input, #search-bar, .section, .column, .list-item, h1, h2, h3, h4, h5, h6, .title, .subtitle
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease

Same change applies to the prefers-reduced-motion block using *.

Why this matters

  • Performance cost scales with page size. The * selector doesn't just target visible, styled elements — it matches every node in the DOM, including deeply nested wrappers, hidden elements, SVG internals, and third-party embeds. On content-heavy pages, this can mean thousands of elements being touched on a single toggle.
  • Real, measurable jank. Applying transitions to background-color, color, and border-color on every element forces the browser to run style recalculation and repaint across the full render tree. On low-end/mobile devices this is visible as stutter or dropped frames during what should be an instant, lightweight UI action.
  • It defeats the purpose of prefers-reduced-motion. Users who've explicitly opted out of animations still get transitions computed for the entire DOM under this rule — the accessibility intent is currently undermined by the same anti-pattern.
  • It grows silently over time. As the site adds more components, sections, or third-party widgets, this selector automatically absorbs all of them into the transition — meaning the performance cost increases without any recognition, and without appearing in any diff as a red flag.
  • Low effort, high payoff fix. This isn't a rearchitecture — it's a scoped selector swap. The visual behavior (smooth theme transition) is fully preserved; only the unnecessary browser workload is removed. It's a quick, safe win for site responsiveness with virtually no downside or regression risk.
  • Best-practice alignment. Scoping transitions to only the elements that actually change is standard CSS performance guidance (avoid * in transition/animation rules), and adopting it here brings the codebase in line with that convention going forward.

Impact

Eliminates unnecessary style recalculation overhead across the entire DOM on every theme switch, scoping transitions only to elements that need them.

Happy to open a PR with this fix if it looks good.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions