From 37d8b60bc4d11a5a2f80657298abe48d79f28e5d Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:19:14 +0200 Subject: [PATCH 01/37] =?UTF-8?q?=F0=9F=8E=A8=20Improved=20dark=20mode=20s?= =?UTF-8?q?idebar=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added a dedicated sidebar background token so the dark sidebar reads as a distinct surface rather than pure gray-950 - Reworked the sidebar search field in dark mode: gray-900/30 fill with a gray-900/50 border, plus a lighter text and a slightly stronger border on hover so the affordance is clearer against the new background --- apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx | 2 +- apps/shade/theme-variables.css | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx b/apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx index da729cb7c78..cce6b494736 100644 --- a/apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx +++ b/apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx @@ -60,7 +60,7 @@ function AppSidebarHeader({ ...props }: React.ComponentProps
diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index b2fb3634e93..e0b5f01bf5f 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -74,6 +74,9 @@ } .dark { + /* One-off colors (not in the standard color scale) */ + --color-sidebar-bg: oklch(0.23 0.006 260); /* #1B1D20 */ + --background: var(--color-black); --foreground: var(--color-gray-200); --muted: var(--color-gray-900); @@ -124,7 +127,7 @@ --chart-4: hsl(201 82% 90%); --chart-5: hsl(201 87% 94%); --chart-gray: hsl(210 13% 55%); - --sidebar-background: var(--color-gray-950); + --sidebar-background: var(--color-sidebar-bg); --sidebar-foreground: var(--color-gray-100); --sidebar-primary: var(--color-gray-900); --sidebar-primary-foreground: var(--color-white); From 5047ee002b39a7bd8beafd4afc32f469dbe8f7db Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:28:10 +0200 Subject: [PATCH 02/37] =?UTF-8?q?=F0=9F=8E=A8=20Improved=20dark=20mode=20s?= =?UTF-8?q?idebar=20menu=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added a dedicated sidebar border token (#25272A) so the right edge of the dark sidebar reads as a distinct surface, matching the new background tone - Tuned active and hover backgrounds: active items now use gray-900/60, non-active hover uses gray-900/30, and active-item hover stays at the active color instead of lightening - Dimmed inactive menu item text (and their icons via currentColor) from gray-100 to gray-500 so the active item reads as the primary signal --- apps/shade/src/components/ui/sidebar.tsx | 4 ++-- apps/shade/theme-variables.css | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/shade/src/components/ui/sidebar.tsx b/apps/shade/src/components/ui/sidebar.tsx index c9f9a9a178b..15551d46f10 100644 --- a/apps/shade/src/components/ui/sidebar.tsx +++ b/apps/shade/src/components/ui/sidebar.tsx @@ -530,11 +530,11 @@ const SidebarMenuItem = React.forwardRef< SidebarMenuItem.displayName = 'SidebarMenuItem'; const sidebarMenuButtonVariants = cva( - 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md px-3 py-2 text-left text-control font-medium text-sidebar-foreground ring-sidebar-ring outline-hidden transition-all group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', + 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md px-3 py-2 text-left text-control font-medium text-sidebar-foreground ring-sidebar-ring outline-hidden transition-all group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[active=true]:hover:bg-sidebar-accent data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground dark:hover:bg-gray-900/30 dark:data-[active=true]:hover:bg-sidebar-accent [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', { variants: { variant: { - default: 'hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground', + default: 'hover:bg-sidebar-accent/70 hover:text-sidebar-accent-foreground dark:hover:bg-gray-900/30', outline: 'border border-sidebar-border bg-background hover:border-sidebar-accent hover:bg-sidebar-accent hover:text-sidebar-accent-foreground' }, diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index e0b5f01bf5f..2a669b40973 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -76,6 +76,7 @@ .dark { /* One-off colors (not in the standard color scale) */ --color-sidebar-bg: oklch(0.23 0.006 260); /* #1B1D20 */ + --color-sidebar-border: oklch(0.29 0.006 260); /* #25272A */ --background: var(--color-black); --foreground: var(--color-gray-200); @@ -128,11 +129,11 @@ --chart-5: hsl(201 87% 94%); --chart-gray: hsl(210 13% 55%); --sidebar-background: var(--color-sidebar-bg); - --sidebar-foreground: var(--color-gray-100); + --sidebar-foreground: var(--color-gray-500); --sidebar-primary: var(--color-gray-900); --sidebar-primary-foreground: var(--color-white); - --sidebar-accent: var(--color-gray-900); + --sidebar-accent: color-mix(in oklab, var(--color-gray-900) 60%, transparent); --sidebar-accent-foreground: var(--color-gray-100); - --sidebar-border: var(--color-gray-900); + --sidebar-border: var(--color-sidebar-border); --sidebar-ring: var(--color-gray-700); } From 56491cc8e3dd45d2850182261a51ed955f21df3d Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:30:48 +0200 Subject: [PATCH 03/37] =?UTF-8?q?=F0=9F=8E=A8=20Updated=20secondary=20icon?= =?UTF-8?q?=20and=20text=20colors=20in=20dark=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Aligned the "create post" plus icon, sidebar count badges, and the user menu email with the ⌘K kbd colour (gray-800) in dark mode so the secondary affordances all read with the same weight against the new sidebar surface --- apps/admin/src/layout/app-sidebar/nav-content.tsx | 2 +- apps/admin/src/layout/app-sidebar/user-menu.tsx | 2 +- apps/shade/src/components/ui/sidebar.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/admin/src/layout/app-sidebar/nav-content.tsx b/apps/admin/src/layout/app-sidebar/nav-content.tsx index bc8d11c1c9e..7af433f7e63 100644 --- a/apps/admin/src/layout/app-sidebar/nav-content.tsx +++ b/apps/admin/src/layout/app-sidebar/nav-content.tsx @@ -30,7 +30,7 @@ function PostsNavItemContent({isActive, to}: {isActive: boolean; to: string}) {
{currentUser.data?.name} - + {currentUser.data?.email}
diff --git a/apps/shade/src/components/ui/sidebar.tsx b/apps/shade/src/components/ui/sidebar.tsx index 15551d46f10..93eaa16d8cc 100644 --- a/apps/shade/src/components/ui/sidebar.tsx +++ b/apps/shade/src/components/ui/sidebar.tsx @@ -648,7 +648,7 @@ const SidebarMenuBadge = React.forwardRef<
Date: Thu, 25 Jun 2026 11:36:16 +0200 Subject: [PATCH 04/37] =?UTF-8?q?=F0=9F=8E=A8=20Aligned=20dark=20mode=20ta?= =?UTF-8?q?b=20and=20page=20menu=20colors=20with=20the=20sidebar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Applied the sidebar's active/hover palette to PageMenuItem and the button, button-sm, and pill Tabs variants in dark mode (gray-900/30 hover, gray-900/60 active, active state preserved on hover) so the Analytics navigation and in-card tabs read as part of the same surface system as the sidebar --- apps/shade/src/components/ui/pagemenu.tsx | 1 + apps/shade/src/components/ui/tabs.tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/shade/src/components/ui/pagemenu.tsx b/apps/shade/src/components/ui/pagemenu.tsx index 2b193092aac..9cbd9707c17 100644 --- a/apps/shade/src/components/ui/pagemenu.tsx +++ b/apps/shade/src/components/ui/pagemenu.tsx @@ -210,6 +210,7 @@ const PageMenuItem = React.forwardRef( className={cn( 'relative px-3 gap-1.5 text-control font-medium text-text-secondary hover:text-foreground focus-visible:ring-0', 'data-[state=active]:bg-muted-foreground/10 data-[state=active]:text-foreground', + 'dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', className )} data-state={isActive ? 'active' : undefined} diff --git a/apps/shade/src/components/ui/tabs.tsx b/apps/shade/src/components/ui/tabs.tsx index 6b3678204b0..cd7060d0305 100644 --- a/apps/shade/src/components/ui/tabs.tsx +++ b/apps/shade/src/components/ui/tabs.tsx @@ -89,11 +89,11 @@ const tabsTriggerVariants = cva( variant: { segmented: 'h-7 rounded-md text-control font-medium data-[state=active]:shadow-md', 'segmented-sm': 'h-[26px] rounded-md text-sm font-medium data-[state=active]:shadow-md', - button: 'h-(--control-height) gap-1.5 rounded-md py-2 text-control font-normal hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium', - 'button-sm': 'h-6 gap-1.5 rounded-md p-2 text-sm font-normal text-text-secondary hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium data-[state=active]:text-foreground', + button: 'h-(--control-height) gap-1.5 rounded-md py-2 text-control font-normal hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', + 'button-sm': 'h-6 gap-1.5 rounded-md p-2 text-sm font-normal text-text-secondary hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium data-[state=active]:text-foreground dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', underline: 'relative h-9 px-0 text-control font-semibold text-text-secondary after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] hover:after:opacity-10 data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:after:opacity-100!', navbar: 'relative h-[52px] px-px text-control font-semibold text-muted-foreground after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] hover:text-foreground data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:after:opacity-100!', - pill: 'relative h-[30px] rounded-md px-3 text-control font-medium text-text-secondary hover:text-foreground data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-semibold data-[state=active]:text-foreground', + pill: 'relative h-[30px] rounded-md px-3 text-control font-medium text-text-secondary hover:text-foreground data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-semibold data-[state=active]:text-foreground dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', kpis: 'relative h-full! items-start! rounded-none border-border bg-transparent px-6 py-5 text-foreground ring-0 transition-all after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] first:rounded-tl-md last:rounded-tr-md hover:bg-accent/50 data-[state=active]:bg-transparent data-[state=active]:after:opacity-100 [&:not(:last-child)]:border-r [&[data-state=active]_[data-type="value"]]:text-foreground' } }, From 62a44d05e13707c9cdebf70881587c713c688754 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:46:00 +0200 Subject: [PATCH 05/37] =?UTF-8?q?=F0=9F=8E=A8=20Added=20named=20tab-hover?= =?UTF-8?q?=20and=20tab-active=20surface=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Introduced --tab-hover and --tab-active design tokens (registered as Tailwind bg-tab-hover and bg-tab-active utilities) so the repeated gray-900/30 and gray-900/60 dark mode values for tabs and the page menu live in one place - Pointed the dark --sidebar-accent at --tab-active so the active surface value is no longer duplicated between the sidebar and tab systems --- apps/shade/src/components/ui/pagemenu.tsx | 2 +- apps/shade/src/components/ui/tabs.tsx | 6 +++--- apps/shade/tailwind.theme.css | 2 ++ apps/shade/theme-variables.css | 7 ++++++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/shade/src/components/ui/pagemenu.tsx b/apps/shade/src/components/ui/pagemenu.tsx index 9cbd9707c17..95662ff2eef 100644 --- a/apps/shade/src/components/ui/pagemenu.tsx +++ b/apps/shade/src/components/ui/pagemenu.tsx @@ -210,7 +210,7 @@ const PageMenuItem = React.forwardRef( className={cn( 'relative px-3 gap-1.5 text-control font-medium text-text-secondary hover:text-foreground focus-visible:ring-0', 'data-[state=active]:bg-muted-foreground/10 data-[state=active]:text-foreground', - 'dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', + 'dark:hover:bg-tab-hover dark:data-[state=active]:bg-tab-active dark:data-[state=active]:hover:bg-tab-active', className )} data-state={isActive ? 'active' : undefined} diff --git a/apps/shade/src/components/ui/tabs.tsx b/apps/shade/src/components/ui/tabs.tsx index cd7060d0305..33539e99027 100644 --- a/apps/shade/src/components/ui/tabs.tsx +++ b/apps/shade/src/components/ui/tabs.tsx @@ -89,11 +89,11 @@ const tabsTriggerVariants = cva( variant: { segmented: 'h-7 rounded-md text-control font-medium data-[state=active]:shadow-md', 'segmented-sm': 'h-[26px] rounded-md text-sm font-medium data-[state=active]:shadow-md', - button: 'h-(--control-height) gap-1.5 rounded-md py-2 text-control font-normal hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', - 'button-sm': 'h-6 gap-1.5 rounded-md p-2 text-sm font-normal text-text-secondary hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium data-[state=active]:text-foreground dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', + button: 'h-(--control-height) gap-1.5 rounded-md py-2 text-control font-normal hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium dark:hover:bg-tab-hover dark:data-[state=active]:bg-tab-active dark:data-[state=active]:hover:bg-tab-active', + 'button-sm': 'h-6 gap-1.5 rounded-md p-2 text-sm font-normal text-text-secondary hover:bg-muted data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-medium data-[state=active]:text-foreground dark:hover:bg-tab-hover dark:data-[state=active]:bg-tab-active dark:data-[state=active]:hover:bg-tab-active', underline: 'relative h-9 px-0 text-control font-semibold text-text-secondary after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] hover:after:opacity-10 data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:after:opacity-100!', navbar: 'relative h-[52px] px-px text-control font-semibold text-muted-foreground after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] hover:text-foreground data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:after:opacity-100!', - pill: 'relative h-[30px] rounded-md px-3 text-control font-medium text-text-secondary hover:text-foreground data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-semibold data-[state=active]:text-foreground dark:hover:bg-gray-900/30 dark:data-[state=active]:bg-gray-900/60 dark:data-[state=active]:hover:bg-gray-900/60', + pill: 'relative h-[30px] rounded-md px-3 text-control font-medium text-text-secondary hover:text-foreground data-[state=active]:bg-muted-foreground/10 data-[state=active]:font-semibold data-[state=active]:text-foreground dark:hover:bg-tab-hover dark:data-[state=active]:bg-tab-active dark:data-[state=active]:hover:bg-tab-active', kpis: 'relative h-full! items-start! rounded-none border-border bg-transparent px-6 py-5 text-foreground ring-0 transition-all after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:bg-foreground after:opacity-0 after:content-[""] first:rounded-tl-md last:rounded-tr-md hover:bg-accent/50 data-[state=active]:bg-transparent data-[state=active]:after:opacity-100 [&:not(:last-child)]:border-r [&[data-state=active]_[data-type="value"]]:text-foreground' } }, diff --git a/apps/shade/tailwind.theme.css b/apps/shade/tailwind.theme.css index c458be1eac2..bc227799c25 100644 --- a/apps/shade/tailwind.theme.css +++ b/apps/shade/tailwind.theme.css @@ -204,6 +204,8 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + --color-tab-hover: var(--tab-hover); + --color-tab-active: var(--tab-active); /* Typography */ /* ---------------------------------------------------------------------- */ diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index 2a669b40973..6de49b12fc5 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -70,6 +70,8 @@ --sidebar-accent-foreground: var(--color-black); --sidebar-border: var(--color-gray-100); --sidebar-ring: var(--color-gray-600); + --tab-hover: var(--color-gray-100); + --tab-active: color-mix(in oklab, var(--color-gray-700) 10%, transparent); --mobile-navbar-height: 64px; } @@ -78,6 +80,9 @@ --color-sidebar-bg: oklch(0.23 0.006 260); /* #1B1D20 */ --color-sidebar-border: oklch(0.29 0.006 260); /* #25272A */ + --tab-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); + --tab-active: color-mix(in oklab, var(--color-gray-900) 60%, transparent); + --background: var(--color-black); --foreground: var(--color-gray-200); --muted: var(--color-gray-900); @@ -132,7 +137,7 @@ --sidebar-foreground: var(--color-gray-500); --sidebar-primary: var(--color-gray-900); --sidebar-primary-foreground: var(--color-white); - --sidebar-accent: color-mix(in oklab, var(--color-gray-900) 60%, transparent); + --sidebar-accent: var(--tab-active); --sidebar-accent-foreground: var(--color-gray-100); --sidebar-border: var(--color-sidebar-border); --sidebar-ring: var(--color-gray-700); From b80679589a55b00b03e682408c8e0271e400e512 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:49:30 +0200 Subject: [PATCH 06/37] =?UTF-8?q?=F0=9F=8E=A8=20Lifted=20dark=20mode=20bas?= =?UTF-8?q?e=20border=20to=20gray-900=20at=2050%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Raised the dark mode --border from solid gray-950 to gray-900 at 50% so dividers and outlines read at the right contrast against the new panel surfaces - Pointed --sidebar-border at --border so the sidebar's right edge matches the rest of the system instead of carrying its own one-off value --- apps/shade/theme-variables.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index 6de49b12fc5..a4c053cd548 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -78,7 +78,6 @@ .dark { /* One-off colors (not in the standard color scale) */ --color-sidebar-bg: oklch(0.23 0.006 260); /* #1B1D20 */ - --color-sidebar-border: oklch(0.29 0.006 260); /* #25272A */ --tab-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); --tab-active: color-mix(in oklab, var(--color-gray-900) 60%, transparent); @@ -91,7 +90,7 @@ --accent-foreground: var(--color-gray-100); --popover: var(--color-black); --popover-foreground: var(--color-gray-400); - --border: var(--color-gray-950); + --border: color-mix(in oklab, var(--color-gray-900) 50%, transparent); --input: var(--color-gray-900); --card: var(--color-black); --card-foreground: var(--color-gray-100); @@ -139,6 +138,6 @@ --sidebar-primary-foreground: var(--color-white); --sidebar-accent: var(--tab-active); --sidebar-accent-foreground: var(--color-gray-100); - --sidebar-border: var(--color-sidebar-border); + --sidebar-border: var(--border); --sidebar-ring: var(--color-gray-700); } From b719d16da59cad2c4431a3acadcec93999807142 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 11:50:49 +0200 Subject: [PATCH 07/37] =?UTF-8?q?=F0=9F=8E=A8=20Changed=20the=20new=20post?= =?UTF-8?q?=20+=20icon=20to=20white=20on=20hover=20in=20dark=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Bumped the create post + icon to white on hover in dark mode so it reads as the primary affordance against the new active surface fill --- apps/admin/src/layout/app-sidebar/nav-content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin/src/layout/app-sidebar/nav-content.tsx b/apps/admin/src/layout/app-sidebar/nav-content.tsx index 7af433f7e63..f7b67d46914 100644 --- a/apps/admin/src/layout/app-sidebar/nav-content.tsx +++ b/apps/admin/src/layout/app-sidebar/nav-content.tsx @@ -30,7 +30,7 @@ function PostsNavItemContent({isActive, to}: {isActive: boolean; to: string}) { Date: Thu, 25 Jun 2026 11:52:50 +0200 Subject: [PATCH 08/37] =?UTF-8?q?=F0=9F=8E=A8=20Removed=20the=20gradient?= =?UTF-8?q?=20background=20from=20the=20Latest=20post=20performance=20card?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Dropped the muted top-right gradient on the Analytics overview's "Latest post performance" card so the section sits flat against the page surface in both light and dark mode --- apps/stats/src/views/Stats/Overview/components/latest-post.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stats/src/views/Stats/Overview/components/latest-post.tsx b/apps/stats/src/views/Stats/Overview/components/latest-post.tsx index 609cacf9ccf..1f5438fbde1 100644 --- a/apps/stats/src/views/Stats/Overview/components/latest-post.tsx +++ b/apps/stats/src/views/Stats/Overview/components/latest-post.tsx @@ -59,7 +59,7 @@ const LatestPost: React.FC = ({ const shouldGoToEditor = postDestination.startsWith('/editor/'); return ( - + Latest post performance From 336312531bf22e66ada2e3ff876d1cb4f4520802 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 12:01:55 +0200 Subject: [PATCH 09/37] =?UTF-8?q?=F0=9F=8E=A8=20Improved=20dark=20mode=20d?= =?UTF-8?q?ropdown=20and=20outline=20button=20surfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added a --button-hover token (gray-100 light, gray-900/30 dark) so outline buttons, the dropdown button variant, and the SelectTrigger share one named hover surface - Switched the input-surface recipe to bg-transparent and border-border in dark mode so the Select trigger (plus Input/Textarea/InputGroup) blends into its container the way they already do in light mode and matches the lifted base border colour - Aligned outline / dropdown button borders with the recipe so all control surfaces use the same border-border value --- apps/shade/src/components/ui/button.tsx | 4 ++-- apps/shade/src/components/ui/input-surface.ts | 2 +- apps/shade/src/components/ui/select.tsx | 2 +- apps/shade/tailwind.theme.css | 1 + apps/shade/theme-variables.css | 2 ++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/shade/src/components/ui/button.tsx b/apps/shade/src/components/ui/button.tsx index 71a4ea52860..7f95ee4e033 100644 --- a/apps/shade/src/components/ui/button.tsx +++ b/apps/shade/src/components/ui/button.tsx @@ -12,11 +12,11 @@ const buttonVariants = cva( variant: { default: 'bg-primary font-semibold text-primary-foreground hover:bg-primary/90', destructive: 'bg-destructive font-medium text-destructive-foreground hover:bg-destructive/90', - outline: 'border border-input bg-background font-medium hover:bg-accent hover:text-accent-foreground', + outline: 'border border-border bg-background font-medium hover:bg-button-hover hover:text-accent-foreground', secondary: 'bg-secondary font-medium text-secondary-foreground hover:bg-secondary/80', ghost: 'font-medium hover:bg-accent hover:text-accent-foreground', link: 'font-medium text-primary underline-offset-4 hover:underline', - dropdown: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' + dropdown: 'border border-border bg-background hover:bg-button-hover hover:text-accent-foreground' }, size: { default: 'h-(--control-height) px-2.5 py-2', diff --git a/apps/shade/src/components/ui/input-surface.ts b/apps/shade/src/components/ui/input-surface.ts index 762766cfc35..feb05db38cd 100644 --- a/apps/shade/src/components/ui/input-surface.ts +++ b/apps/shade/src/components/ui/input-surface.ts @@ -33,7 +33,7 @@ import {cn} from '@/lib/utils'; * )} /> */ export const inputSurfaceClasses = { - base: 'rounded-md border border-border-default bg-surface-elevated transition-colors', + base: 'rounded-md border border-border bg-surface-elevated transition-colors dark:bg-transparent', focusSelf: 'focus-visible:outline-hidden focus-visible:bg-transparent focus-visible:border-focus-ring focus-visible:ring-2 focus-visible:ring-focus-ring/25', focusWithin: diff --git a/apps/shade/src/components/ui/select.tsx b/apps/shade/src/components/ui/select.tsx index ae9ed50dc15..f65d8776aa8 100644 --- a/apps/shade/src/components/ui/select.tsx +++ b/apps/shade/src/components/ui/select.tsx @@ -20,7 +20,7 @@ const SelectTrigger = React.forwardRef< ref={ref} className={cn( inputSurface('self'), - 'flex h-(--control-height) w-full items-center justify-between whitespace-nowrap px-3 py-2 text-control hover:bg-accent data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1', + 'flex h-(--control-height) w-full items-center justify-between whitespace-nowrap px-3 py-2 text-control hover:bg-button-hover dark:hover:bg-button-hover data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1', className )} {...props} diff --git a/apps/shade/tailwind.theme.css b/apps/shade/tailwind.theme.css index bc227799c25..592842a5937 100644 --- a/apps/shade/tailwind.theme.css +++ b/apps/shade/tailwind.theme.css @@ -206,6 +206,7 @@ --color-sidebar-ring: var(--sidebar-ring); --color-tab-hover: var(--tab-hover); --color-tab-active: var(--tab-active); + --color-button-hover: var(--button-hover); /* Typography */ /* ---------------------------------------------------------------------- */ diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index a4c053cd548..09cbb9ece40 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -72,6 +72,7 @@ --sidebar-ring: var(--color-gray-600); --tab-hover: var(--color-gray-100); --tab-active: color-mix(in oklab, var(--color-gray-700) 10%, transparent); + --button-hover: var(--color-gray-100); --mobile-navbar-height: 64px; } @@ -81,6 +82,7 @@ --tab-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); --tab-active: color-mix(in oklab, var(--color-gray-900) 60%, transparent); + --button-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); --background: var(--color-black); --foreground: var(--color-gray-200); From c903852015f42734149806f1c040fd4357db5594 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 13:00:37 +0200 Subject: [PATCH 10/37] =?UTF-8?q?=F0=9F=8E=A8=20Updated=20dark=20mode=20el?= =?UTF-8?q?evated=20and=20popover=20surfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Pointed dark --surface-elevated and --popover at the existing --color-sidebar-bg so cards, popovers, dropdowns, and the sidebar share one elevated surface value - Bumped the sidebar user menu popover one step lighter so it reads as floating above the sidebar instead of melting into it --- apps/admin/src/layout/app-sidebar/user-menu.tsx | 2 +- apps/shade/theme-variables.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/admin/src/layout/app-sidebar/user-menu.tsx b/apps/admin/src/layout/app-sidebar/user-menu.tsx index 992f8a44fdd..d3a9d37b566 100644 --- a/apps/admin/src/layout/app-sidebar/user-menu.tsx +++ b/apps/admin/src/layout/app-sidebar/user-menu.tsx @@ -121,7 +121,7 @@ function UserMenu(props: UserMenuProps) { Date: Thu, 25 Jun 2026 13:23:59 +0200 Subject: [PATCH 11/37] =?UTF-8?q?=F0=9F=8E=A8=20Added=20interactive-hover?= =?UTF-8?q?=20token=20and=20reused=20it=20across=20menus=20and=20tables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added a shared --interactive-hover token (aliased to --button-hover) so dropdown options, table rows, and the existing outline button hover all reference one named value - Routed SelectItem and every DropdownMenu item/sub-trigger through --interactive-hover so list options match the outline button hover - Switched the Shade TableCell row hover to --interactive-hover and dropped the tags list's custom row hover + cell-hover suppressor so it picks up the standard treatment - Moved --members-sticky-hover-bg into theme-variables so the members list sticky column hovers with a solid composite of the new hover colour over the page surface in both modes --- apps/posts/src/views/Tags/components/tags-list.tsx | 2 +- .../src/views/members/components/members-list-item.tsx | 5 +---- apps/shade/src/components/ui/dropdown-menu.tsx | 8 ++++---- apps/shade/src/components/ui/select.tsx | 2 +- apps/shade/src/components/ui/table.tsx | 2 +- apps/shade/tailwind.theme.css | 1 + apps/shade/theme-variables.css | 4 ++++ 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/posts/src/views/Tags/components/tags-list.tsx b/apps/posts/src/views/Tags/components/tags-list.tsx index ef62ee4c43d..948231268bf 100644 --- a/apps/posts/src/views/Tags/components/tags-list.tsx +++ b/apps/posts/src/views/Tags/components/tags-list.tsx @@ -87,7 +87,7 @@ function TagsList({ diff --git a/apps/posts/src/views/members/components/members-list-item.tsx b/apps/posts/src/views/members/components/members-list-item.tsx index b30e53dc10a..c44699fce82 100644 --- a/apps/posts/src/views/members/components/members-list-item.tsx +++ b/apps/posts/src/views/members/components/members-list-item.tsx @@ -228,9 +228,6 @@ function MembersListItem({ ...props }: MembersListItemProps & Omit, 'onClick'>) { - const memberCellStyle = { - '--members-sticky-hover-bg': 'color-mix(in oklab, var(--muted) 50%, var(--background))' - } as CSSProperties; const handleRowClick = (event: React.MouseEvent) => { if (isModifiedClick(event)) { openMemberInNewTab(item.id, backPath); @@ -258,7 +255,7 @@ function MembersListItem({ > + )}> {showPinnedEdge && ( <> diff --git a/apps/shade/src/components/ui/dropdown-menu.tsx b/apps/shade/src/components/ui/dropdown-menu.tsx index abb96bf84b0..fb5eb0dc676 100644 --- a/apps/shade/src/components/ui/dropdown-menu.tsx +++ b/apps/shade/src/components/ui/dropdown-menu.tsx @@ -26,7 +26,7 @@ const DropdownMenuSubTrigger = React.forwardRef< svg]:size-4 [&>svg]:shrink-0', + 'relative flex cursor-default select-none cursor-pointer items-center gap-2 rounded-xs px-2 py-1.5 text-control outline-hidden transition-colors focus:bg-interactive-hover focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0', inset && 'pl-8', className )} @@ -104,7 +104,7 @@ const DropdownMenuCheckboxItem = React.forwardRef< ref={ref} checked={checked} className={cn( - 'relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-control outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', + 'relative flex cursor-default select-none items-center rounded-xs py-1.5 pl-8 pr-2 text-control outline-hidden transition-colors focus:bg-interactive-hover focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', className )} {...props} @@ -127,7 +127,7 @@ const DropdownMenuRadioItem = React.forwardRef< [role=checkbox]]:translate-y-[2px] group-hover:bg-muted/50', + 'relative p-2.5 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px] group-hover:bg-interactive-hover', className )} {...props} diff --git a/apps/shade/tailwind.theme.css b/apps/shade/tailwind.theme.css index 592842a5937..a47c08de29c 100644 --- a/apps/shade/tailwind.theme.css +++ b/apps/shade/tailwind.theme.css @@ -207,6 +207,7 @@ --color-tab-hover: var(--tab-hover); --color-tab-active: var(--tab-active); --color-button-hover: var(--button-hover); + --color-interactive-hover: var(--interactive-hover); /* Typography */ /* ---------------------------------------------------------------------- */ diff --git a/apps/shade/theme-variables.css b/apps/shade/theme-variables.css index 8e12775f54f..26f21a9500c 100644 --- a/apps/shade/theme-variables.css +++ b/apps/shade/theme-variables.css @@ -73,6 +73,8 @@ --tab-hover: var(--color-gray-100); --tab-active: color-mix(in oklab, var(--color-gray-700) 10%, transparent); --button-hover: var(--color-gray-100); + --interactive-hover: var(--button-hover); + --members-sticky-hover-bg: var(--interactive-hover); --mobile-navbar-height: 64px; } @@ -83,6 +85,8 @@ --tab-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); --tab-active: color-mix(in oklab, var(--color-gray-900) 60%, transparent); --button-hover: color-mix(in oklab, var(--color-gray-900) 30%, transparent); + --interactive-hover: var(--button-hover); + --members-sticky-hover-bg: color-mix(in oklab, var(--color-gray-900) 30%, var(--background) 70%); --background: var(--color-black); --foreground: var(--color-gray-200); From e4aa7a1338c8b29bb9b528ff38393f38d8336372 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 13:28:25 +0200 Subject: [PATCH 12/37] =?UTF-8?q?=F0=9F=8E=A8=20Updated=20dark=20mode=20to?= =?UTF-8?q?oltips=20to=20use=20the=20elevated=20surface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added dark:bg-popover dark:text-popover-foreground to the Shade TooltipContent so every default tooltip lifts above the page using the elevated colour instead of inverting to a light-on-dark pill - Added dark:bg-popover to the TrendBadge's custom tooltip override so the Analytics overview trend tooltip stops melting into the page --- apps/shade/src/components/ui/tooltip.tsx | 2 +- apps/shade/src/components/ui/trend-badge.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/shade/src/components/ui/tooltip.tsx b/apps/shade/src/components/ui/tooltip.tsx index 31af5d4672e..262ce1165a2 100644 --- a/apps/shade/src/components/ui/tooltip.tsx +++ b/apps/shade/src/components/ui/tooltip.tsx @@ -19,7 +19,7 @@ const TooltipContent = React.forwardRef< ( {badge} - + {tooltip} From 88277993912e7aa5b265e9550e68f322f0ce4deb Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Thu, 25 Jun 2026 14:07:54 +0200 Subject: [PATCH 13/37] =?UTF-8?q?=F0=9F=8E=A8=20Improved=20dark=20mode=20h?= =?UTF-8?q?over=20surfaces,=20borders,=20and=20elevated=20surface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/tryghost/issue/DES-1397/ - Added a --table-row-hover token (gray-50 light, sidebar-bg dark) and routed the Shade TableCell, the top posts list, and the members list sticky column through it so row hovers settle on the elevated colour instead of the generic interactive hover - Reused --interactive-hover for the analytics top posts overlay, comments list rows, filter dropdown options (CommandItem and the load-more button), the filter add button, and the kpis tab variant - Added a --control-border token (gray-200 light, solid gray-900 dark) for controls that were lacking contrast and applied it to outline / dropdown Button variants, the input-surface recipe, and the filter add button - Bumped --color-sidebar-bg from oklch 0.23 to 0.25 so sidebar, surface-elevated, popover, and table-row-hover lift together - Dropped the members filter add button's border-input override so it picks up the new control border --- .../src/views/comments/components/comments-list.tsx | 2 +- .../src/views/members/components/members-filters.tsx | 2 +- apps/shade/src/components/patterns/filters.tsx | 4 ++-- apps/shade/src/components/ui/button.tsx | 4 ++-- apps/shade/src/components/ui/command.tsx | 2 +- apps/shade/src/components/ui/input-surface.ts | 2 +- apps/shade/src/components/ui/table.tsx | 2 +- apps/shade/src/components/ui/tabs.tsx | 2 +- apps/shade/tailwind.theme.css | 2 ++ apps/shade/theme-variables.css | 10 +++++++--- .../src/views/Stats/Overview/components/top-posts.tsx | 2 +- 11 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/posts/src/views/comments/components/comments-list.tsx b/apps/posts/src/views/comments/components/comments-list.tsx index cb871a4a6c7..7da430c3e27 100644 --- a/apps/posts/src/views/comments/components/comments-list.tsx +++ b/apps/posts/src/views/comments/components/comments-list.tsx @@ -136,7 +136,7 @@ function CommentsList({
{ // Close sidebar when clicking on a comment in the main list diff --git a/apps/posts/src/views/members/components/members-filters.tsx b/apps/posts/src/views/members/components/members-filters.tsx index da2ef27ae1e..3bf73216401 100644 --- a/apps/posts/src/views/members/components/members-filters.tsx +++ b/apps/posts/src/views/members/components/members-filters.tsx @@ -118,7 +118,7 @@ const MembersFilters: React.FC = ({ const hasFilters = filters.length > 0; const showIconOnlyTrigger = iconOnly && !hasFilters; const addFilterButtonClassName = cn( - 'border-input bg-white dark:bg-background', + 'bg-white dark:bg-background', showIconOnlyTrigger && 'min-w-[34px] gap-0 px-2 text-[0px] lg:min-w-0 lg:gap-1.5 lg:px-3 lg:text-sm !px-3' ); diff --git a/apps/shade/src/components/patterns/filters.tsx b/apps/shade/src/components/patterns/filters.tsx index 977cd3bdf12..d8548a9e2b4 100644 --- a/apps/shade/src/components/patterns/filters.tsx +++ b/apps/shade/src/components/patterns/filters.tsx @@ -303,7 +303,7 @@ const filterAddButtonVariants = cva( variants: { variant: { solid: 'border border-input hover:bg-secondary/60', - outline: 'border border-border hover:bg-accent' + outline: 'border border-control-border hover:bg-interactive-hover dark:hover:bg-interactive-hover' }, size: { lg: 'h-10 gap-1.5 px-4 text-sm [&_svg:not([class*=size-])]:size-4', @@ -1395,7 +1395,7 @@ function SelectOptionsList({ {(selectedOptions.length > 0 || unselectedOptions.length > 0) && }