-
Couldn't load subscription status.
- Fork 48
Fix: Dark mode support improvements #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Updated UserDetailPage, TagDetailPage, and Webview to use adaptive colors: - Replaced hardcoded .white with Color.primaryText/itemBackground - Replaced .black with Color.primaryText - Replaced .gray with Color.secondaryText - Updated CSS colors in Webview for proper dark mode support - Adjusted overlay opacity for better visibility in both themes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove UIUserInterfaceStyle restriction from Info.plist to allow system theme - Create adaptive color system with light/dark variants for all UI elements - Add 15 new color sets with proper dark mode support - Implement appearance settings with light/dark/system options - Update main app to apply user's color scheme preference - Add appearance toggle in settings page with Chinese localization - Update key views to use adaptive colors instead of hardcoded values - Fix navigation bar appearance to adapt to dark mode - Add @retroactive attribute to fix UINavigationController extension warning - Make color properties public to fix accessibility issues This enables full dark mode support throughout the app, automatically adapting to system preferences or user's manual selection. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
- Replace primaryText overlay with dynamic(light: .black, dark: .white) to maintain proper darkening effect in light mode and lightening in dark mode - Fixes build issues by moving SettingActions into SettingState temporarily - Restores original overlay opacity values (0.3/0.1)
The @retroactive attribute is only available in Swift 6, but the CI uses an older version of Swift. Removing this attribute to fix the build failure.
- Change NodeView text from hardcoded black to primaryText color - Add secondaryText color to comment count labels for better visibility - Add Appearance Settings option to Settings page menu
- Use dynamic colors for NodeView text (#666666 light, #CCCCCC dark) - Ensure proper contrast between text and background in both modes
- Changed from semi-transparent bodyText to fully opaque primaryText - Ensures proper contrast in both light and dark modes - Fixes issue where titles were invisible or hard to read
- Added colorScheme environment and store access to HtmlView - Calculate isDark based on app appearance setting (light/dark/system) - Pass correct dark mode state to JavaScript for HTML rendering - HTML now respects app's theme preferences
- Changed reply bar background from static Color.white to adaptive Color.itemBg - Ensures proper contrast in both light and dark themes - Reply input area now properly adapts to theme changes
- Added updateWindowInterfaceStyle method to force window-level theme updates - Set overrideUserInterfaceStyle on all windows when appearance changes - Call window update on app launch and appearance changes - This ensures immediate visual updates without requiring app restart
- Changed from AppSettings.Appearance to AppearanceMode - Fixes build error from incorrect type reference
- Access Store through @EnvironmentObject instead of passing parameters - Avoid type visibility issues with AppearanceMode - Follows established pattern used in other views
- Add LoadMockData action to FeedReducer for loading mock data - Create FeedInfo.mockData() extension with 10 sample feed items - Modify FeedPage to detect when user is not logged in and load mock data - Add visual debug banner indicating mock data is being shown - Mock data includes varied content focusing on UI/UX and dark mode topics This allows testing UI color contrast and layout without requiring actual API data. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed hard-coded colors in ReplyItemView, AuthorInfoView, CreateTopicPage - Updated TwoStepLoginPage with proper text field styling and dynamic colors - Fixed Toast visibility issues in dark mode - Replaced gray/black/white colors with dynamic theme colors - Improved search page and settings page color adaptation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes dark mode support across the V2er iOS app by replacing hard-coded colors with dynamic theme-aware colors and implementing a complete appearance management system.
- Replaced hard-coded colors throughout the app with semantic color names that adapt to dark mode
- Added appearance settings page allowing users to choose between light, dark, or system mode
- Fixed invisible elements in dark mode including toasts, login dialogs, and text elements
Reviewed Changes
Copilot reviewed 45 out of 50 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| V2er/General/V2erApp.swift | Implements appearance mode switching and dynamic navigation bar styling |
| V2er/General/Color.swift | Adds semantic color system with dark mode variants |
| V2er/Info.plist | Removes forced light mode constraint to enable dark mode support |
| V2er/Assets.xcassets/Colors/ | Defines color assets with light/dark variants for semantic colors |
| V2er/State/DataFlow/State/SettingState.swift | Adds appearance mode state management and persistence |
| V2er/View/Settings/AppearanceSettingView.swift | Creates appearance settings UI with mode selection |
| V2er/View/Widget/Toast.swift | Fixes toast visibility in dark mode |
| Multiple View files | Updates color references to use semantic colors |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| <style type='text/css'> | ||
| img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;} | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #000000 \(colorImportant == false ? "" : "!important"); } | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #1C1C1E \(colorImportant == false ? "" : "!important"); } |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS color values are hard-coded and duplicated across multiple color scheme functions. Consider extracting these hex values into constants or using the existing semantic color system to maintain consistency and reduce duplication.
| <style type='text/css'> | ||
| img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;} | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F2 \(colorImportant == false ? "" : "!important"); } | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F7 \(colorImportant == false ? "" : "!important"); } |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS color values are hard-coded and duplicated across multiple color scheme functions. Consider extracting these hex values into constants or using the existing semantic color system to maintain consistency and reduce duplication.
| @media (prefers-color-scheme: light) { | ||
| img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;} | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #000000 \(colorImportant == false ? "" : "!important"); } | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #1C1C1E \(colorImportant == false ? "" : "!important"); } |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS color values are hard-coded and duplicated across multiple color scheme functions. Consider extracting these hex values into constants or using the existing semantic color system to maintain consistency and reduce duplication.
| @media (prefers-color-scheme: dark) { | ||
| img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;} | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F2 \(colorImportant == false ? "" : "!important"); } | ||
| h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: #F2F2F7 \(colorImportant == false ? "" : "!important"); } |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS color values are hard-coded and duplicated across multiple color scheme functions. Consider extracting these hex values into constants or using the existing semantic color system to maintain consistency and reduce duplication.
V2er/View/Widget/TabBar.swift
Outdated
| Text(num.string) | ||
| .font(.system(size: 10)) | ||
| .foregroundColor(.white) | ||
| .foregroundColor(Color.itemBackground) |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Color.itemBackground as text color on a colored background may result in poor contrast. This should use a text color like Color.primaryText or Color.itemBackground should be replaced with a color specifically designed for text on colored backgrounds.
| .foregroundColor(Color.itemBackground) | |
| .foregroundColor(.white) |
| title: "深色模式下的文字对比度优化讨论", | ||
| avatar: "https://cdn.v2ex.com/avatar/2c60/19e1/1_mini.png", | ||
| userName: "testuser1", | ||
| replyUpdate: "12分钟前", | ||
| nodeName: "分享创造", | ||
| nodeId: "create", | ||
| replyNum: "42"), | ||
|
|
||
| Item(id: "mock2", | ||
| title: "SwiftUI 中实现自适应颜色系统的最佳实践,如何在保证视觉美观的同时确保可访问性", | ||
| avatar: "https://cdn.v2ex.com/avatar/c4ca/4238/2_mini.png", | ||
| userName: "swiftdev", | ||
| replyUpdate: "25分钟前 designer 回复了", | ||
| nodeName: "程序员", | ||
| nodeId: "programmer", | ||
| replyNum: "128"), | ||
|
|
||
| Item(id: "mock3", | ||
| title: "iOS 15 新特性:Dynamic Color 深度解析", | ||
| avatar: "https://cdn.v2ex.com/avatar/9b1b/00d4/3_mini.png", | ||
| userName: "iosdev", | ||
| replyUpdate: "1小时前", | ||
| nodeName: "iPhone", | ||
| nodeId: "iphone", | ||
| replyNum: "15"), | ||
|
|
||
| Item(id: "mock4", | ||
| title: "关于 V2EX 客户端的一些改进建议", | ||
| avatar: "https://cdn.v2ex.com/avatar/e3db/1115/4_mini.png", | ||
| userName: "feedback", | ||
| replyUpdate: "2小时前", | ||
| nodeName: "反馈", |
Copilot
AI
Sep 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mock data contains hard-coded URLs and Chinese text that may not be appropriate for all testing environments. Consider using placeholder URLs and localizable strings to make the mock data more maintainable.
| title: "深色模式下的文字对比度优化讨论", | |
| avatar: "https://cdn.v2ex.com/avatar/2c60/19e1/1_mini.png", | |
| userName: "testuser1", | |
| replyUpdate: "12分钟前", | |
| nodeName: "分享创造", | |
| nodeId: "create", | |
| replyNum: "42"), | |
| Item(id: "mock2", | |
| title: "SwiftUI 中实现自适应颜色系统的最佳实践,如何在保证视觉美观的同时确保可访问性", | |
| avatar: "https://cdn.v2ex.com/avatar/c4ca/4238/2_mini.png", | |
| userName: "swiftdev", | |
| replyUpdate: "25分钟前 designer 回复了", | |
| nodeName: "程序员", | |
| nodeId: "programmer", | |
| replyNum: "128"), | |
| Item(id: "mock3", | |
| title: "iOS 15 新特性:Dynamic Color 深度解析", | |
| avatar: "https://cdn.v2ex.com/avatar/9b1b/00d4/3_mini.png", | |
| userName: "iosdev", | |
| replyUpdate: "1小时前", | |
| nodeName: "iPhone", | |
| nodeId: "iphone", | |
| replyNum: "15"), | |
| Item(id: "mock4", | |
| title: "关于 V2EX 客户端的一些改进建议", | |
| avatar: "https://cdn.v2ex.com/avatar/e3db/1115/4_mini.png", | |
| userName: "feedback", | |
| replyUpdate: "2小时前", | |
| nodeName: "反馈", | |
| title: NSLocalizedString("mock_title_1", comment: ""), | |
| avatar: "https://example.com/avatar1.png", | |
| userName: "testuser1", | |
| replyUpdate: NSLocalizedString("mock_reply_update_1", comment: ""), | |
| nodeName: NSLocalizedString("mock_node_name_1", comment: ""), | |
| nodeId: "create", | |
| replyNum: "42"), | |
| Item(id: "mock2", | |
| title: NSLocalizedString("mock_title_2", comment: ""), | |
| avatar: "https://example.com/avatar2.png", | |
| userName: "swiftdev", | |
| replyUpdate: NSLocalizedString("mock_reply_update_2", comment: ""), | |
| nodeName: NSLocalizedString("mock_node_name_2", comment: ""), | |
| nodeId: "programmer", | |
| replyNum: "128"), | |
| Item(id: "mock3", | |
| title: NSLocalizedString("mock_title_3", comment: ""), | |
| avatar: "https://example.com/avatar3.png", | |
| userName: "iosdev", | |
| replyUpdate: NSLocalizedString("mock_reply_update_3", comment: ""), | |
| nodeName: NSLocalizedString("mock_node_name_3", comment: ""), | |
| nodeId: "iphone", | |
| replyNum: "15"), | |
| Item(id: "mock4", | |
| title: NSLocalizedString("mock_title_4", comment: ""), | |
| avatar: "https://example.com/avatar4.png", | |
| userName: "feedback", | |
| replyUpdate: NSLocalizedString("mock_reply_update_4", comment: ""), | |
| nodeName: NSLocalizedString("mock_node_name_4", comment: ""), |
- Fixed hard-coded black color in FlowStack preview component
- Extract hard-coded CSS colors into constants in Webview.swift - Fix text color contrast issue in TabBar badge (use white on red background) - Remove mock data implementation that was auto-reverted 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Removed mockData() extension from FeedInfo.swift - Removed LoadMockData action and handler from FeedReducer.swift - Removed mock data loading logic and debug banner from FeedPage.swift - Mock data for ExploreInfo was already removed by linter 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Removed all debug/development screenshots from screenshots/dark/ directory - These were used for tracking dark mode fixes during development - Not needed for production release 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Changes Made
Color Fixes
Testing
Related Issues
Fixes dark mode adaptation issues reported by users