diff --git a/scripts/userstyles.yml b/scripts/userstyles.yml
index 57c1872f88..1893ac0895 100644
--- a/scripts/userstyles.yml
+++ b/scripts/userstyles.yml
@@ -75,6 +75,7 @@ collaborators:
- &koibtw koibtw
- &TheAnonymousCrusher TheAnonymousCrusher
- &42willow 42willow
+ - &ELHart05 ELHart05
userstyles:
advent-of-code:
@@ -282,7 +283,7 @@ userstyles:
categories: [search_engine]
icon: duckduckgo
color: peach
- current-maintainers: [*GenShibe]
+ current-maintainers: [*GenShibe, *ELHart05]
ecosia:
name: Ecosia
link: https://www.ecosia.org
diff --git a/styles/duckduckgo/catppuccin.user.less b/styles/duckduckgo/catppuccin.user.less
index 6f3f492c96..00777753cc 100644
--- a/styles/duckduckgo/catppuccin.user.less
+++ b/styles/duckduckgo/catppuccin.user.less
@@ -18,11 +18,13 @@
@import "https://userstyles.catppuccin.com/lib/lib.less";
@-moz-document domain("duckduckgo.com"), domain("duck.ai") {
- :root:not(.dark-bg, .no-theme) {
+ /* the homepage uses .theme-dark instead of .dark-bg, so count it as dark */
+ :root:not(.dark-bg, .no-theme, .theme-dark) {
#catppuccin(@lightFlavor);
}
- :root.dark-bg {
+ :root.dark-bg,
+ :root.theme-dark {
#catppuccin(@darkFlavor);
}
@@ -163,6 +165,14 @@
--col-blue-30: @blue !important;
--sds-color-palette-green-60: @green !important;
--sds-color-background-utility: @surface0 !important;
+ --sds-color-background-03: @surface0 !important;
+ --sds-color-border-inner-divider: @surface1 !important;
+ --sds-color-focus: @accent !important;
+ --sds-color-text-primary: @text !important;
+ /* the header search box keys its bg off background-03 above; its input and
+ button text still ship near-white, so pin them to readable palette ink */
+ --theme-col-txt-search-input: @text !important;
+ --theme-col-txt-search-button: @subtext0 !important;
.address-detail {
background-color: @mantle;
@@ -1063,6 +1073,42 @@
border-color: @surface0;
}
+ /* fill the rounded wrapper, not the square input, so it stops poking out */
+ [data-testid="search-form-input-wrapper"] {
+ background-color: @surface0 !important;
+ }
+ #search_form_input {
+ background-color: transparent !important;
+ }
+ #search_form button[type="submit"],
+ #search_form button[type="reset"] {
+ color: @accent !important;
+ }
+ /* DDG fills this on focus too, so match the hover icon */
+ #search_form button[type="submit"]:hover,
+ #search_form button[type="submit"]:active,
+ #search_form:focus-within button[type="submit"] {
+ color: @base !important;
+ background-color: @accent !important;
+ }
+ [data-testid="search-autocomplete-menu"] > div {
+ background-color: @surface0 !important;
+ }
+ [data-testid="search-autocomplete-menu"] [role="option"]:hover,
+ [data-testid="search-autocomplete-menu"]
+ [role="option"][aria-selected="true"] {
+ background-color: @surface1 !important;
+ }
+ /* Latte is low-contrast; keep the search box defined on the pale page */
+ & when (@flavor = latte) {
+ [data-testid="search-form-input-wrapper"],
+ [data-testid="search-autocomplete-menu"] > div {
+ border-width: 1px !important;
+ border-style: solid !important;
+ border-color: @surface2 !important;
+ }
+ }
+
.acp--highlight,
.bg-clr--platinum-light {
background-color: @overlay0;
@@ -1106,8 +1152,10 @@
background-image: url("data:image/svg+xml,@{svg}") !important;
}
- // ddg header logo
- .header_logoImg__PFezv {
+ // ddg header logo — recolor it on every page that ships it (the home page
+ // gives the image a class, the compare page renders a bare
)
+ [class*="header_logoImg"],
+ [class*="header_logo"] img {
@svg: escape(
''
);
@@ -1116,8 +1164,10 @@
}
}
-@-moz-document domain("start.duckduckgo.com") {
- :root:not(.theme-dark) {
+/* the homepage app (also on start.duckduckgo.com) keys off theme-light/dark;
+ the results pages don't, so this block stays off the SERP */
+@-moz-document domain("duckduckgo.com") {
+ :root.theme-light {
#catppuccin(@lightFlavor);
}
@@ -1145,59 +1195,349 @@
background-color: @surface0 !important;
color: @text !important;
}
- li:hover {
- background-color: @surface2 !important;
- }
- ul {
- background-color: @surface0 !important;
- }
body {
background-color: @base !important;
- }
- h1,
- h2,
- h3,
- p,
- a,
- span {
+ /* inherited, so generic text follows the palette while accent-filled
+ callouts and buttons keep their own readable content color */
color: @text !important;
}
--color-yellow60: @yellow !important;
--theme-button-primary-bg: @blue !important;
+ --theme-button-primary-text: @base !important;
+ --theme-button-secondary-text: @text !important;
--theme-searchbox-caret-bg: @accent !important;
-
- .home_root__naJUp {
- --theme-bg-home: @base !important;
- --theme-button-secondary-text: @text !important;
- --theme-text-bg: @text !important;
- --theme-bg-home-searchbox: @surface0 !important;
- --theme-border-color-home-searchbox: @surface0 !important;
- }
- .searchbox_suggestions__umkQH {
- --theme-searchbox-bg: @surface0 !important;
+ --theme-searchbox-bg: @surface0 !important;
+ --theme-bg-home: @base !important;
+ --theme-bg-home-searchbox: @surface0 !important;
+ --theme-border-color-home-searchbox: @surface0 !important;
+ --theme-text-bg: @text !important;
+ --theme-sidemenu-bg: @surface0 !important;
+ --theme-searchbox-suggestion--hover: @surface1 !important;
+
+ /* the redesign builds every --theme-* var on top of these design-system
+ tokens; recoloring the solid ones here themes the whole app at once and
+ leaves the translucent overlays (glow, tone, decoration) to blend on
+ their own */
+ --ds-text-primary: @text !important;
+ --ds-text-secondary: @subtext1 !important;
+ --ds-text-tertiary: @subtext0 !important;
+ --ds-icons-primary: @text !important;
+ --ds-icons-secondary: @subtext1 !important;
+ --ds-icons-tertiary: @subtext0 !important;
+ --ds-surface-backdrop: @base !important;
+ --ds-surface-canvas: @base !important;
+ --ds-surface-primary: @surface0 !important;
+ --ds-surface-secondary: @surface0 !important;
+ --ds-surface-tertiary: @surface1 !important;
+ --ds-surface-primary-text: @text !important;
+ --ds-accent-primary: @accent !important;
+ --ds-accent-secondary: @accent !important;
+ --ds-accent-tertiary: @accent !important;
+ --ds-accent-quaternary: @accent !important;
+ --ds-accent-primary-content-color: @base !important;
+ --ds-accent-secondary-content-color: @base !important;
+ --ds-accent-tertiary-content-color: @base !important;
+ --ds-accent-primary-text-color: @accent !important;
+ --ds-accent-secondary-text-color: @accent !important;
+ --ds-accent-tertiary-text-color: @accent !important;
+ --ds-status-green: @green !important;
+ --ds-status-red: @red !important;
+ --ds-status-yellow: @yellow !important;
+ --ds-destructive-primary: @red !important;
+ --ds-destructive-secondary: @red !important;
+ --ds-destructive-tertiary: @red !important;
+ --ds-destructive-primary-content-color: @base !important;
+ --ds-destructive-primary-text-color: @red !important;
+ --ds-destructive-secondary-text-color: @red !important;
+ --ds-destructive-tertiary-text-color: @red !important;
+
+ /* header nav links and the home/AI-chat button text fall back to plain
+ black or white; bring them onto the palette */
+ --theme-header-text: @text !important;
+ --theme-header-text--hover: @subtext1 !important;
+ --theme-header-text--active: @subtext0 !important;
+ --theme-text-home: @text !important;
+
+ /* the AI-search and Duck.ai glyphs ship in DuckDuckGo brand blue */
+ --theme-ai-searchbox-ai-chat-icon--path: @blue !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-b1: @sapphire !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-b2: @sapphire !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-c1: @sapphire !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-c2: @sapphire !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-d1: @sapphire !important;
+ --theme-ai-searchbox-ai-chat-icon--gradient-d2: @sapphire !important;
+ --theme-ai-searchbox-search-find-icon--path: @text !important;
+ --theme-ai-searchbox-search-find-icon--gradient-a1: @sapphire !important;
+ --theme-ai-searchbox-search-find-icon--gradient-a2: @sapphire !important;
+
+ [class*="minimal-homepage_layout"],
+ [class*="minimal-homepage_minimalHero"],
+ [class*="app_appRebrand"] {
+ background-color: @base !important;
}
- .minimal-homepage_minimalHeroLogo__QSVlf {
+ [class*="minimalHeroLogo"] {
@svg: escape(
''
);
content: url("data:image/svg+xml,@{svg}");
}
- .header_headerButton__cLYU3 {
+ [class*="header_headerButton"] {
color: @text !important;
}
- .sideMenu_sideMenuContent__OE7n9,
- .searchbox_iconWrapper__suWUe {
+ [class*="sideMenu_sideMenuContent"],
+ [class*="ai-searchbox_combobox"],
+ [class*="search-input_searchbox"],
+ [class*="suggestions-list_suggestions"] {
background-color: @surface0 !important;
}
- .button_primary__e2_Sy {
- color: @mantle !important;
+ [class*="search-input_searchInput"] {
+ background-color: transparent !important;
}
- .searchbox_hasQuery__j8_VE:hover
- .searchbox_searchButton__F5Bwq:not(:disabled),
- .searchbox_hasQuery__j8_VE:focus-within {
- color: @mantle !important;
+ [class*="ai-searchbox_combobox"] {
+ outline-color: @accent !important;
+ /* snap to a whole pixel; -0.5px renders lopsided at fractional
+ display scaling in Firefox */
+ outline-offset: 0 !important;
+ }
+ [class*="suggestions-list_suggestions"] [role="option"]:hover {
+ background-color: @surface1 !important;
+ }
+ /* the "Ask Duck.ai" footer suffix and its shortcut ship DuckDuckGo greys
+ that wash out on the pale row; pin them to a readable palette tone */
+ [class*="suggestions-list_duckAISuggestion"] {
+ color: @subtext0 !important;
+ }
+ /* only the accent-filled solid buttons want the content color; matching the
+ bare "button_primary" also hit surface cards and washed their text out */
+ [class*="button_primary-solid"] {
+ color: var(--ds-accent-primary-content-color) !important;
+ }
+ /* search and Duck.ai modes use different button classes; target the
+ submit button so both get the same treatment */
+ [class*="ai-searchbox_combobox"] button[type="submit"] {
+ color: @subtext0 !important;
+ background-color: transparent !important;
+ }
+ [class*="ai-searchbox_combobox"] button[type="submit"]:not(:disabled) {
+ color: @accent !important;
+ }
+ /* keep the icon on the button color (the generic span rule would
+ otherwise wash it out) */
+ [class*="ai-searchbox_combobox"] button[type="submit"] span {
+ color: inherit !important;
+ }
+ /* fill on focus, bar-hover, and button hover/press, like the results page */
+ [class*="ai-searchbox_combobox"] button[type="submit"]:not(:disabled):hover,
+ [class*="ai-searchbox_combobox"]
+ button[type="submit"]:not(:disabled):active,
+ [class*="ai-searchbox_combobox"]:hover button[type="submit"]:not(:disabled),
+ [class*="ai-searchbox_form"]:focus-within
+ button[type="submit"]:not(:disabled) {
+ color: @base !important;
background-color: @accent !important;
}
+
+ /* below-the-fold: flatten brand-colored cards onto the palette */
+ [class*="downloadsCard"],
+ [class*="card_card"],
+ [class*="card-button_root"] {
+ background-color: @surface0 !important;
+ border-color: @surface1 !important;
+ }
+ /* restore the hover lift the flat fill above wiped out */
+ [class*="card-button_root"]:hover {
+ background-color: @surface1 !important;
+ }
+ /* a step darker than the hero, so the scroll-tab dome blends in and only
+ the bump shows (as in the original) */
+ [class*="minimal-homepage_belowTheFold"],
+ [class*="app-quotes_root"],
+ [class*="_bottomRow"] {
+ background-color: @mantle !important;
+ }
+ [class*="pull-tab-scroll-button_keyContainer"] path {
+ fill: @mantle !important;
+ }
+ /* the down arrow ships light; keep it on the text color so it reads */
+ [class*="pull-tab-scroll-button"] [class*="icon-button"] path {
+ fill: @text !important;
+ }
+ [class*="pull-tab-scroll-button_peekButtonKey"] {
+ color: @text !important;
+ }
+ [class*="plusMinusButton"] {
+ background-color: @surface1 !important;
+ }
+ [class*="heading_heading"],
+ [class*="_headingText"] {
+ color: @text !important;
+ }
+
+ [class*="chip-select_root"] {
+ background-color: @crust !important;
+ }
+ [class*="chip-select_selectedIndicator"] {
+ background-color: @surface2 !important;
+ }
+ [class*="chip-select_option"] {
+ color: @text !important;
+ }
+ [class*="chip-select_optionIcon"] path {
+ fill: @text !important;
+ }
+ /* Duck.ai spark, mono (unselected) glyph */
+ [clip-path*="ai-chat"]:not([clip-path*="color"]) path:nth-of-type(2) {
+ fill: @mauve !important;
+ }
+ /* selected option swaps in the filled color glyph: drop its solid bubble so
+ only the outline shows, leaving a spark that matches the mono one */
+ [clip-path*="ai-chat"][clip-path*="color"] path:nth-of-type(1),
+ [clip-path*="ai-chat"][clip-path*="color"] path:nth-of-type(3) {
+ fill: transparent !important;
+ }
+ [clip-path*="ai-chat"][clip-path*="color"] path:nth-of-type(2) {
+ fill: @text !important;
+ }
+ [clip-path*="ai-chat"][clip-path*="color"] path:nth-of-type(4),
+ [clip-path*="ai-chat"][clip-path*="color"] path:nth-of-type(5) {
+ fill: @mauve !important;
+ /* this spark is bigger than the mono one; scale it down to match */
+ transform: scale(0.72) !important;
+ transform-box: fill-box !important;
+ transform-origin: center !important;
+ }
+ [aria-label="Search"][class*="chip-select_optionIcon"] path {
+ fill: @blue !important;
+ }
+ /* the home comparison uses pill tabs; the /compare-privacy page uses an
+ underline indicator, so only fill the pills and recolor the underline */
+ [class*="chipTabs"] [class*="tabs_tab__"] {
+ background-color: @surface0 !important;
+ }
+ [class*="chipTabs"] [class*="tabs_tab__"][aria-selected="true"],
+ [class*="chipTabs"] [class*="tabs_tab__"][data-selected] {
+ color: @base !important;
+ background-color: @accent !important;
+ }
+ /* the label is a child with its own color, so pin it on the filled pill */
+ [class*="chipTabs"]
+ [class*="tabs_tab__"][aria-selected="true"]
+ [class*="tabs_tabLabel"],
+ [class*="chipTabs"]
+ [class*="tabs_tab__"][data-selected]
+ [class*="tabs_tabLabel"] {
+ color: @base !important;
+ }
+ [class*="tabs_tab__"][aria-selected="true"] {
+ border-bottom-color: @accent !important;
+ }
+ /* comparison table zebra rows: DuckDuckGo rounds the odd rows, so the
+ fill has to land on those for the pill shape to show */
+ [class*="marketing-compare-privacy-table"] tbody tr:nth-child(odd) {
+ background-color: @surface0 !important;
+ }
+
+ /* the primary CTA fill and label already ride the accent tokens; only its
+ icon ships a fixed color, so tie that to the button's content color */
+ [class*="link-button_primary-solid"] path {
+ fill: var(--ds-accent-primary-content-color) !important;
+ }
+
+ /* the compare-privacy hero ships a brand navy band; swap it for a crust band
+ so it stays clearly distinct from the page without the off-palette blue */
+ [class*="compare-privacy-page_hero"] {
+ background-color: @crust !important;
+ }
+ /* outlined secondary buttons (e.g. the extension CTA) and the checkbox box
+ fall back to a faint black-alpha outline; give them a palette edge */
+ [class*="link-button_secondary"],
+ [class*="checkbox_indicator"] {
+ border-color: @surface2 !important;
+ }
+ /* secondary/ghost link buttons (including the tagline pill) ship grey label
+ text that washes out; pin it to the palette ink */
+ [class*="link-button_secondary"],
+ [class*="link-button_secondary"] [class*="buttonLabel"],
+ [class*="minimal-homepage_taglineText"] {
+ color: @text !important;
+ }
+ /* the side menu's close glyph ships a fixed light grey that vanishes on the
+ pale surface */
+ [class*="sideMenu_closeButton"] {
+ color: @text !important;
+ }
+ [class*="sideMenu_closeButton"]:hover {
+ background-color: @surface1 !important;
+ }
+
+ /* the +/- feature popups and hover tooltips paint the bubble, its text,
+ and its pointer with a motif color; flatten them onto a surface so they
+ read in every section */
+ [class*="tooltip_tooltip"],
+ [class*="feature-card-detail-button_popover__"] {
+ background-color: @surface0 !important;
+ color: @text !important;
+ }
+ [class*="feature-card-detail-button_popoverText"] {
+ color: @text !important;
+ }
+ [class*="tooltip_arrow"] {
+ fill: @surface0 !important;
+ }
+ [class*="feature-card-detail-button_pointer"] {
+ color: @surface0 !important;
+ }
+
+ /* the Customize Homepage dialog ships a flat #444/#fafafa surface and a
+ blue glow on the selected option; pull both onto the palette */
+ [class*="customize-button_modal__"] {
+ background-color: @base !important;
+ }
+ [class*="customize-button_radio__"][data-selected] {
+ background-color: fade(@accent, 20%) !important;
+ }
+ /* the unselected option ships a near-invisible black-alpha tint and no edge;
+ give it a defined surface so both options read as choices */
+ [class*="customize-button_radio__"]:not([data-selected]) {
+ background-color: @surface0 !important;
+ border-color: @surface2 !important;
+ }
+ /* the feedback thumbs ship mismatched colors (one brand blue, one grey);
+ give both glyphs one readable tone on their surface */
+ [class*="customize-button_feedbackRadio__"] svg,
+ [class*="customize-button_feedbackRadio__"] path {
+ color: @subtext0 !important;
+ fill: @subtext0 !important;
+ }
+
+ /* Latte is low-contrast, so give the surfaces a hairline border to keep
+ them defined against the pale page */
+ & when (@flavor = latte) {
+ [class*="ai-searchbox_combobox"],
+ [class*="card_card"],
+ [class*="card-button_root"],
+ [class*="feature-card_root"],
+ [class*="downloadsCard"],
+ [class*="chip-select_root"] {
+ border-width: 1px !important;
+ border-style: solid !important;
+ border-color: @surface2 !important;
+ }
+ /* these controls fall back to a muddy black-on-white tint (or no fill at
+ all), which reads as dirty grey on the pale page; give them a clean
+ surface instead */
+ [class*="customize-button_customizeButton"],
+ [class*="link-button_secondary-solid"] {
+ background-color: @surface0 !important;
+ border-color: @surface2 !important;
+ }
+ [class*="customize-button_feedbackRadio__"] {
+ background-color: @surface0 !important;
+ }
+ [class*="customize-button_feedbackRadio__"]:hover {
+ background-color: @surface1 !important;
+ }
+ }
}
}