Skip to content

feat: dark mode#3246

Open
HarishChandran3304 wants to merge 22 commits intofrappe:developfrom
HarishChandran3304:feat/dark-mode
Open

feat: dark mode#3246
HarishChandran3304 wants to merge 22 commits intofrappe:developfrom
HarishChandran3304:feat/dark-mode

Conversation

@HarishChandran3304
Copy link
Copy Markdown

Summary

Adds dark mode support to Helpdesk, building on the approach from this PR.

Screen.Recording.2026-04-24.at.7.41.32.PM.mov

Technical Decisions

Color token migration

  • Ran frappe-ui's update-tailwind-classes.js script (ref) across desk/src to move from literal Tailwind shades (bg-gray-100, text-white, etc.) to semantic tokens.
  • Manual audit for shades the script couldn't auto-map (bg-gray-400, bg-gray-600, border-gray-100, bg-black, text-black). Chose closest exact token per the frappe-ui colors table (surface-gray-4 / surface-gray-5 / surface-gray-7 / ink-gray-9).

Theme toggle

  • useTheme() wired in App.vue for initialization.
  • Single "Toggle theme" item in the user dropdown
  • Default = light

Email HTML sanitization

  • stripEmailColors() util removes color / background / background-color / border-color inline styles and bgcolor / color attributes from inbound email HTML so the iframe's themed CSS controls foreground/background. Keeps font, spacing, layout, alignment intact.

cc: @RitvikSardana @niraj2477

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds dark mode support across the Helpdesk desk UI by migrating hardcoded Tailwind color utilities to semantic design tokens, wiring a theme toggle into the user menus, and ensuring inbound email HTML renders with theme-controlled colors.

Changes:

  • Replaced many text-gray-* / bg-white / border-gray-* utilities with semantic token classes (text-ink-*, bg-surface-*, border-outline-*) to make dark mode viable.
  • Added theme initialization + theme toggle entries in sidebar user dropdowns (desktop + mobile).
  • Added email-color stripping + a reactive data-theme mirror to keep iframe-based email content and theme-aware charts in sync.

Reviewed changes

Copilot reviewed 141 out of 141 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
desk/src/utils.ts Adds stripEmailColors() and a reactive dataTheme mirror for theme-aware JS components.
desk/src/stores/ticketStatus.ts Migrates status color map entries to semantic ink tokens.
desk/src/pages/ticket/TicketTextEditor.vue Migrates placeholder/background colors to semantic tokens.
desk/src/pages/ticket/TicketNew.vue Migrates form label + required indicator colors to semantic tokens.
desk/src/pages/ticket/TicketFeedback.vue Migrates text + required indicator colors to semantic tokens.
desk/src/pages/ticket/TicketCustomerTemplateFields.vue Migrates field label/value colors to semantic tokens.
desk/src/pages/ticket/TicketConversation.vue Migrates heading + timeline border colors to semantic tokens.
desk/src/pages/ticket/TicketCommunication.vue Migrates meta text/icon colors to semantic tokens.
desk/src/pages/ticket/MobileTicketAgent.vue Migrates button/background/text colors to semantic tokens.
desk/src/pages/knowledge-base/NewArticle.vue Migrates editor border colors to semantic tokens.
desk/src/pages/knowledge-base/KnowledgeBaseCustomer.vue Migrates heading text colors to semantic tokens.
desk/src/pages/knowledge-base/KnowledgeBaseAgent.vue Migrates heading text colors to semantic tokens.
desk/src/pages/knowledge-base/Article.vue Migrates editor background + meta colors to semantic tokens.
desk/src/pages/home/components/RecentFeedback.vue Makes ECharts options theme-aware via CSS variables + dataTheme.
desk/src/pages/home/components/PendingTickets.vue Migrates table borders/hover states + SLA warning text color to semantic tokens.
desk/src/pages/home/components/AvgTimeMetrics.vue Makes chart colors/theme tooltips driven by CSS vars + dataTheme.
desk/src/pages/home/components/AvgTimeCard.vue Migrates percentage change colors to semantic tokens.
desk/src/pages/home/components/AgentTicketsCard.vue Migrates percentage change colors to semantic tokens.
desk/src/pages/home/Home.vue Migrates header title color to semantic tokens.
desk/src/pages/desk/customer/Customers.vue Migrates header title color to semantic tokens.
desk/src/pages/desk/customer/CustomerDialog.vue Migrates dialog title color to semantic tokens.
desk/src/pages/desk/contact/Contacts.vue Migrates header title color to semantic tokens.
desk/src/pages/desk/contact/ContactDialog.vue Migrates dialog title + body text colors to semantic tokens.
desk/src/pages/call-logs/CallLogs.vue Migrates header title color to semantic tokens.
desk/src/pages/MobileNotifications.vue Migrates list hover + text/icon colors to semantic tokens.
desk/src/pages/InvalidPage.vue Migrates empty/error page text color to semantic tokens.
desk/src/main.js Migrates toast icon color to semantic token.
desk/src/index.css Sets base background and color-scheme for light/dark.
desk/src/components/view-controls/SortBy.vue Migrates popover/background/border/icon colors to semantic tokens.
desk/src/components/view-controls/Filter.vue Migrates popover/background/border/text colors to semantic tokens.
desk/src/components/ticket/TicketSplitModal.vue Migrates banner ring/text colors to semantic tokens.
desk/src/components/ticket/TicketMergeModal.vue Migrates modal backgrounds/rings/code backgrounds to semantic tokens.
desk/src/components/ticket/TicketFeedback.vue Migrates feedback text colors to semantic tokens.
desk/src/components/ticket/TicketCustomerSidebar.vue Migrates label/value colors to semantic tokens.
desk/src/components/ticket/TicketAgentSidebar.vue Migrates action button + feedback section colors to semantic tokens.
desk/src/components/ticket/TicketAgentDetails.vue Migrates label text color to semantic tokens.
desk/src/components/ticket/TicketAgentContact.vue Migrates hover text color to semantic tokens.
desk/src/components/ticket/TicketAgentActivities.vue Migrates timeline border + icon/empty-state colors to semantic tokens.
desk/src/components/ticket/ActivityHeader.vue Migrates header title color to semantic tokens.
desk/src/components/ticket-agent/FeedbackBox.vue Migrates optional text color to semantic tokens.
desk/src/components/ticket-agent/AssignTo.vue Adds transparent input styling for dark mode + migrates label color.
desk/src/components/notifications/Notifications.vue Migrates notification drawer backgrounds/hover/text colors to semantic tokens.
desk/src/components/layouts/Sidebar.vue Adds theme toggle menu item; migrates sidebar colors to semantic tokens.
desk/src/components/layouts/SettingsLayoutBase.vue Migrates description text color to semantic tokens.
desk/src/components/layouts/MobileSidebar.vue Adds theme toggle menu item; migrates overlay/sidebar colors to semantic tokens.
desk/src/components/knowledge-base/CategoryFolder.vue Migrates borders/text colors to semantic tokens.
desk/src/components/knowledge-base/ArticleFeedback.vue Migrates container + text colors to semantic tokens.
desk/src/components/knowledge-base/ArticleCard.vue Migrates borders/text colors to semantic tokens.
desk/src/components/icons/ThumbsUpIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/ThumbsUpFilledIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/ThumbsDownIcon.vue Removes hardcoded background rect; switches fill to currentColor.
desk/src/components/icons/ThumbsDownFilledIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/GlobeIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/FieldDependencyIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/ExternalLinkIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/CopyIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/icons/AgentIcon.vue Switches SVG fill to currentColor for theming.
desk/src/components/frappe-ui/MultiSelectCombobox.vue Migrates input/popover/active-state colors to semantic tokens.
desk/src/components/frappe-ui/Link.vue Migrates label + required asterisk colors to semantic tokens.
desk/src/components/frappe-ui/DurationPicker.vue Migrates borders/hover/text colors to semantic tokens.
desk/src/components/frappe-ui/DurationField.vue Migrates popover/background/border/hover/text colors to semantic tokens.
desk/src/components/frappe-ui/Autocomplete.vue Migrates input/popover/active-state tokens + transparent input bg.
desk/src/components/desk/global/NewContactDialog.vue Migrates label + required indicator colors to semantic tokens.
desk/src/components/desk/global/AddNewAgentsDialog.vue Migrates container/item/hover colors to semantic tokens.
desk/src/components/conditions-filter/CFConditions.vue Migrates container border color to semantic tokens.
desk/src/components/conditions-filter/CFCondition.vue Migrates connective label text color to semantic tokens.
desk/src/components/command-palette/CPGroupResult.vue Migrates active/background/icon/text colors to semantic tokens.
desk/src/components/command-palette/CPGroup.vue Migrates active/background/icon/text colors to semantic tokens.
desk/src/components/command-palette/CP.vue Migrates input placeholder + border colors to semantic tokens.
desk/src/components/UserMenu.vue Migrates text/icon colors to semantic tokens.
desk/src/components/UniInput.vue Migrates label + required indicator colors to semantic tokens.
desk/src/components/TypingIndicator.vue Migrates indicator text color to semantic tokens.
desk/src/components/TicketField.vue Migrates label + required indicator colors to semantic tokens.
desk/src/components/SidebarLink.vue Migrates base text/icon colors + selected/hover classes to semantic tokens.
desk/src/components/Settings/Telephony/Telephony.vue Migrates hover backgrounds to semantic tokens.
desk/src/components/Settings/Teams/TeamsList.vue Migrates list header + hover backgrounds to semantic tokens.
desk/src/components/Settings/Teams/TeamEdit.vue Migrates list header + hover/icon colors to semantic tokens.
desk/src/components/Settings/Sla/SlaWorkDaysList.vue Migrates borders/header text/required indicator colors to semantic tokens.
desk/src/components/Settings/Sla/SlaPriorityListItem.vue Migrates hover bg + popover bg to semantic tokens.
desk/src/components/Settings/Sla/SlaPriorityList.vue Migrates borders/header text/required indicator colors to semantic tokens.
desk/src/components/Settings/Sla/SlaPolicyView.vue Migrates code block bg + borders to semantic tokens.
desk/src/components/Settings/Sla/SlaPolicyListItem.vue Migrates hover background to semantic tokens.
desk/src/components/Settings/Sla/SlaPolicyList.vue Migrates list header text to semantic tokens.
desk/src/components/Settings/Sla/SlaPolicies.vue Migrates search input styling to token-friendly classes.
desk/src/components/Settings/Sla/SlaAssignmentConditions.vue Migrates empty-state border/text colors to semantic tokens.
desk/src/components/Settings/Sla/Modals/WorkDayModal.vue Migrates error border class to semantic token.
desk/src/components/Settings/Sla/Modals/EditResponseResolutionModal.vue Migrates picker bg/text/placeholder colors to semantic tokens.
desk/src/components/Settings/SettingsModal.vue Migrates sidebar tab colors to semantic tokens.
desk/src/components/Settings/SettingsLayoutHeader.vue Migrates description text color to semantic tokens.
desk/src/components/Settings/SavedReplies/components/PreviewDialog.vue Migrates loading overlay color to semantic token.
desk/src/components/Settings/SavedReplies/SavedRepliesList.vue Migrates list header + hover backgrounds to semantic tokens.
desk/src/components/Settings/Profile/Profile.vue Migrates loading overlay color to semantic token.
desk/src/components/Settings/Holiday/RecurringHolidaysList.vue Migrates borders/header/empty text colors to semantic tokens.
desk/src/components/Settings/Holiday/HolidaysTableView.vue Migrates borders/header/empty text + row input bg to semantic tokens.
desk/src/components/Settings/Holiday/HolidaysCalendarView.vue Migrates border + indicator dot colors to semantic tokens.
desk/src/components/Settings/Holiday/Holidays.vue Migrates search input styling to token-friendly classes.
desk/src/components/Settings/Holiday/HolidayView.vue Migrates legend swatch background to semantic token.
desk/src/components/Settings/Holiday/HolidayListItem.vue Migrates hover background to semantic tokens.
desk/src/components/Settings/Holiday/HolidayList.vue Migrates header text color to semantic tokens.
desk/src/components/Settings/Holiday/HLCalender.vue Migrates text/background/border colors to semantic tokens.
desk/src/components/Settings/General/components/WorkflowKnowledgebaseSettings.vue Migrates section header color to semantic tokens.
desk/src/components/Settings/General/components/TicketSettings.vue Migrates header + helper text color to semantic tokens.
desk/src/components/Settings/General/components/LogoUpload.vue Migrates border color to semantic tokens.
desk/src/components/Settings/General/components/Branding.vue Migrates section header color to semantic tokens.
desk/src/components/Settings/General/General.vue Migrates section header color to semantic tokens.
desk/src/components/Settings/FieldDependency/FieldDependencyValueSelection.vue Migrates search icon colors to semantic tokens.
desk/src/components/Settings/FieldDependency/FieldDependencyList.vue Migrates list header + hover backgrounds to semantic tokens.
desk/src/components/Settings/EmailProviderIcon.vue Migrates provider tile colors to semantic tokens.
desk/src/components/Settings/EmailNotifications/NotificationList.vue Migrates hover background to semantic tokens.
desk/src/components/Settings/EmailNotifications/Notification.vue Migrates helper text color to semantic tokens.
desk/src/components/Settings/EmailEdit.vue Migrates banner ring/text + helper text + toast icon colors to semantic tokens.
desk/src/components/Settings/EmailAdd.vue Migrates banner ring/text + link color to semantic tokens.
desk/src/components/Settings/EmailAccountList.vue Migrates list header text to semantic tokens.
desk/src/components/Settings/EmailAccountCard.vue Migrates border/hover background to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssignmentSchedule.vue Migrates borders/header text to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssignmentRulesSection.vue Migrates empty-state border/text colors to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssignmentRulesListView.vue Migrates header text colors to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssignmentRulesList.vue Migrates search input styling to token-friendly classes.
desk/src/components/Settings/Assignment Rules/AssignmentRuleView.vue Migrates popover backgrounds + hover/bg + borders to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssignmentRuleListItem.vue Migrates hover background to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssigneeSearch.vue Migrates popover/input bg + active/empty colors to semantic tokens.
desk/src/components/Settings/Assignment Rules/AssigneeRules.vue Migrates popover bg + badge colors to semantic tokens.
desk/src/components/Settings/Agents.vue Migrates search input styling + list header colors to semantic tokens.
desk/src/components/SelectDropdown.vue Migrates dropdown background to semantic tokens.
desk/src/components/SearchMultiSelect.vue Migrates avatar ring color to token variable.
desk/src/components/SearchArticles.vue Migrates hover/text colors to semantic tokens.
desk/src/components/SavedRepliesSelectorModal.vue Migrates hover/overlay/empty text/editor colors to semantic tokens.
desk/src/components/PageTitle.vue Migrates title color to semantic tokens.
desk/src/components/MultipleAvatar.vue Migrates ring color to token variable.
desk/src/components/MultiSelectInput.vue Migrates input/popover/active/text colors to semantic tokens.
desk/src/components/MultiSelect.vue Migrates container background to semantic tokens.
desk/src/components/HistoryBox.vue Migrates text colors to semantic tokens.
desk/src/components/EmailEditor.vue Migrates label + quote border colors to semantic tokens.
desk/src/components/EmailContent.vue Strips inbound email colors + keeps iframe theme in sync with app theme.
desk/src/components/EmailArea.vue Migrates container background to semantic tokens.
desk/src/components/ConfirmDialog.vue Migrates message text color to semantic tokens.
desk/src/components/CommunicationArea.vue Migrates active-state button backgrounds to semantic tokens.
desk/src/components/CommentBox.vue Migrates header/action colors and partial reaction state to tokens.
desk/src/components/Autocomplete.vue Migrates input/popover/active-state colors to semantic tokens.
desk/src/components/Apps.vue Migrates popover/list item colors to semantic tokens.
desk/src/App.vue Initializes theme and migrates toast icon colors to semantic tokens.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread desk/src/utils.ts
Comment on lines +812 to +817
div.querySelectorAll("[bgcolor]").forEach((el) =>
el.removeAttribute("bgcolor")
);
div.querySelectorAll("font[color]").forEach((el) =>
el.removeAttribute("color")
);
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The comment says this removes bgcolor/color attributes, but the implementation only removes color on font[color]. If the goal is to strip color attributes generally, use a broader selector (e.g., [color]) or update the comment to match the narrower behavior.

Copilot uses AI. Check for mistakes.
Comment thread desk/src/App.vue
Comment on lines +26 to 31
if (!localStorage.getItem("theme")) {
localStorage.setItem("theme", "light");
}
useTheme();

onMounted(() => {
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

localStorage is accessed at module initialization time. In environments where window/localStorage isn’t available (SSR, certain test setups, pre-render tooling), this will throw before the app mounts. Wrap this in a typeof window !== 'undefined' guard (and/or move it into onMounted).

Suggested change
if (!localStorage.getItem("theme")) {
localStorage.setItem("theme", "light");
}
useTheme();
onMounted(() => {
onMounted(() => {
if (typeof window !== "undefined") {
if (!window.localStorage.getItem("theme")) {
window.localStorage.setItem("theme", "light");
}
useTheme();
}

Copilot uses AI. Check for mistakes.
Comment on lines +15 to 16
<p class="text-p-sm text-ink-gray-7 max-w-md text-ink-gray-6">
{{ description }}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The description text has two conflicting text color utility classes (text-ink-gray-7 and text-ink-gray-6) on the same element. Only the latter will effectively apply, and it makes the intended color unclear—remove one of them (likely keep a single semantic token).

Copilot uses AI. Check for mistakes.
Comment on lines +10 to 12
<p class="text-p-sm text-ink-gray-7 max-w-md text-ink-gray-6">
{{ description }}
</p>
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This paragraph includes two different text-ink-gray-* classes (text-ink-gray-7 and text-ink-gray-6) which conflict; only one should be present to avoid accidental overrides and to make the intended description color clear.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to 14
class="flex-col rounded-l-lg w-56 shrink-0 bg-surface-menu-bar m-1 bg-surface-menu-bar overflow-y-auto hide-scrollbar"
>
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

This class list repeats bg-surface-menu-bar twice, which is redundant and makes future theme tweaks harder to reason about. Remove the duplicate class.

Copilot uses AI. Check for mistakes.
:class="
reaction.current_user_reacted
? 'bg-blue-100 text-blue-700 hover:bg-blue-200'
? 'bg-surface-blue-2 text-blue-700 hover:bg-blue-200'
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The “current user reacted” classes still use hardcoded Tailwind colors (text-blue-700, hover:bg-blue-200). These won’t adapt to dark mode the way semantic tokens do, and can end up with poor contrast. Replace them with the appropriate semantic ink/surface tokens for the reacted state.

Suggested change
? 'bg-surface-blue-2 text-blue-700 hover:bg-blue-200'
? 'bg-surface-blue-2 text-ink-blue-3 hover:bg-surface-blue-3'

Copilot uses AI. Check for mistakes.
Comment on lines +3 to 5
class="flex size-8 cursor-pointer items-center justify-center rounded-full bg-surface-gray-2 hover:bg-surface-gray-3"
:class="{ 'ring-2 ring-blue-500': selected }"
>
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The selection ring color is still using a hardcoded Tailwind color (ring-blue-500). For consistent theming/dark-mode support, this should be switched to a semantic token-based ring/outline class that matches the design system.

Copilot uses AI. Check for mistakes.
@RitvikSardana
Copy link
Copy Markdown
Member

@HarishChandran3304

please resolve conflicts & the changes suggested by Co pilot, some of them are unrelated, you can ignore those.

@RitvikSardana RitvikSardana removed the TBR To be reviewed label Apr 30, 2026
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.

4 participants