Skip to content

🎨 Improved dark mode across Shade and Ghost admin (DES-1397)#28895

Merged
peterzimon merged 52 commits into
mainfrom
DES-1397/shade-dark-mode-fixes
Jun 30, 2026
Merged

🎨 Improved dark mode across Shade and Ghost admin (DES-1397)#28895
peterzimon merged 52 commits into
mainfrom
DES-1397/shade-dark-mode-fixes

Conversation

@peterzimon

@peterzimon peterzimon commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

A pass over Ghost admin's dark mode covering the Shade design system, the React admin shell, and the legacy Ember admin. The goal was to bring every surface, control, and state up to a consistent visual standard — no more isolated islands of lighter / darker fills, no green focus rings on form controls, no oversaturated greys.

ref https://linear.app/tryghost/issue/DES-1397/

Surface palette

  • New page surface: --background is now oklch(0.178 0.003 271) (≈ #101114) — slightly darker, very low chroma.
  • New --color-sidebar-bg (oklch(0.204 0.004 258)#15171A) cascades to --sidebar-background, --surface-elevated, --popover, and --table-row-hover so the sidebar, cards, popovers, and table-row hovers share one elevated value.
  • Promoted --surface-elevated-2 (oklch(0.235 0.004 260)) for surfaces that sit ABOVE an elevated surface — sidebar user menu, dropdowns, modals, automation feature image placeholder.
  • Halved the chroma of the full gray-* / grey-* scale in the .dark block so dark mode reads as neutral grey rather than tinted blue.
  • Ember --main-bg-color, --dark-main-bg-color, .gh-main-white, the editor title / excerpt / btn / post-status / word-count, the fullscreen modal overlay, and the publish setting form all routed to the new page surface so they stop reading as light islands inside the editor and modals.

Interactive surfaces (semantic hover/active tokens)

  • --interactive-hover — generic catch-all hover (DropdownMenu items, SelectItem, CommandItem, filter options, load-more, etc.).
  • --button-hover — outline / dropdown Button hover (aliased to --interactive-hover).
  • --tab-hover / --tab-active — Tabs (button, button-sm, pill, kpis), PageMenu, sidebar menu items, active Toggle in dark.
  • --table-row-hover — Shade Table row hover, also used by table-like lists (top posts overlay, comments list, members sticky cell, legacy Ember posts list hover).

Borders

  • New --control-border for form controls (inputs, outline button, dropdown trigger, filter add button) — lifts a step above the base border so controls keep contrast against the page surface.
  • Default base border raised to gray-900/50 so dividers and outlines read at the right contrast against the new surfaces; sidebar right border now references --border directly.

Shadows

  • Added direct .dark .shadow-* overrides (xs2xl) so the standard shadow-md / shadow-lg utilities pick up alpha-0.45–0.8 shadows in dark automatically. Tailwind v4 inlines @theme --shadow-* at compile time, so token-level overrides don't propagate; direct selectors do.

Dropdowns (DropdownMenu, Select, Popover — one visual contract)

  • All three Radix-backed floating menus share: bg-surface-elevated-2, border border-border/60 (light) / dark:border-border/30 (dark), shadow-md.
  • Inner Command switched to bg-transparent so it inherits the wrapping PopoverContent surface instead of overlaying with bg-popover.

Modals

  • DialogOverlay / AlertDialogOverlay: dark uses transparent black (bg-black/60) instead of a white tint, no backdrop blur in either mode.
  • DialogContent routed to bg-surface-elevated-2.
  • Bulk delete modal: dropped the text-sm wrapper so the body inherits base body size.
  • DialogDescription: dropped text-sm so descriptions follow base body size too.

Sidebar

  • Background uses the new --color-sidebar-bg; right border now references --border.
  • Sidebar foreground dimmed to gray-500 so the active item carries the visual weight via --sidebar-accent-foreground.
  • Search trigger: white in light, gray-900/30 in dark, with hover lifting border + text to gray-400.
  • Inactive menu hover dark:hover:bg-gray-900/30; active item stays at --sidebar-accent (which references --tab-active); active-on-hover keeps the active fill (no lightening).
  • New post + icon, the count badges, and the user menu email all share the gray-800 secondary tone in dark (matches ⌘K).
  • Sidebar user menu DropdownMenuContent uses --surface-elevated-2 so it lifts above the sidebar.

Form controls (Shade input recipe + Ember inputs)

  • Shade inputSurface recipe: border-control-border, bg-transparent in dark.
  • Ember .gh-input / .gh-select / .gh-input-x / settings sidebar select / date-time picker: transparent fill in dark with a visible --lightgrey-l2 border, neutral midgrey focus border + 25% midgrey ring (no more green focus).
  • Multi-tag selector (.ember-power-select-multiple-trigger): transparent bg + visible border.
  • Label/tag selector (.gh-input-x + .ember-power-select-trigger): transparent + visible border + ring on focus.
  • Removed the bare input:focus dark rule so the nested <input> inside the date/time picker doesn't double-ring.

Toggle + ToggleGroup

  • Light unchanged: grey container, white pill + shadow on the active item.
  • Dark: container becomes bg-background with border-border, 9px radius (--input-group-radius). Active uses bg-tab-active (no more inverted bg-surface-elevated-2 look).
  • Switch in dark: green track + white thumb.

Tabs

  • button, button-sm, pill, kpis variants all use dark:bg-tab-hover and dark:bg-tab-active (active-on-hover preserved).
  • Tabs count pill, TrendBadge tooltip, default Tooltip content → use --popover / --surface-elevated so tooltips lift in dark.

Avatars

  • Avatar fill saturation dropped from 7545 everywhere (Shade Avatar, Posts newsletter feedback, legacy Ember gh-member-avatar) so members list / comments / member details / member activity stop reading as oversaturated.

Primary button

  • Dark --primary lifted from gray-100 (~97% lightness, near-white) to gray-200 (~95%) so the default Button doesn't burn eyes.

Destructive

  • Dark --destructive lifted from red-900 to red-500 so destructive menu items / buttons (e.g. bulk Delete in members menu) read clearly.

Theme switching

  • Added a global html.theme-switching kill-rule (transition: none !important; animation-duration: 0s !important) wrapping the Ember _setAdminTheme handler. The class is added before the .dark flip and released via double-rAF after the dark stylesheet finishes loading. Stops the per-element crossfade so theme swaps paint in a single frame.

Other surface refinements

  • Analytics overview "Latest post performance" card: dropped the muted gradient background.
  • Analytics overview / Post analytics / Stats headers: dark:bg-blackdark:bg-background so the sticky header tracks the new page surface.
  • Top posts feature image placeholder: routed to --surface-elevated-2.
  • Help cards (Analytics overview, Members empty-state, Automations list): hover uses --table-row-hover.
  • Automations editor: top bar + step sidebar → bg-surface-elevated, canvas wrapper + React Flow background → bg-background. Automations list row hover promoted to the row (covers the column gap).
  • Ember .ember-power-select-multiple-option chip background lifted to --lightgrey so label/tag chips are visible against the new page surface.

Storybook docs

  • New token sections covering surface elevation, interactive surfaces, control-border, and the three-Radix-primitive dropdown contract.
  • Shadow story annotated with the dark mode override pattern.

Outline / dropdown Button bg

  • Both variants now bg-transparent so they inherit whatever surface sits behind them (no more carved rectangles over elevated surfaces).

Test plan

  • Toggle between dark and light — UI swaps in one frame (no per-element crossfade), hover transitions still work afterwards.
  • Members list, comments list, members detail, member activity — avatars read as muted, not saturated.
  • Members list — Filter button, ... menu, bulk action modals (add label, remove label, delete) all match the new dark style.
  • Analytics overview / Web / Newsletters / Growth — KPI tabs, top posts list, feature-image placeholder, header backgrounds, date range Select.
  • Post analytics — header background tracks the page.
  • Tags list / Comments list / Automations list — row hover matches Shade Table.
  • Editor — title, excerpt, btn, post-status, word-count all blend with the canvas; full-screen modals (publish flow, preview, update flow) use the new page surface.
  • Settings sidebar (post settings) — inputs and date/time pickers use neutral focus, label/tag selector shows a visible border + ring on focus.
  • Switch toggle reads green in dark.
  • Dropdowns, Selects, and Popovers (including filter options) share one visual.
  • Shade Storybook — Tokens / Colors / Surface and Interactive Surfaces stories document the new tokens.

🤖 Generated with Claude Code

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
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds new Shade surface and interactive design tokens, maps them into Tailwind, and updates shared UI primitives and app surfaces to use the new token-based hover, active, border, and elevation styling. It also expands token documentation and Storybook coverage, adjusts dialog tests for revised description styling, and changes Ghost admin theme switching behavior plus dark stylesheet visuals.

Possibly related PRs

  • TryGhost/Ghost#27109: It updates the same Shade semantic token plumbing and surface/token contract that this PR extends.
  • TryGhost/Ghost#28597: It touches apps/posts/src/views/Automations/components/automations-help-cards.tsx, the same shared help-card surface that this PR restyles.

Suggested reviewers

  • EvanHahn
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: broad dark mode improvements across Shade and Ghost admin.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the dark mode and token updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DES-1397/shade-dark-mode-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jun 25, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit f87feed

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 10m 48s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded 1s View ↗
nx run-many -t test:unit -p @tryghost/activityp... ✅ Succeeded 7m 16s View ↗
nx run @tryghost/admin:build ✅ Succeeded 4m 35s View ↗
nx run ghost-admin:test ✅ Succeeded 2m 57s View ↗
nx run-many -t lint -p @tryghost/activitypub,@t... ✅ Succeeded 1m 16s View ↗
nx run @tryghost/activitypub:test:acceptance ✅ Succeeded 56s View ↗
nx run ghost:build:assets ✅ Succeeded 2s View ↗
nx run ghost:build:tsc ✅ Succeeded 6s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-06-30 10:35:17 UTC

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
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
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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/shade/src/components/ui/sidebar.tsx`:
- Around line 533-537: The sidebar button styles in the component are using
hard-coded dark theme color utilities, which should be replaced with semantic
token-based classes. Update the class strings in the Sidebar menu/button styling
logic (including the variant definitions for the button styles) to remove
`dark:` color utilities like `dark:hover:bg-gray-900/30` and any similar
hard-coded dark text/background classes, and move that visual intent into
semantic tokens defined in `theme-variables.css`. Keep the component’s Tailwind
classes token-driven so the `sidebar.tsx` styles follow Shade’s color
guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d18c7cfe-a7d1-4f5e-8575-9a3c46aa8897

📥 Commits

Reviewing files that changed from the base of the PR and between 37d8b60 and 56491cc.

📒 Files selected for processing (4)
  • apps/admin/src/layout/app-sidebar/nav-content.tsx
  • apps/admin/src/layout/app-sidebar/user-menu.tsx
  • apps/shade/src/components/ui/sidebar.tsx
  • apps/shade/theme-variables.css
✅ Files skipped from review due to trivial changes (1)
  • apps/admin/src/layout/app-sidebar/nav-content.tsx

Comment thread apps/shade/src/components/ui/sidebar.tsx
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
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
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
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
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
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
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
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
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
ref https://linear.app/tryghost/issue/DES-1397/

- Reworked ToggleGroup to sit on the page surface with a border and a
  9px radius (--input-group-radius) so it reads as a flat control bar
  instead of a tinted muted block
- Routed the active Toggle state through --tab-active so it visually
  matches the active Tabs / sidebar surface in both modes
- Promoted the user menu's inline oklch(0.27 0.006 260) into a named
  --surface-elevated-2 token (light = gray-100, dark = oklch 0.27) so
  "above-elevated" popovers and controls share one named value
ref https://linear.app/tryghost/issue/DES-1397/

- Nudged --color-sidebar-bg and --surface-elevated-2 chroma from 0.006
  to 0.009 so the sidebar, elevated panels, popovers, and row hover
  read with a subtle cool tint instead of feeling flat / dull
- Swapped the Analytics top posts feature image placeholder's dark
  background from the inline #36373a to --surface-elevated-2 so it
  sits a touch darker and shares the named elevated-2 surface
ref https://linear.app/tryghost/issue/DES-1397/

- Overrode the full gray-* and grey-* scale inside .dark with the
  same lightness and hue but roughly half the chroma so dark mode
  surfaces, text, and borders feel less tinted and cleaner without
  affecting light mode
ref https://linear.app/tryghost/issue/DES-1397/

- Sourced --tab-active from a slightly more tinted oklch (chroma 0.012)
  instead of the now-desaturated --color-gray-900 so active tabs and
  sidebar items keep a hint of warmth against the cleaner gray scale
- Dropped --control-border from solid gray-900 to gray-900 at 70% so
  outline buttons, inputs, and the dropdown trigger sit between the
  base --border and the previous solid value
ref https://linear.app/tryghost/issue/DES-1397/

- Overrode the .gh-posts-list-item-group hover background in app-dark
  with oklch(0.23 0.009 260) so the legacy posts list matches the
  Shade --table-row-hover surface used by the React tables
- Locked .gh-post-list-feature-image base and hover backgrounds to
  oklch(0.27 0.009 260) so the legacy feature image placeholder
  matches the --surface-elevated-2 colour used by the analytics top
  posts placeholder
ref https://linear.app/tryghost/issue/DES-1397/

- Added dark:data-[state=checked]:bg-green-500 to the Switch track and
  dark:bg-white to the thumb so the checked Switch reads as a green
  toggle with a white circle in dark mode instead of an inverted
  white-track-with-black-thumb
ref https://linear.app/tryghost/issue/DES-1397/

- Added a global html.theme-switching kill-rule that nukes transitions
  and animations on every element and pseudo-element while the class
  is set, so the toggle paints the new theme in a single frame instead
  of crossfading every element with its own duration
- Wrapped _setAdminTheme so it adds .theme-switching before flipping
  the .dark class, then releases it via a double requestAnimationFrame
  after the dark stylesheet finishes loading (covers both first-time
  lazy load and cached subsequent toggles, plus the catch path)
ref https://linear.app/tryghost/issue/DES-1397/

- Repointed --background in dark to oklch(0.178 0.006 271) (#101114)
  so the page sits a touch darker than the previous --color-black
- Repointed --color-sidebar-bg in dark to oklch(0.204 0.007 258)
  (#15171A) which cascades to the sidebar, --surface-elevated,
  --popover, and --table-row-hover
- Routed --card through --background in dark so analytics cards
  sit flat on the new page surface instead of lifting
- Switched the analytics header and Navbar dark:bg-black to
  dark:bg-background so the sticky header tracks the new page colour
ref https://linear.app/tryghost/issue/DES-1397/

- Repointed --main-bg-color and --dark-main-bg-color in app-dark.css
  from #15171A to #101114 so the legacy Ember admin page surface
  matches the new React --background colour
ref https://linear.app/tryghost/issue/DES-1397/

- Repointed .gh-main-white (used by the lexical editor route) to
  --dark-main-bg-color so the editor canvas sits on the new page bg
- Routed .gh-editor-title / .gh-editor-excerpt, .gh-btn-editor,
  .gh-editor-post-status, and .gh-editor-wordcount-container to the
  same token so the title field, header controls, post status pill,
  and word count chip stop reading as lighter islands inside the
  editor view
ref https://linear.app/tryghost/issue/DES-1397/

- Overrode .fullscreen-modal-total-overlay (publish-flow, preview, and
  update-flow editor modals) and .gh-publish-setting-form to use
  --dark-main-bg-color so they sit on the new page surface instead of
  the lighter --white default
ref https://linear.app/tryghost/issue/DES-1397/

- Routed the comments list row hover through bg-table-row-hover so it
  matches the Shade Table row hover and the top posts list instead of
  using the generic interactive hover
- Updated the Ember post list row hover override to oklch(0.204 0.007
  258) / #15171A so the legacy posts list tracks the current
  --table-row-hover value
- Switched the Post analytics header and Navbar dark:bg-black to
  dark:bg-background so the sticky header matches the new page
  surface in dark mode
ref https://linear.app/tryghost/issue/DES-1397/

- Bumped dark --destructive from --color-red-900 to --color-red-500 so
  destructive menu items, links, and buttons (e.g. the bulk Delete
  action in the members ... menu) read clearly against the dark
  surfaces instead of disappearing into them
ref https://linear.app/tryghost/issue/DES-1397/

- Routed the AutomationHeader top bar and the right step sidebar to
  bg-surface-elevated so they lift above the canvas instead of
  blending into the page surface
- Switched the AutomationCanvas wrapper and the React Flow
  --xy-background-color (dark) to --background so the canvas itself
  sits on the new page colour rather than the legacy --surface-page
  / --color-black values
- Also dropped the gap-x-4 hover artifact on the automations list by
  promoting the row hover bg to TableRow so the bg spans the gap
  between cells
ref https://linear.app/tryghost/issue/DES-1397/

- Aligned DropdownMenuContent / DropdownMenuSubContent, SelectContent,
  and PopoverContent on a single visual: bg-surface-elevated-2, a
  subtler border-border/30, and the standard shadow-md so the three
  Radix popover surfaces read as the same component
- Set the inner Command bg to bg-transparent so the filter dropdown
  picks up the PopoverContent surface instead of being overlaid by
  bg-popover (caused the filter dropdown to render at the darker
  --color-sidebar-bg instead of --surface-elevated-2)
- Pulled --surface-elevated-2 in dark from oklch 0.27 down to 0.235 so
  the elevated-2 surface sits between --surface-elevated (0.204) and
  the previous brighter value (cascades to the user menu, the active
  Toggle pill, and the analytics feature image placeholder too)
- Added direct .dark .shadow-xs through .dark .shadow-2xl rules with
  black at 0.35-0.65 alpha so every Shade consumer of the default
  shadow utilities gets meaningfully darker shadows in dark mode
  (Tailwind v4 inlines the @theme --shadow-* values at compile time
  so a token-level override couldn't propagate)
ref https://linear.app/tryghost/issue/DES-1397/

- Dialed avatar fill saturation from 75 to 45 across the Shade Avatar
  component, the Posts newsletter feedback avatar, and the legacy
  gh-member-avatar Ember component so members list, comments list,
  member details, and member activity avatars stop reading as
  oversaturated in both modes
- Reverted light --surface-elevated-2 from --color-gray-100 back to
  --color-white so the dropdown surfaces (DropdownMenu, Select,
  Popover, plus the user menu / placeholder consumers' light variant)
  stop showing as a light grey block in light mode
- Bumped the light mode dropdown border opacity from border-border/30
  to border-border/60 while keeping dark:border-border/30 so the three
  dropdown surfaces have a clearer visible edge against light backgrounds
ref https://linear.app/tryghost/issue/DES-1397/

- Repointed .ember-power-select-multiple-option background from
  var(--white) (= #15171A, now barely distinguishable from the new
  page bg) to var(--lightgrey) so label and tag chips in Ember
  selectors lift above the surface in dark mode
…ode-fixes

# Conflicts:
#	ghost/admin/app/services/feature.js
ref https://linear.app/tryghost/issue/DES-1397/

- Added surface elevation, interactive surfaces, form control border,
  and dropdown surface contract sections to tokens.mdx so the dark
  mode rules introduced on this branch are documented in one place
- Expanded the Colors Surface story to include --surface-elevated-2
  and added a new InteractiveSurfaces story documenting
  --interactive-hover, --button-hover, --tab-hover/active, and
  --table-row-hover with consumer mappings
- Added --control-border to the Border story with the rule that form
  controls use it instead of --border-default
- Annotated the Shadows story to call out the dark mode override
  pattern (direct .dark .shadow-* rules, since Tailwind v4 inlines
  @theme --shadow-* at compile time)
ref https://linear.app/tryghost/issue/DES-1397/

- Swapped DialogOverlay dark:bg-white/8 for dark:bg-black/60 so the
  backdrop dims the page with a transparent black instead of lifting
  it with a white tint, and added dark:data-[state=open]:backdrop-
  blur-none so the open animation doesn't blur in dark
- Repointed DialogContent bg from bg-popover to bg-surface-elevated-2
  so modals sit on the same lifted surface as the dropdown family in
  dark mode
ref https://linear.app/tryghost/issue/DES-1397/

- DialogOverlay: switched dark:bg-white/8 to dark:bg-black/60 and
  removed the open-state backdrop blur in both modes so the dim is
  just a transparent black wash
- DialogContent: routed bg through bg-surface-elevated-2 so modals
  share the lifted surface with the dropdown family
- AlertDialogOverlay: mirrored the new Dialog overlay (dark:bg-black/60,
  no blur)
- Label picker (bulk add / remove label modals): replaced the bespoke
  bg-gray-100 / bg-gray-950 + green focus wrapper with the Shade
  input recipe (bg-surface-elevated, dark:bg-transparent,
  border-control-border, focus-within ring on focus-ring), and
  switched input + placeholder from text-sm to text-control (13px)
- Bulk delete modal: dropped the text-sm wrapper so the body copy
  inherits the base body size; DialogContent gap-5 handles paragraph
  spacing
- Dropped text-sm on DialogDescription so descriptions follow base
  body size too
- Filters add button md size: explicit text-base so the Filter
  trigger in members / comments / analytics renders at base size,
  and the members consumer's lg:text-sm bumped to lg:text-base
@peterzimon peterzimon changed the title 🎨 Improved dark mode sidebar styling 🎨 Improved dark mode across Shade and Ghost admin (DES-1397) Jun 29, 2026
ref https://linear.app/tryghost/issue/DES-1397/

- Updated the two Dialog Components specs that asserted the default
  DialogDescription className to look for text-muted-foreground only
  (we dropped text-sm so the description follows the base body size)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/shade/src/components/ui/dialog.tsx`:
- Line 22: The dialog overlay in the Dialog component is hard-coding a dark-mode
backdrop with a Tailwind dark variant, which violates the semantic token
contract for Shade overlays. Update the overlay styling in the Dialog/content
wrapper to use the existing semantic overlay token instead of
`dark:bg-black/60`, and keep the class usage aligned with the token-based
pattern used by the UI layer.

In `@apps/shade/src/components/ui/dropdown-menu.tsx`:
- Line 50: The dropdown menu classes in the component should not use
`dark:border-border/30`, since Shade components must stay theme-agnostic and
rely on semantic tokens instead. Update the dropdown/menu styling in the
relevant dropdown content wrappers to express the darker border contrast through
an existing or new semantic token/class, and remove the `dark:` color override
from the affected class strings.

In `@apps/shade/src/components/ui/tooltip.tsx`:
- Line 22: The tooltip styling in the `TooltipContent` class list is using
`dark:`-specific color overrides, which should be replaced with semantic token
classes so the component stays theme-agnostic. Update the tooltip surface/text
styling in `tooltip.tsx` to use a single semantic background and foreground
token pair instead of `dark:bg-popover` and `dark:text-popover-foreground`,
keeping the rest of the animation and positioning classes unchanged.

In `@apps/shade/src/docs/tokens/colors.stories.tsx`:
- Around line 175-181: The Colors story in colors.stories.tsx currently groups
table-row-hover with the translucent overlay tokens even though it maps to an
opaque sidebar background. Update the swatches/intro in the relevant story
section to either exclude table-row-hover from the “translucent in dark mode”
description or explicitly label it as the opaque exception, using the swatches
list and surrounding description to keep the guidance accurate.

In `@apps/shade/theme-variables.css`:
- Around line 184-201: The dark shadow utility rules in theme-variables.css are
overriding Tailwind’s shadow stack by assigning box-shadow directly, which
breaks combined shadow and ring styles in dark mode. Update the dark .shadow-xs
through dark .shadow-2xl declarations to set --tw-shadow instead of box-shadow
so Tailwind can preserve ring and focus-visible ring layers. Use the existing
shadow utility selectors as the anchor for the change and keep the same shadow
values in the custom property form.

In `@ghost/admin/app/services/feature.js`:
- Around line 212-214: The `loadStyle()` flow in `feature.js` is restoring the
dark stylesheet state from a stale captured `isDark` value after
`_loadAdminThemeStylesheet()` resolves, which can overwrite a newer theme change
in `system` mode. Update the promise callback so it does not blindly reapply the
old `isDark` state; instead, re-read the current theme state or guard against
intervening `system` theme updates before toggling `link[title=dark]`. Keep the
`releaseSuppression()` path intact, and focus the fix around `loadStyle()` and
the `isDark`/stylesheet update logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4931049-3bc7-4c63-9491-bdd4903c7c92

📥 Commits

Reviewing files that changed from the base of the PR and between 62a44d0 and 4166a3c.

📒 Files selected for processing (51)
  • apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx
  • apps/admin/src/layout/app-sidebar/nav-content.tsx
  • apps/admin/src/layout/app-sidebar/user-menu.tsx
  • apps/posts/src/components/label-picker/label-picker.tsx
  • apps/posts/src/views/Automations/components/automation-header.tsx
  • apps/posts/src/views/Automations/components/automations-help-cards.tsx
  • apps/posts/src/views/Automations/components/automations-list.tsx
  • apps/posts/src/views/Automations/components/canvas/automation-canvas.tsx
  • apps/posts/src/views/Automations/components/canvas/step-sidebar.tsx
  • apps/posts/src/views/PostAnalytics/Newsletter/components/feedback.tsx
  • apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx
  • apps/posts/src/views/Tags/components/tags-list.tsx
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/posts/src/views/members/components/bulk-action-modals/delete-modal.tsx
  • apps/posts/src/views/members/components/members-filters.tsx
  • apps/posts/src/views/members/components/members-help-cards.tsx
  • apps/posts/src/views/members/components/members-list-item.tsx
  • apps/shade/src/components/patterns/filters.tsx
  • apps/shade/src/components/ui/alert-dialog.tsx
  • apps/shade/src/components/ui/avatar.tsx
  • apps/shade/src/components/ui/button.tsx
  • apps/shade/src/components/ui/command.tsx
  • apps/shade/src/components/ui/dialog.tsx
  • apps/shade/src/components/ui/dropdown-menu.tsx
  • apps/shade/src/components/ui/input-surface.ts
  • apps/shade/src/components/ui/popover.tsx
  • apps/shade/src/components/ui/select.tsx
  • apps/shade/src/components/ui/switch.tsx
  • apps/shade/src/components/ui/table.tsx
  • apps/shade/src/components/ui/tabs.tsx
  • apps/shade/src/components/ui/toggle-group.tsx
  • apps/shade/src/components/ui/toggle.tsx
  • apps/shade/src/components/ui/tooltip.tsx
  • apps/shade/src/components/ui/trend-badge.tsx
  • apps/shade/src/docs/tokens.mdx
  • apps/shade/src/docs/tokens/colors.stories.tsx
  • apps/shade/src/docs/tokens/shadows.stories.tsx
  • apps/shade/tailwind.theme.css
  • apps/shade/test/unit/components/ui/dialog.test.tsx
  • apps/shade/theme-variables.css
  • apps/stats/src/views/Stats/Overview/components/latest-post.tsx
  • apps/stats/src/views/Stats/Overview/components/top-posts.tsx
  • apps/stats/src/views/Stats/Overview/overview.tsx
  • apps/stats/src/views/Stats/components/feature-image-placeholder.tsx
  • apps/stats/src/views/Stats/layout/stats-header.tsx
  • ghost/admin/app/components/gh-member-avatar.js
  • ghost/admin/app/services/feature.js
  • ghost/admin/app/styles/app-dark.css
  • ghost/admin/app/styles/app.css
  • ghost/admin/app/styles/components/settings-menu.css
  • ghost/admin/app/styles/patterns/forms.css
✅ Files skipped from review due to trivial changes (31)
  • apps/shade/src/components/ui/trend-badge.tsx
  • ghost/admin/app/styles/app.css
  • apps/posts/src/views/comments/components/comments-list.tsx
  • apps/shade/src/components/ui/switch.tsx
  • apps/posts/src/views/Automations/components/canvas/step-sidebar.tsx
  • apps/posts/src/views/Automations/components/automation-header.tsx
  • apps/posts/src/views/PostAnalytics/Newsletter/components/feedback.tsx
  • apps/stats/src/views/Stats/layout/stats-header.tsx
  • apps/posts/src/views/PostAnalytics/components/post-analytics-header.tsx
  • apps/posts/src/views/Automations/components/canvas/automation-canvas.tsx
  • apps/stats/src/views/Stats/components/feature-image-placeholder.tsx
  • apps/shade/src/components/ui/toggle-group.tsx
  • apps/shade/src/docs/tokens/shadows.stories.tsx
  • apps/admin/src/layout/app-sidebar/user-menu.tsx
  • apps/shade/src/components/ui/toggle.tsx
  • apps/posts/src/views/members/components/bulk-action-modals/delete-modal.tsx
  • apps/shade/src/components/ui/avatar.tsx
  • apps/posts/src/components/label-picker/label-picker.tsx
  • apps/stats/src/views/Stats/Overview/components/latest-post.tsx
  • apps/shade/src/components/ui/table.tsx
  • apps/shade/src/components/ui/input-surface.ts
  • apps/shade/src/components/ui/alert-dialog.tsx
  • apps/shade/tailwind.theme.css
  • apps/posts/src/views/Automations/components/automations-list.tsx
  • apps/posts/src/views/members/components/members-help-cards.tsx
  • apps/shade/src/docs/tokens.mdx
  • apps/shade/src/components/ui/select.tsx
  • apps/admin/src/layout/app-sidebar/nav-content.tsx
  • apps/shade/src/components/ui/popover.tsx
  • apps/admin/src/layout/app-sidebar/app-sidebar-header.tsx
  • apps/shade/src/components/patterns/filters.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/shade/src/components/ui/tabs.tsx

Comment thread apps/shade/src/components/ui/dialog.tsx
ref={ref}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border/60 bg-surface-elevated-2 dark:border-border/30 p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Keep dropdown borders token-driven instead of dark: overrides.

Lines 50 and 69 add dark:border-border/30, which reintroduces theme-specific colour branching inside a Shade component. Please express that darker border contrast through a semantic token/class so these menus stay theme-agnostic at the component layer. As per coding guidelines, "Do not add dark: Tailwind variants for colour in Shade components; use semantic tokens instead".

Also applies to: 69-69

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/shade/src/components/ui/dropdown-menu.tsx` at line 50, The dropdown menu
classes in the component should not use `dark:border-border/30`, since Shade
components must stay theme-agnostic and rely on semantic tokens instead. Update
the dropdown/menu styling in the relevant dropdown content wrappers to express
the darker border contrast through an existing or new semantic token/class, and
remove the `dark:` color override from the affected class strings.

Source: Coding guidelines

ref={ref}
className={cn(
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:bg-popover dark:text-popover-foreground',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Keep tooltip colours semantic rather than dark:-branched.

Line 22 adds dark:bg-popover dark:text-popover-foreground, which pushes theme logic back into the component. Please move this to a single semantic tooltip surface/text token pair and keep the class list theme-agnostic. As per coding guidelines, "Do not add dark: Tailwind variants for colour in Shade components; use semantic tokens instead".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/shade/src/components/ui/tooltip.tsx` at line 22, The tooltip styling in
the `TooltipContent` class list is using `dark:`-specific color overrides, which
should be replaced with semantic token classes so the component stays
theme-agnostic. Update the tooltip surface/text styling in `tooltip.tsx` to use
a single semantic background and foreground token pair instead of
`dark:bg-popover` and `dark:text-popover-foreground`, keeping the rest of the
animation and positioning classes unchanged.

Source: Coding guidelines

Comment thread apps/shade/src/docs/tokens/colors.stories.tsx Outdated
Comment thread apps/shade/theme-variables.css
Comment thread ghost/admin/app/services/feature.js
ref https://linear.app/tryghost/issue/DES-1397/

- Updated the .gh-list-scrolling first-child sticky-column gradients
  in app-dark.css from rgba(21,23,26,...) (the old #15171A page bg) to
  rgba(16,17,20,...) so the fade matches the new #101114 page surface
- Added a dark-mode override for the member activity row hover
  (oklch(0.204 0.004 258)) so the legacy table matches Shade's
  --table-row-hover value used by the React lists; realigned the
  posts-list hover override to the same chroma
- Added 20px left padding on the first cell / 20px right padding on
  the last cell of the member activity table, scoped tightly enough
  to override lists.css's sticky-first-cell padding-left: 0 rule

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ghost/admin/app/styles/layouts/member-activity.css`:
- Around line 380-381: The suppression option row in the member activity layout
is too short, so the label and switch can be clipped or cramped. Update the
styling for this row in member-activity.css to restore a taller height that
accommodates the existing label padding and 1.4rem text, and verify the related
suppression option row selector still aligns correctly with the switch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f11159f7-738c-466d-9860-b3ac9e5370ab

📥 Commits

Reviewing files that changed from the base of the PR and between 4166a3c and 431421c.

📒 Files selected for processing (2)
  • ghost/admin/app/styles/app-dark.css
  • ghost/admin/app/styles/layouts/member-activity.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • ghost/admin/app/styles/app-dark.css

Comment thread ghost/admin/app/styles/layouts/member-activity.css
ref https://linear.app/tryghost/issue/DES-1397/

- Pulled the .dark .shadow-* alphas down from 0.25-0.55 to 0.15-0.4
  so dropdowns, popovers, dialogs, and other shadow consumers feel
  less heavy in dark mode
- Switched AlertDialogContent from bg-background to
  bg-surface-elevated-2 so alert dialogs share the same lifted
  surface as DialogContent and the dropdown family
ref https://linear.app/tryghost/issue/DES-1397/

- Main sticky header (header.tsx) dark:bg-black → dark:bg-background
  so the blur-bar matches the new page surface
- Sidebar feedback box wrapper (sidebar.tsx) and the feedback box
  itself (feedback-box.tsx) dark:bg-black → dark:bg-background
- Search modal sticky search header + Input dark:bg-(--color-popover)
  → dark:bg-surface-elevated-2 so the search field matches the modal
  surface, and added focus-visible:!ring-0 on the Input so the recipe
  focus ring is suppressed
- Profile preview hover card: dark:bg-[#101114] → dark:bg-surface-elevated-2
  and replaced the bespoke triple-stack box-shadow with shadow-lg so the
  card lifts above the page and picks up the dark mode shadow override
- Bumped activitypub from 3.1.52 to 3.1.53 for the visual change
ref https://linear.app/tryghost/issue/DES-1397/

- Feed action buttons (heart / comment / repost) and the ... menu
  trigger: dropped dark:bg-black so they have no rest-state fill in
  dark mode, hover still uses dark:hover:bg-gray-950
- Back button: dark:bg-black/85 → dark:bg-transparent so it doesn't
  show a fill at rest
- Profile avatar wrapper: dark:bg-black → dark:bg-background so the
  avatar border matches the new page surface
- Profile settings avatar wrapper border: dark:border-[#101114] →
  dark:border-surface-elevated-2 so the avatar blends into the modal
  surface; Upload cover image button gets dark:bg-black/40 with
  dark:text-white so it reads as a translucent pill on the cover
- Dialed back the "What's new?" textarea and the reply box
  placeholder colour in dark from gray-400 to gray-700 so it sits at
  a quieter dim tone
- Bumped activitypub from 3.1.53 to 3.1.54 for the visual changes
ref https://linear.app/tryghost/issue/DES-1397/

- Analytics / Growth paid subscription breakdown pie chart: set the
  inter-slice stroke to var(--card) so the 5px separators match the
  card surface instead of rendering as thick white borders against the
  new dark page bg
- Explore testimonials modal: variant order before:dark:bg-black wasn't
  applying in Tailwind v4 so the testimonial bubble carets stayed white
  in dark mode; swapped to dark:before:bg-black on all three quotes so
  the carets match the bubble background

@weylandswart weylandswart left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

ref https://linear.app/tryghost/issue/DES-1397/

- Shade SheetContent base: bg-background → bg-surface-elevated so every
  Sheet (left/right/top/bottom variants) sits on the lifted surface in
  dark mode instead of blending into the page
- Updated the 8 consumer sticky SheetHeader bands (top sources / top
  content / growth sources / locations across Stats and PostAnalytics
  plus the comment thread sidebar) from bg-background/60 to
  bg-surface-elevated/60 so the sticky cover matches the sheet surface
ref https://linear.app/tryghost/issue/DES-1397/

- Switched the .dark .shadow-* overrides from setting box-shadow
  directly to overriding --tw-shadow so Tailwind v4's composed
  box-shadow keeps any --tw-ring-shadow / --tw-inset-shadow layers
  intact. Previously the direct box-shadow nuked the ring stack for
  any component using shadow-* together with focus-visible:ring-*
  (calendar trigger, toggle, sidebar user menu) in dark mode
- Re-read the current isDark in the _setAdminTheme then-callback so
  the stale value captured before _loadAdminThemeStylesheet() doesn't
  stomp the system theme listener if the OS theme flips while the
  dark stylesheet is loading
- Updated the Colors / Interactive surfaces story so it scopes the
  "translucent in dark mode" guidance to the overlay tokens and
  calls out --table-row-hover as the opaque exception (it tracks
  --color-sidebar-bg, used where rows sit over scrolling content)
@peterzimon peterzimon enabled auto-merge (squash) June 30, 2026 10:27
@peterzimon peterzimon merged commit f824e12 into main Jun 30, 2026
41 checks passed
@peterzimon peterzimon deleted the DES-1397/shade-dark-mode-fixes branch June 30, 2026 10:40
peterzimon added a commit that referenced this pull request Jun 30, 2026
ref https://linear.app/ghost/issue/DES-1394

- shade-component-decision: removed ListPage from Pattern examples (it
  belongs in page-templates/), matching the same fix already in
  AGENTS.md and the Storybook MDX docs.
- shade-tokens-not-hex: split the border row into three distinct
  entries — border-border-default (opaque, default), border-border
  (translucent, for floating surfaces with /60-/30 opacity modifiers),
  and border-control-border (form controls). Also clarified the role
  of theme-variables.css (token values + dark overrides) vs
  tailwind.theme.css (Tailwind v4 @theme utility mapping + raw scale).
- shade-no-dark-variants: explained why bg-foo dark:bg-foo is bad —
  not just verbose, but reads as if dark mode is special-cased when
  it isn't.
- shade-dropdown-surface-contract: added `text` language tag to the
  fenced contract block (markdownlint MD040).

Skipped two CodeRabbit notes after verification:
- "dark:bg-transparent not in inputSurface base" — CodeRabbit reviewed
  pre-rebase; after rebasing onto main (with PR #28895 merged), base
  does include dark:bg-transparent. Skill is correct.
- "Add .css to no-dark-variants trigger" — dark: isn't valid CSS-file
  syntax and Shade doesn't use @apply dark: patterns. Low value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants