From 27c7550f283ce70add2568e045080b218cdd69c4 Mon Sep 17 00:00:00 2001 From: ujiro99 Date: Thu, 22 Oct 2026 12:29:12 +0900 Subject: [PATCH 1/2] feat(extension): support dark mode on the options page Apply the theme to the options page and add dark counterparts to its hard-coded neutral colors. The `dark:` classes are added next to the existing ones, so the light theme renders exactly as before. Monochrome icons (generic glyphs, folder SVGs) are drawn in black and vanished on dark backgrounds. In the dark theme they are now recolored like the global icon color setting does, keeping favicons, brand icons and icons excluded from recoloring as they are. This also applies to the popup menu when no popup color is customized. The resolved theme is shared through a context so that each icon does not load the settings. Refs #495 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019DvJ25jdbq4pvn4K8Wk7Mx --- packages/extension/src/components/App.tsx | 36 +++--- .../src/components/Common.module.css | 18 +++ .../extension/src/components/Popup.module.css | 3 +- packages/extension/src/components/Popup.tsx | 17 +-- .../src/components/help/PageActionHelp.tsx | 10 +- .../src/components/menu/MenuImage.tsx | 10 +- .../menu/__tests__/MenuImage.test.tsx | 34 ++++++ .../src/components/option/CopyButton.tsx | 4 +- .../src/components/option/DevTools.tsx | 16 ++- .../components/option/DeveloperSupport.tsx | 7 +- .../src/components/option/Dialog.module.css | 10 ++ .../src/components/option/EditButton.tsx | 4 +- .../components/option/HubBanner.module.css | 6 +- .../src/components/option/HubBanner.tsx | 5 +- .../src/components/option/HubShareToast.tsx | 4 +- .../src/components/option/HubUserInfo.tsx | 8 +- .../src/components/option/ImportExport.tsx | 22 +++- .../src/components/option/Option.module.css | 19 +++- .../src/components/option/Option.tsx | 105 +++++++++--------- .../src/components/option/OptionMenuImage.tsx | 29 +++++ .../components/option/PromptHistoryBanner.tsx | 6 +- .../src/components/option/RemoveButton.tsx | 2 +- .../src/components/option/SettingForm.tsx | 32 ++++-- .../src/components/option/ShareButton.tsx | 6 +- .../src/components/option/SortableItem.tsx | 9 +- .../src/components/option/StorageUsage.tsx | 72 ++++++++---- .../option/TableOfContents.module.css | 2 +- .../src/components/option/TableOfContents.tsx | 6 +- .../src/components/option/UserSupport.tsx | 7 +- .../option/__tests__/OptionMenuImage.test.tsx | 53 +++++++++ .../option/editor/CommandEditDialog.tsx | 8 +- .../option/editor/CommandListMenu.tsx | 8 +- .../option/editor/CommandTreeRenderer.tsx | 10 +- .../components/option/editor/CommandType.tsx | 17 +-- .../editor/CommandTypeSelectionDialog.tsx | 6 +- .../option/editor/PageActionSection.tsx | 7 +- .../components/option/editor/PageRuleList.tsx | 32 +++--- .../option/editor/PopupAnimation.tsx | 2 +- .../option/editor/PopupPlacement.tsx | 2 +- .../components/option/editor/ShortcutList.tsx | 7 +- .../option/editor/UserStyleList.tsx | 4 +- .../src/components/option/field/IconField.tsx | 9 +- .../components/option/field/InfoTooltip.tsx | 2 +- .../components/option/field/InputField.tsx | 4 +- .../option/field/OpenModeToggleField.tsx | 12 +- .../option/field/PopupPlacementField.tsx | 4 +- .../components/option/field/SelectField.tsx | 6 +- .../option/field/UserVariablesField.tsx | 17 ++- .../extension/src/components/ui/button.tsx | 3 +- packages/extension/src/components/ui/form.tsx | 2 +- .../extension/src/components/ui/input.tsx | 2 +- .../extension/src/components/ui/textarea.tsx | 2 +- packages/extension/src/hooks/useTheme.ts | 11 +- 53 files changed, 517 insertions(+), 222 deletions(-) create mode 100644 packages/extension/src/components/option/OptionMenuImage.tsx create mode 100644 packages/extension/src/components/option/__tests__/OptionMenuImage.test.tsx diff --git a/packages/extension/src/components/App.tsx b/packages/extension/src/components/App.tsx index a86907b2c..1f2803e03 100644 --- a/packages/extension/src/components/App.tsx +++ b/packages/extension/src/components/App.tsx @@ -17,7 +17,7 @@ import { TabContextProvider } from "@/providers/TabContextProvider" import { Ipc, TabCommand } from "@/services/ipc" import { Settings } from "@/services/settings/settings" import { ServiceWorkerData } from "@/services/serviceWorkerData" -import { useTheme } from "@/hooks/useTheme" +import { useTheme, themeContext } from "@/hooks/useTheme" import type { ShowToastParam } from "@/types" ServiceWorkerData.init() @@ -31,7 +31,7 @@ export function App({ rootElm }: Props) { // rootElm is the shadow root; the theme attribute goes on its host so that // the `:host()` selectors in the stylesheets can match it. - useTheme(rootElm instanceof ShadowRoot ? rootElm.host : rootElm) + const theme = useTheme(rootElm instanceof ShadowRoot ? rootElm.host : rootElm) useEffect(() => { const handleShowToast = ( @@ -75,20 +75,22 @@ export function App({ rootElm }: Props) { }, []) return ( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + ) } diff --git a/packages/extension/src/components/Common.module.css b/packages/extension/src/components/Common.module.css index 43c8932aa..05ae2b32e 100644 --- a/packages/extension/src/components/Common.module.css +++ b/packages/extension/src/components/Common.module.css @@ -13,6 +13,24 @@ } } +/* + * Composed from other CSS modules (`composes`), which bypasses Tailwind, so + * the dark theme selectors are spelled out instead of `@variant dark`. + */ +:host([data-sc-theme="dark"]) .button, +[data-sc-theme="dark"] .button { + border-color: #4b5563; /* gray-600 */ + background-color: #1f2937; /* gray-800 */ + + &:hover { + background-color: #374151; /* gray-700 */ + } + + svg { + fill: #d1d5db; /* gray-300 */ + } +} + .shadow-xl { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), diff --git a/packages/extension/src/components/Popup.module.css b/packages/extension/src/components/Popup.module.css index 59724e2bc..2e4735336 100644 --- a/packages/extension/src/components/Popup.module.css +++ b/packages/extension/src/components/Popup.module.css @@ -41,6 +41,7 @@ --sc-bd-color-s: 10%; --sc-bd-color-l: 26%; --sc-font-color: #e2e8f0; + --sc-icon-color: #e2e8f0; --foreground: 213 31% 91%; /* Hover color: lighter than the background, unlike the light theme. */ @@ -68,7 +69,7 @@ } .previewLabel { - @apply font-mono text-gray-800; + @apply font-mono text-gray-800 dark:text-gray-200; font-size: 19.2px; } diff --git a/packages/extension/src/components/Popup.tsx b/packages/extension/src/components/Popup.tsx index 516d9e63b..78b22ca0b 100644 --- a/packages/extension/src/components/Popup.tsx +++ b/packages/extension/src/components/Popup.tsx @@ -8,6 +8,7 @@ import { useSidePanelNavigation } from "@/hooks/useSidePanelNavigation" import { useSidePanelAutoClose } from "@/hooks/useSidePanelAutoClose" import { useSelectContext } from "@/hooks/useSelectContext" import { popupContext } from "@/hooks/usePopupContext" +import { useThemeContext } from "@/hooks/useTheme" import { isMac, onHover, cn } from "@/lib/utils" import { findUserStyleValue, @@ -102,10 +103,14 @@ export const Popup = forwardRef( setDetectSelectionEnabled(!hover) } - const hasIconColor = hasUserStyle( - userSettings?.userStyles, - STYLE_VARIABLE.ICON_COLOR, - ) + const hasCustomColors = hasUserColors(userSettings?.userStyles) + // The dark theme paints recolorable icons (not favicons or brand icons) + // in its own icon color, as the global icon color setting would: they + // are mostly black glyphs that would vanish on the dark background. + const isDarkDefault = useThemeContext() === "dark" && !hasCustomColors + const hasIconColor = + hasUserStyle(userSettings?.userStyles, STYLE_VARIABLE.ICON_COLOR) || + isDarkDefault return ( ( alignOffset={alignOffset} className={cn(css.popup, isPreview && "z-10 mt-2")} style={userStyles} - data-custom-colors={ - hasUserColors(userSettings?.userStyles) || undefined - } + data-custom-colors={hasCustomColors || undefined} {...onHover(handleOnHover, true)} > {!isContextMenu ? : null} diff --git a/packages/extension/src/components/help/PageActionHelp.tsx b/packages/extension/src/components/help/PageActionHelp.tsx index 1f4059e7e..191c9f1f0 100644 --- a/packages/extension/src/components/help/PageActionHelp.tsx +++ b/packages/extension/src/components/help/PageActionHelp.tsx @@ -44,7 +44,7 @@ export const PageActionHelp = ({ className }: Props) => {