webui: dark mode support to demo fmwk, tool cards
Demo framework fixes:
- sketch-push-button.demo.ts: add dark variants for bg, border, text colors
- chat-input.ts: add dark variants for message containers and status sections
- demo-runner.ts: replace inline error styles with Tailwind dark mode classes
- sketch-call-status.demo.ts: fix hardcoded white backgrounds in status cards
- sketch-diff-range-picker.demo.ts: add dark variants to picker and status displays
- sketch-timeline-message.demo.ts: fix message container backgrounds
- sketch-view-mode-select.demo.ts: comprehensive dark mode for all scenarios
Tool card fixes:
- Update shared createPreElement function with dark:bg-gray-700/dark:text-gray-100
- bash tool: fix command display and result areas
- think tool: fix input content area with proper dark background
- patch tool: comprehensive diff rendering with dark variants for added/removed/context lines
- codereview tool: inherits dark mode through shared utilities
All components now use consistent dark mode patterns with proper contrast:
bg-white dark:bg-gray-800, border-gray-200 dark:border-gray-700,
text-gray-600 dark:text-gray-300, matching existing components.
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s8ac5253d0cbaa3ack
diff --git a/webui/src/web-components/demo/chat-input.ts b/webui/src/web-components/demo/chat-input.ts
index 7ddcc7e..ca72939 100644
--- a/webui/src/web-components/demo/chat-input.ts
+++ b/webui/src/web-components/demo/chat-input.ts
@@ -26,7 +26,7 @@
const messagesDiv = document.createElement("div");
messagesDiv.id = "chat-messages";
messagesDiv.className =
- "min-h-[100px] max-h-[200px] overflow-y-auto border border-gray-300 rounded-md p-3 mb-3 bg-gray-50";
+ "min-h-[100px] max-h-[200px] overflow-y-auto border border-gray-300 dark:border-gray-600 rounded-md p-3 mb-3 bg-gray-50 dark:bg-gray-900";
// Create chat input
const chatInput = document.createElement("sketch-chat-input") as any;
@@ -41,7 +41,7 @@
messageDiv.className = `p-2 my-1 rounded max-w-xs ${
isUser
? "bg-blue-500 text-white ml-auto"
- : "bg-gray-200 text-gray-900 mr-auto"
+ : "bg-gray-200 dark:bg-gray-700 text-gray-900 dark:text-gray-100 mr-auto"
}`;
const timeStr = timestamp
@@ -113,7 +113,7 @@
const statusDiv = document.createElement("div");
statusDiv.className =
- "bg-blue-50 border border-blue-200 rounded p-3 text-sm";
+ "bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded p-3 text-sm text-blue-800 dark:text-blue-200";
statusDiv.innerHTML = `
<div>✓ Drag and drop files onto the chat input</div>
<div>✓ Paste images from clipboard</div>
diff --git a/webui/src/web-components/demo/demo-framework/demo-runner.ts b/webui/src/web-components/demo/demo-framework/demo-runner.ts
index 6b59c38..680651b 100644
--- a/webui/src/web-components/demo/demo-framework/demo-runner.ts
+++ b/webui/src/web-components/demo/demo-framework/demo-runner.ts
@@ -195,19 +195,12 @@
*/
private showError(message: string, error: any): void {
this.container.innerHTML = `
- <div style="
- padding: 20px;
- background: #fee;
- border: 1px solid #fcc;
- border-radius: 4px;
- color: #800;
- font-family: monospace;
- ">
- <h3>Demo Error</h3>
- <p><strong>${message}</strong></p>
- <details>
- <summary>Error Details</summary>
- <pre>${error.stack || error.message || error}</pre>
+ <div class="p-5 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded text-red-800 dark:text-red-200 font-mono">
+ <h3 class="text-lg font-semibold mb-2">Demo Error</h3>
+ <p class="mb-4"><strong>${message}</strong></p>
+ <details class="text-sm">
+ <summary class="cursor-pointer hover:text-red-600 dark:hover:text-red-300">Error Details</summary>
+ <pre class="mt-2 p-2 bg-red-100 dark:bg-red-800/30 rounded text-xs overflow-auto">${error.stack || error.message || error}</pre>
</details>
</div>
`;
diff --git a/webui/src/web-components/demo/sketch-call-status.demo.ts b/webui/src/web-components/demo/sketch-call-status.demo.ts
index d17e2f0..91502dd 100644
--- a/webui/src/web-components/demo/sketch-call-status.demo.ts
+++ b/webui/src/web-components/demo/sketch-call-status.demo.ts
@@ -39,8 +39,8 @@
label: string,
) => {
const wrapper = document.createElement("div");
- wrapper.style.cssText =
- "margin: 15px 0; padding: 10px; border: 1px solid #e1e5e9; border-radius: 6px; background: white;";
+ wrapper.className =
+ "my-4 p-3 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800";
const labelEl = document.createElement("h4");
labelEl.textContent = label;
@@ -215,8 +215,8 @@
statusVariationsSection.appendChild(workingDisconnectedStatus);
const interactiveWrapper = document.createElement("div");
- interactiveWrapper.style.cssText =
- "padding: 10px; border: 1px solid #e1e5e9; border-radius: 6px; background: white;";
+ interactiveWrapper.className =
+ "p-3 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800";
interactiveWrapper.appendChild(interactiveStatus);
interactiveWrapper.appendChild(controlsDiv);
interactiveSection.appendChild(interactiveWrapper);
diff --git a/webui/src/web-components/demo/sketch-diff-range-picker.demo.ts b/webui/src/web-components/demo/sketch-diff-range-picker.demo.ts
index 0ce1358..f70f72f 100644
--- a/webui/src/web-components/demo/sketch-diff-range-picker.demo.ts
+++ b/webui/src/web-components/demo/sketch-diff-range-picker.demo.ts
@@ -30,14 +30,9 @@
const rangePickerElement = document.createElement(
"sketch-diff-range-picker",
);
- rangePickerElement.style.cssText = `
- width: 100%;
- max-width: 800px;
- margin: 20px 0;
- padding: 16px;
- border: 1px solid #e0e0e0;
- border-radius: 8px;
- background: white;
+ rangePickerElement.className = `
+ w-full max-w-3xl my-5 p-4 border border-gray-300 dark:border-gray-600
+ rounded-lg bg-white dark:bg-gray-800
`;
// Set up the git service
@@ -45,15 +40,9 @@
// Create status display
const statusDisplay = document.createElement("div");
- statusDisplay.style.cssText = `
- padding: 12px;
- margin: 16px 0;
- background: var(--demo-fixture-section-bg);
- border-radius: 6px;
- border: 1px solid #e9ecef;
- font-family: monospace;
- font-size: 14px;
- line-height: 1.4;
+ statusDisplay.className = `
+ p-3 my-4 bg-gray-50 dark:bg-gray-800 rounded border
+ border-gray-200 dark:border-gray-700 font-mono text-sm leading-relaxed
`;
statusDisplay.innerHTML = `
<div><strong>Status:</strong> No range selected</div>
@@ -84,12 +73,9 @@
// Add some demo instructions
const instructionsDiv = document.createElement("div");
- instructionsDiv.style.cssText = `
- margin: 20px 0;
- padding: 16px;
- background: var(--demo-instruction-bg);
- border-radius: 6px;
- border-left: 4px solid #2196f3;
+ instructionsDiv.className = `
+ my-5 p-4 bg-blue-50 dark:bg-blue-900/20 rounded
+ border-l-4 border-blue-500 dark:border-blue-400
`;
instructionsDiv.innerHTML = `
<h3 style="margin: 0 0 8px 0; color: #1976d2;">Demo Instructions:</h3>
diff --git a/webui/src/web-components/demo/sketch-push-button.demo.ts b/webui/src/web-components/demo/sketch-push-button.demo.ts
index f7b83fd..4405a9e 100644
--- a/webui/src/web-components/demo/sketch-push-button.demo.ts
+++ b/webui/src/web-components/demo/sketch-push-button.demo.ts
@@ -12,18 +12,20 @@
render() {
return html`
<div
- class="p-4 bg-white rounded-lg shadow-sm border border-gray-200 max-w-md mx-auto"
+ class="p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 max-w-md mx-auto"
>
- <h2 class="text-lg font-semibold mb-4">Push Button Demo</h2>
+ <h2 class="text-lg font-semibold mb-4 text-gray-900 dark:text-gray-100">
+ Push Button Demo
+ </h2>
<div class="mb-4">
- <p class="text-sm text-gray-600 mb-2">
+ <p class="text-sm text-gray-600 dark:text-gray-300 mb-2">
Test the push button component:
</p>
<sketch-push-button></sketch-push-button>
</div>
- <div class="text-xs text-gray-500">
+ <div class="text-xs text-gray-500 dark:text-gray-400">
<p>Click the push button to test:</p>
<ul class="list-disc list-inside mt-1">
<li>Modal opens with git information</li>
diff --git a/webui/src/web-components/demo/sketch-timeline-message.demo.ts b/webui/src/web-components/demo/sketch-timeline-message.demo.ts
index 68f60c4..a101101 100644
--- a/webui/src/web-components/demo/sketch-timeline-message.demo.ts
+++ b/webui/src/web-components/demo/sketch-timeline-message.demo.ts
@@ -45,8 +45,8 @@
state = mockState,
) => {
const wrapper = document.createElement("div");
- wrapper.style.cssText =
- "margin: 15px 0; padding: 15px; border: 1px solid #e1e5e9; border-radius: 6px; background: white;";
+ wrapper.className =
+ "my-4 p-4 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800";
const labelEl = document.createElement("h4");
labelEl.textContent = label;
@@ -178,8 +178,8 @@
interactiveMessage.open = true;
const interactiveWrapper = document.createElement("div");
- interactiveWrapper.style.cssText =
- "padding: 15px; border: 1px solid #e1e5e9; border-radius: 6px; background: white;";
+ interactiveWrapper.className =
+ "p-4 border border-gray-200 dark:border-gray-700 rounded bg-white dark:bg-gray-800";
interactiveWrapper.appendChild(interactiveMessage);
// Control buttons for interactive demo
diff --git a/webui/src/web-components/demo/sketch-view-mode-select.demo.ts b/webui/src/web-components/demo/sketch-view-mode-select.demo.ts
index 3688b30..71354bc 100644
--- a/webui/src/web-components/demo/sketch-view-mode-select.demo.ts
+++ b/webui/src/web-components/demo/sketch-view-mode-select.demo.ts
@@ -50,13 +50,9 @@
// Status display for basic selector
const basicStatus = document.createElement("div");
basicStatus.id = "basic-status";
- basicStatus.style.cssText = `
- margin-top: 15px;
- padding: 10px;
- background: #f6f8fa;
- border-radius: 6px;
- font-family: monospace;
- font-size: 14px;
+ basicStatus.className = `
+ mt-4 p-3 bg-gray-50 dark:bg-gray-800 rounded font-mono text-sm
+ text-gray-900 dark:text-gray-100
`;
const updateBasicStatus = () => {
@@ -86,11 +82,9 @@
viewModeScenarios.forEach((scenario) => {
const scenarioCard = document.createElement("div");
- scenarioCard.style.cssText = `
- padding: 15px;
- border: 1px solid #d0d7de;
- border-radius: 8px;
- background: white;
+ scenarioCard.className = `
+ p-4 border border-gray-300 dark:border-gray-600 rounded-lg
+ bg-white dark:bg-gray-800
`;
const scenarioTitle = document.createElement("h4");
@@ -134,7 +128,7 @@
// Status display for interactive selector
const interactiveStatus = document.createElement("div");
interactiveStatus.id = "interactive-status";
- interactiveStatus.style.cssText = basicStatus.style.cssText;
+ interactiveStatus.className = basicStatus.className;
const updateInteractiveStatus = () => {
interactiveStatus.innerHTML = `
@@ -159,11 +153,8 @@
// Custom controls for interactive testing
const customControls = document.createElement("div");
- customControls.style.cssText = `
- margin: 15px 0;
- padding: 15px;
- background: #f6f8fa;
- border-radius: 6px;
+ customControls.className = `
+ my-4 p-4 bg-gray-50 dark:bg-gray-800 rounded
`;
const addLinesButton = demoUtils.createButton("Add +5 Lines", () => {
@@ -258,11 +249,8 @@
containerExamples.forEach((example) => {
// Create container wrapper
const wrapper = document.createElement("div");
- wrapper.style.cssText = `
- border: 2px solid;
- border-radius: 8px;
- padding: 15px;
- background: #f9f9f9;
+ wrapper.className = `
+ border-2 rounded-lg p-4 bg-gray-50 dark:bg-gray-800 ${example.borderColor}
`;
wrapper.className = example.borderColor;
@@ -278,12 +266,9 @@
// Create constrained container for the component
const componentContainer = document.createElement("div");
componentContainer.className = "@container";
- componentContainer.style.cssText = `
- width: ${example.width};
- border: 1px dashed #ccc;
- padding: 10px;
- background: white;
- border-radius: 4px;
+ componentContainer.className = `
+ border border-dashed border-gray-400 dark:border-gray-600 p-3
+ bg-white dark:bg-gray-800 rounded ${example.containerClass}
`;
// Create the component
@@ -317,13 +302,9 @@
// Create interactive container
const interactiveContainer = document.createElement("div");
interactiveContainer.className = "@container";
- interactiveContainer.style.cssText = `
- border: 2px solid #007acc;
- border-radius: 8px;
- padding: 15px;
- background: #f0f8ff;
- transition: width 0.3s ease;
- width: 700px;
+ interactiveContainer.className = `
+ @container border-2 border-blue-600 dark:border-blue-400 rounded-lg p-4
+ bg-blue-50 dark:bg-blue-900/20 transition-all duration-300 w-[700px]
`;
// Create interactive component
diff --git a/webui/src/web-components/sketch-tool-card.ts b/webui/src/web-components/sketch-tool-card.ts
index c96d8e0..881285a 100644
--- a/webui/src/web-components/sketch-tool-card.ts
+++ b/webui/src/web-components/sketch-tool-card.ts
@@ -63,7 +63,7 @@
// Shared utility function for creating Tailwind pre elements
function createPreElement(content: string, additionalClasses: string = "") {
return html`<pre
- class="bg-gray-200 text-black p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-wrap-break-word ${additionalClasses}"
+ class="bg-gray-200 dark:bg-gray-700 text-black dark:text-gray-100 p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-wrap-break-word ${additionalClasses}"
>
${content}</pre
>`;
@@ -101,7 +101,7 @@
>
<div class="w-full relative">
<pre
- class="bg-gray-200 text-black p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-wrap-break-word w-full mb-0 rounded-t rounded-b-none box-border"
+ class="bg-gray-200 dark:bg-gray-700 text-black dark:text-gray-100 p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-wrap-break-word w-full mb-0 rounded-t rounded-b-none box-border"
>
${backgroundIcon}${slowIcon}${inputData?.command}</pre
>
@@ -211,19 +211,29 @@
const coloredLines = lines.map((line) => {
if (line.startsWith("+")) {
- return html`<div class="text-green-600 bg-green-50">${line}</div>`;
+ return html`<div
+ class="text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-900/20"
+ >
+ ${line}
+ </div>`;
} else if (line.startsWith("-")) {
- return html`<div class="text-red-600 bg-red-50">${line}</div>`;
+ return html`<div
+ class="text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20"
+ >
+ ${line}
+ </div>`;
} else if (line.startsWith("@@")) {
// prettier-ignore
- return html`<div class="text-cyan-600 bg-cyan-50 font-semibold">${line}</div>`;
+ return html`<div class="text-cyan-600 dark:text-cyan-400 bg-cyan-50 dark:bg-cyan-900/20 font-semibold">${line}</div>`;
} else {
- return html`<div class="text-gray-800">${line}</div>`;
+ return html`<div class="text-gray-800 dark:text-gray-200">
+ ${line}
+ </div>`;
}
});
return html`<pre
- class="bg-gray-100 text-xs p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-x-auto font-mono"
+ class="bg-gray-100 dark:bg-gray-800 text-xs p-2 rounded whitespace-pre-wrap break-words max-w-full w-full box-border overflow-x-auto font-mono text-gray-900 dark:text-gray-100"
>
${coloredLines}
</pre
@@ -285,7 +295,7 @@
</span>`;
const inputContent = html`<div
- class="overflow-x-auto mb-1 font-mono px-2 py-1 bg-gray-200 rounded select-text cursor-text text-sm leading-relaxed"
+ class="overflow-x-auto mb-1 font-mono px-2 py-1 bg-gray-200 dark:bg-gray-700 rounded select-text cursor-text text-sm leading-relaxed text-gray-900 dark:text-gray-100"
>
<div class="markdown-content">
${unsafeHTML(renderMarkdown(thoughts))}