Skip to content

feature: implement dark mode support for webstatus.dev#2280

Open
jcscottiii wants to merge 1 commit intojcscottiii/skillsfrom
jcscottiii/light-dark-mode
Open

feature: implement dark mode support for webstatus.dev#2280
jcscottiii wants to merge 1 commit intojcscottiii/skillsfrom
jcscottiii/light-dark-mode

Conversation

@jcscottiii
Copy link
Collaborator

@jcscottiii jcscottiii commented Feb 26, 2026

This commit implements dark mode support for webstatus.dev, allowing users to switch between light and dark themes for a more personalized browsing experience. The implementation includes:

  • A new theme context to manage the current theme state across the application.
  • A theme toggle button in the header to allow users to switch themes easily.
  • Updated CSS to support both light and dark themes, ensuring a consistent look and feel across the site.
  • Updated end-to-end tests to verify that the dark mode functionality works correctly and that the UI renders appropriately in both themes.

Fixes #744

This commit implements dark mode support for webstatus.dev, allowing users to switch between light and dark themes for a more personalized browsing experience. The implementation includes:
- A new theme context to manage the current theme state across the application.
- A theme toggle button in the header to allow users to switch themes easily.
- Updated CSS to support both light and dark themes, ensuring a consistent look and feel across the site.
- Updated end-to-end tests to verify that the dark mode functionality works correctly and that the UI renders appropriately in both themes.

Fixes 744
@jcscottiii jcscottiii linked an issue Feb 26, 2026 that may be closed by this pull request
@jcscottiii
Copy link
Collaborator Author

This was my first time using Antigravity and I was pleasantly surprised!

@jcscottiii
Copy link
Collaborator Author

I did ask it why not use the new light-dark feature, it said the following:

That is a great question. The short answer is: It would be technically easy to use, but arguably redundant given your current architectural reliance on Shoelace.

Here is a breakdown of how it would interact with your codebase:

How it works with Shoelace:

The light-dark() function depends entirely on the CSS color-scheme property. For it to work, an element (or its ancestor) must have color-scheme: light or color-scheme: dark set.

Since your WebstatusThemeService toggles the .sl-theme-dark class on the root, and Shoelace's dark.css included in your project automatically sets color-scheme: dark on that class, light-dark() would "just work" inside any of your components.

/* You could do this in _theme-css.ts */
:host {
  --color-background: light-dark(white, var(--sl-color-gray-950));
}

Pros: Why you might want it

Conciseness: It eliminates the need for separate :host and .sl-theme-dark blocks for simple color switches.
Native Support: It's a standard CSS way to handle the exact problem we were solving.
Hierarchy Awareness: If you ever had a "dark mode section" inside a "light mode page" (by just setting color-scheme: dark on a wrapper), light-dark() would adapt automatically without needing a class toggle.

Cons: Why it might be "not really" easy/useful right now

Redundancy with Shoelace: You are already mapping to Shoelace variables like --sl-color-neutral-0. These variables already change their values when the theme toggles. Using light-dark() on top of them is essentially doing the same work twice.
Maintainability: If Shoelace changes their "Gray 950" to a different shade in a future update, your hardcoded light-dark() values would become "out of sync" with the rest of the UI unless you strictly used Shoelace variables inside the light-dark() function.
Browser Support (The Firefox/WebKit concern): While support is now very good (Chrome 123+, Firefox 120+, Safari 17.5+), it is still relatively new. Given that we just spent time fixing issues for older versions of Firefox/WebKit, staying with the "variable inheritance" model is safer for slightly older browsers that might not yet support light-dark() but do support CSS variable inheritance.

Final Thoughts
In your specific project, the Inheritance Model we just implemented (mapping custom variables to Shoelace semantic variables) is actually superior to light-dark().

Why? Because Shoelace is your "Source of Truth." By saying --color-background: var(--sl-color-neutral-0), you are telling your component: "Whatever Shoelace thinks 'Background 0' is, use that." If the user switches themes, Shoelace updates that variable, and your component follows along perfectly across all browsers.

light-dark() is great for projects without a heavy UI library like Shoelace, where you have to manually define every bridge between light and dark.

Copy link
Collaborator

@DanielRyanSmith DanielRyanSmith left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very awesome! Excited to try this out (Antigravity and this dark mode) 🙂

I would love to see your prompt process on this (That gives me an idea that I should probably add my prompts to my PRs as well)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The "Page not found" is a little hard to read. This could be fixed by just increasing the contrast of the color from the background.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The "+ Create ..." buttons are a little hard to read. This could be fixed by just increasing the contrast of the text color from the background.

@jcscottiii
Copy link
Collaborator Author

This is very awesome! Excited to try this out (Antigravity and this dark mode) 🙂

I would love to see your prompt process on this (That gives me an idea that I should probably add my prompts to my PRs as well)

I asked it this:

Alright. A teammate wants to learn how to use you more effectively. Could you provide a breakdown of our conversation so they could learn how we started, what things you did. what things I had to fix etc. This will help my teammates know how to better use AI. Feel free to put all of that into a file named recap-darkmode.md. Can you also include any prompts I used? So they know how to get started. 

It said the following:

Recap: Dark Mode Implementation & Collaboration Journey

This document captures the "behind the scenes" of our session and serves as a guide for how to effectively pair-program with me (the AI) on complex frontend tasks.

1. How We Started: Feature Implementation

The goal was to implement a global light/dark mode for webstatus.dev that correctly themed custom components and Google Charts.

  • What I Did well: Analyzed the Shoelace theme system (setting .sl-theme-dark on the root) and integrated it with WebstatusThemeService. I also successfully refactored the Google Charts component to bridge the gap between CSS variables and its internal JavaScript-based rendering.
  • Key Lesson: Provide me with your existing Makefile or README early. Knowing that make test and make dev_fake_data exist allowed me to verify my work in a "real" environment.

2. Prompts that worked (How to get started)

Here are the specific prompts from this session that were most effective:

Phase 1: The Initial Request

"Implement light/dark mode for the whole site. Make it look great. Switcher in the header. Use Shoelace themes. Google charts need to be dark too."

Phase 2: High-Level Debugging

"Dark mode is not working on Firefox or WebKit. Background is white, text is invisible. High priority fix."

Phase 3: Build Error Correction

"Build fails with this error: [!] RollupError: Could not resolve '../css/main.css' from 'build/static/js/index.js'. Not sure why you're trying to use the css file."

Phase 4: Steering & Planning

"Give your plan first. I shouldn't have to do anything crazy like move stuff into index.html."

Phase 5: Knowledge Transfer

"A teammate wants to learn how to use you more effectively. Could you provide a breakdown of our conversation so they could learn how we started..."


3. The "Wall": Cross-Browser Specificity

We discovered that while dark mode worked in Chrome, it failed in Firefox and WebKit (Safari). Backgrounds stayed white, and text was invisible.

  • The Investigation: We identified the cause—our use of :host-context(.sl-theme-dark) in _theme-css.ts. Firefox and WebKit don't support this selector well inside Shadow DOM.
  • AI Pivot (The Mistake): I initially tried to move theme variables to a global main.css. This broke the Rollup build because I didn't account for how the build pipeline resolves relative paths from the build/ directory.

3. The Power of User Pushback

This was the most important part of our collaboration. When the build failed, I proposed "moving everything into index.html."

  • The User's Correct "Nudge": You said: "Give your plan first. I shouldn't have to do anything crazy like move stuff into index.html."
  • The Result: This forced me to take a step back and stop "throwing code at the problem." I realized we already had the solution: Variable Inheritance.
  • The Lesson: If I propose something that feels like an "architectural hack," push back. Ask for a different plan. I often find a cleaner path when constrained.

4. The Clean Solution: Semantic Mapping

Instead of forcing overrides, we updated _theme-css.ts to map our custom variables (e.g., --color-background) directly to Shoelace semantic variables (e.g., --sl-color-neutral-0).

  • Why it works: Shoelace variables already auto-toggle at the root level. By inheriting them, our components "magically" work across all browsers without needing complex selectors or global CSS files.
  • Lesson: Leverage your existing library's "brain" (in this case, Shoelace) rather than building a parallel system.

5. Architectural Formalization (The "Pro" Move)

At the end, we didn't just fix the code; we fixed the Knowledge Base.

  • Service Containers: We codified that all global providers must live in WebstatusServicesContainer.
  • Context Bridges: We created small, testable "Consumer" components (like WebstatusThemeButton) to bridge context into large, slotted components like the Header.
  • Variable Abstraction: We added a rule to the Frontend Skill that components should never use --sl- variables directly—they must use our project-specific variables, which are then mapped globally.

Summary: How to use AI effectively

  1. Context is King: Keep your GEMINI.md and Skills files updated. They are my "long-term memory."
  2. The "Plan First" Rule: For complex bugs, ask me to explain the plan before I write a single line of code.
  3. Constrain the AI: If a solution feels "too crazy," it probably is. Redirect me toward simpler, more idiomatic project patterns.
  4. Verification is non-negotiable: Always have me run your unit/E2E tests (make test). I am much more reliable when I have to prove my code works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENHANCEMENT] implement dark mode

2 participants