goharbor / goharbor/website

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

Open Beginner friendly
#736 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
HTML
Stars
46
Forks
222
PR merge metrics
No merged PRs in 30d

Description

## Problem

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

```sass
[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:

```sass
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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in assets/sass/dark-mode.sass around lines 387 and 396, then inspect both the theme-switch rules and the prefers-reduced-motion block. Replace the universal-selector transition rules with the scoped elements listed in the issue, and confirm that theme transitions remain covered while no `*` transition selector remains.

Written by the indexing model from the issue text.

Assessment

Tech stack
sass
Domain
frontend, performance
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.