webui: add dark mode support to demo server
Core Component Dark Mode Support:
- sketch-timeline.ts: Welcome box, loading indicators, thinking bubbles, navigation
- sketch-tool-card-base.ts: Status icons, elapsed time, hover states, details panel
- All 14 sketch-tool-card-* components: Consistent dark styling for tool results
Demo System Infrastructure:
- Enhanced demo runner (demo.html) with complete dark theme CSS variables
- Added sketch-theme-toggle integration in sidebar for easy theme switching
- Extended demo-fixtures utilities with semantic color system (8 new CSS variables)
- Comprehensive color mappings: backgrounds, text, borders, controls, buttons
Demo File Compatibility (13 files updated):
- Fixed 60+ instances of hardcoded colors across all demo components
- Replaced light-mode-only colors (#24292f, #f6f8fa, etc.) with CSS variables
- Updated text colors, backgrounds, borders for proper contrast in both themes
- Maintained visual hierarchy while ensuring accessibility
Technical Implementation:
- CSS custom properties system with automatic :root/.dark theme switching
- GitHub-inspired dark color palette for professional appearance
- Smooth 0.2s transitions for seamless theme changes
- Semantic variable naming for maintainability and consistency
Key Features Added:
- Theme toggle accessible from any demo (no need to navigate to Theme Toggle demo)
- Complete visual consistency between light and dark modes
- Proper contrast ratios throughout for accessibility
- Tool card demos showcase dark mode styling with realistic content
Components Updated:
Timeline: Welcome messages, loading states, thinking indicators, jump button
Tool Cards: Status icons, input/output display, hover states, detailed views
Demos: Labels, backgrounds, instruction panels, control elements, text content
The demo system now provides a complete, professional dark mode experience
that matches modern development tool standards with excellent usability
and visual consistency across all components and demonstrations.
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s97589e2fe2fdeeb3k
diff --git a/webui/src/web-components/sketch-timeline.ts b/webui/src/web-components/sketch-timeline.ts
index d6928c7..926c0d2 100644
--- a/webui/src/web-components/sketch-timeline.ts
+++ b/webui/src/web-components/sketch-timeline.ts
@@ -811,31 +811,39 @@
class="overflow-y-auto overflow-x-hidden pl-4 max-w-full w-full h-full ${compactClass} scroll-container print:h-auto print:max-h-none print:overflow-visible"
>
<div
- class="my-8 mx-auto max-w-[90%] w-[90%] p-8 border-2 border-gray-300 rounded-lg shadow-sm bg-white text-center print:break-inside-avoid"
+ class="my-8 mx-auto max-w-[90%] w-[90%] p-8 border-2 border-gray-300 dark:border-gray-600 rounded-lg shadow-sm bg-white dark:bg-gray-800 text-center print:break-inside-avoid"
data-testid="welcome-box"
>
<h2
- class="text-2xl font-semibold mb-6 text-center text-gray-800"
+ class="text-2xl font-semibold mb-6 text-center text-gray-800 dark:text-gray-100"
data-testid="welcome-box-title"
>
How to use Sketch
</h2>
- <p class="text-gray-600 leading-relaxed text-base text-left">
+ <p
+ class="text-gray-600 dark:text-gray-300 leading-relaxed text-base text-left"
+ >
Sketch is an agentic coding assistant.
</p>
- <p class="text-gray-600 leading-relaxed text-base text-left">
+ <p
+ class="text-gray-600 dark:text-gray-300 leading-relaxed text-base text-left"
+ >
Sketch has created a container with your repo.
</p>
- <p class="text-gray-600 leading-relaxed text-base text-left">
+ <p
+ class="text-gray-600 dark:text-gray-300 leading-relaxed text-base text-left"
+ >
Ask it to implement a task or answer a question in the chat box
below. It can edit and run your code, all in the container.
Sketch will create commits in a newly created git branch, which
you can look at and comment on in the Diff tab. Once you're
done, you'll find that branch available in your (original) repo.
</p>
- <p class="text-gray-600 leading-relaxed text-base text-left">
+ <p
+ class="text-gray-600 dark:text-gray-300 leading-relaxed text-base text-left"
+ >
Because Sketch operates a container per session, you can run
Sketch in parallel to work on multiple ideas or even the same
idea with different approaches.
@@ -871,11 +879,11 @@
${!this.isInitialLoadComplete
? html`
<div
- class="flex items-center justify-center p-5 text-gray-600 text-sm gap-2.5 opacity-100 print:hidden"
+ class="flex items-center justify-center p-5 text-gray-600 dark:text-gray-400 text-sm gap-2.5 opacity-100 print:hidden"
data-testid="loading-indicator"
>
<div
- class="w-5 h-5 border-2 border-gray-300 border-t-gray-600 rounded-full loading-spinner"
+ class="w-5 h-5 border-2 border-gray-300 dark:border-gray-600 border-t-gray-600 dark:border-t-gray-300 rounded-full loading-spinner"
data-testid="loading-spinner"
></div>
<span>Loading conversation...</span>
@@ -885,11 +893,11 @@
${this.isLoadingOlderMessages
? html`
<div
- class="flex items-center justify-center p-5 text-gray-600 text-sm gap-2.5 opacity-100 print:hidden"
+ class="flex items-center justify-center p-5 text-gray-600 dark:text-gray-400 text-sm gap-2.5 opacity-100 print:hidden"
data-testid="loading-indicator"
>
<div
- class="w-5 h-5 border-2 border-gray-300 border-t-gray-600 rounded-full loading-spinner"
+ class="w-5 h-5 border-2 border-gray-300 dark:border-gray-600 border-t-gray-600 dark:border-t-gray-300 rounded-full loading-spinner"
data-testid="loading-spinner"
></div>
<span>Loading older messages...</span>
@@ -930,7 +938,7 @@
style="display: flex; padding-left: 85px; margin-top: 6px; margin-bottom: 16px;"
>
<div
- class="bg-gray-100 rounded-2xl px-4 py-2.5 max-w-20 text-black relative rounded-bl-[5px]"
+ class="bg-gray-100 dark:bg-gray-700 rounded-2xl px-4 py-2.5 max-w-20 text-black dark:text-white relative rounded-bl-[5px]"
data-testid="thinking-bubble"
>
<div
@@ -938,15 +946,15 @@
data-testid="thinking-dots"
>
<div
- class="w-1.5 h-1.5 bg-gray-500 rounded-full opacity-60 thinking-dot-1"
+ class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-300 rounded-full opacity-60 thinking-dot-1"
data-testid="thinking-dot"
></div>
<div
- class="w-1.5 h-1.5 bg-gray-500 rounded-full opacity-60 thinking-dot-2"
+ class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-300 rounded-full opacity-60 thinking-dot-2"
data-testid="thinking-dot"
></div>
<div
- class="w-1.5 h-1.5 bg-gray-500 rounded-full opacity-60 thinking-dot-3"
+ class="w-1.5 h-1.5 bg-gray-500 dark:bg-gray-300 rounded-full opacity-60 thinking-dot-3"
data-testid="thinking-dot"
></div>
</div>
@@ -960,7 +968,7 @@
id="jump-to-latest"
class="${this.scrollingState === "floating"
? "block floating"
- : "hidden"} fixed bottom-20 left-1/2 -translate-x-1/2 bg-black/60 text-white border-none rounded-xl px-2 py-1 text-xs font-normal cursor-pointer shadow-md z-[1000] transition-all duration-150 ease-out whitespace-nowrap opacity-80 hover:bg-black/80 hover:-translate-y-0.5 hover:opacity-100 hover:shadow-lg active:translate-y-0 print:hidden"
+ : "hidden"} fixed bottom-20 left-1/2 -translate-x-1/2 bg-black/60 dark:bg-gray-700/80 text-white border-none rounded-xl px-2 py-1 text-xs font-normal cursor-pointer shadow-md z-[1000] transition-all duration-150 ease-out whitespace-nowrap opacity-80 hover:bg-black/80 dark:hover:bg-gray-600/90 hover:-translate-y-0.5 hover:opacity-100 hover:shadow-lg active:translate-y-0 print:hidden"
@click=${this.scrollToBottomWithRetry}
>
↓ Jump to bottom