feat(theme): 校正 K26 预览色板与导航

This commit is contained in:
Zhou Weidong committed 2026-10-19 15:50:07 +08:00
1 parent eefabb6898
commit 87db548591
14 files changed
+293 -58

No files matched your search

+16 -7
View File
@@ -38,25 +38,34 @@ abstract final class AppColors {
static const ink = Color(0xFF0B0C10); static const ink = Color(0xFF0B0C10);
/// Bottom navigation background (rendered at 95% with blur). /// Bottom navigation background (rendered at 95% with blur).
static const bar = Color(0xFF0B0C10); static const bar = Color(0xFF0D0D11);
/// Cards lifted off [ink]. /// Cards lifted off [ink].
static const card = Color(0xFF16181F); static const card = Color(0xFF1B1C21);
/// Alternate card surface from the mobile preview.
static const cardAlt = Color(0xFF1C1D21);
/// Floating surfaces, inputs, and nested rows. /// Floating surfaces, inputs, and nested rows.
static const raised = Color(0xFF1D1E28); static const raised = Color(0xFF1C1D21);
static const quick = Color(0xFF202126);
static const input = Color(0xFF25252A);
static const goldCard = Color(0xFF201B1E);
/// Body text on dark surfaces. /// Body text on dark surfaces.
static const cream = Color(0xFFE3E2E8); static const cream = Color(0xFFE6E1E3);
/// Titles and high-emphasis text. /// Titles and high-emphasis text.
static const onSurface = Color(0xFFFFFFFF); static const onSurface = Color(0xFFFFFFFF);
/// Secondary text. /// Secondary text.
static const muted = Color(0xFFA1A1AA); static const muted = Color(0xFFD2CDD0);
/// Weak hints and timestamps. /// Weak hints and timestamps.
static const subtle = Color(0xFF71717A); static const subtle = Color(0xFF9A96A3);
/// Soft orange used for continue labels and secondary times.
static const accent = Color(0xFFFFA490);
/// 8% white border used by cards and inputs. /// 8% white border used by cards and inputs.
static const border = Color(0x14FFFFFF); static const border = Color(0x14FFFFFF);
@@ -65,7 +74,7 @@ abstract final class AppColors {
static const glassBorder = Color(0x24FFFFFF); static const glassBorder = Color(0x24FFFFFF);
static const outlineDark = Color(0xFF2A2C36); static const outlineDark = Color(0xFF2A2C36);
static const outlineVariantDark = Color(0xFF242630); static const outlineVariantDark = Color(0xFF242329);
static const outlineLight = Color(0xFF8E8E93); static const outlineLight = Color(0xFF8E8E93);
static const outlineVariantLight = Color(0xFFE4E4E7); static const outlineVariantLight = Color(0xFFE4E4E7);
} }
+21 -11
View File
@@ -9,15 +9,17 @@ abstract final class AppDimens {
static const double spaceXl = 24; static const double spaceXl = 24;
static const double pageHorizontal = 16; static const double pageHorizontal = 16;
static const double cardRadius = 16; static const double cardRadius = 12;
static const double authCardRadius = 20; static const double authCardRadius = 20;
static const double cardPadding = 16; static const double cardPadding = 16;
static const double innerRadius = 12; static const double innerRadius = 10;
static const double innerPadding = 12; static const double innerPadding = 12;
static const double pillRadius = 20; static const double pillRadius = 20;
static const double glassSmallRadius = 16; static const double glassSmallRadius = 12;
static const double glassRadius = 24; static const double glassRadius = 14;
static const double glassCardRadius = 26; static const double glassCardRadius = 14;
static const double buttonRadius = 9;
static const double coverRadius = 10;
static const double caption = 12; static const double caption = 12;
static const double emptyStateSubtitle = 12.5; static const double emptyStateSubtitle = 12.5;
@@ -27,17 +29,25 @@ abstract final class AppDimens {
static const double sectionTitle = 16; static const double sectionTitle = 16;
static const double pageTitle = 20; static const double pageTitle = 20;
static const double detailHeadline = 24; static const double detailHeadline = 24;
static const double navLabel = 11; static const double detailHeadlineSmall = 18;
static const double previewCardTitle = 13;
static const double previewSectionTitle = 14;
static const double chipFont = 10;
static const double navLabel = 9;
static const double buttonHeight = 48; static const double buttonHeight = 48;
static const double primaryButtonHeight = 52; static const double primaryButtonHeight = 52;
static const double buttonCompact = 32;
static const double buttonSmall = 34;
static const double buttonMedium = 40;
static const double buttonLarge = 44;
static const double tapTarget = 44; static const double tapTarget = 44;
static const double navHeight = 64; static const double navHeight = 54;
static const double navIcon = 22; static const double navIcon = 17;
static const double progressHeight = 4; static const double progressHeight = 3;
static const double progressRadius = 4; static const double progressRadius = 2;
static const double progressLineHeight = 1.5; static const double progressLineHeight = 3;
static const double searchHeight = 44; static const double searchHeight = 44;
static const double navDot = 3; static const double navDot = 3;
+4 -16
View File
@@ -16,7 +16,6 @@ class AppBottomNavItem {
required this.label, required this.label,
required this.onTap, required this.onTap,
this.selected = false, this.selected = false,
this.indicatorKey,
}); });
final Key tabKey; final Key tabKey;
@@ -25,7 +24,6 @@ class AppBottomNavItem {
final String label; final String label;
final VoidCallback onTap; final VoidCallback onTap;
final bool selected; final bool selected;
final Key? indicatorKey;
} }
/// Shared 3-tab navigation used by all root tabs and the player fallback. /// Shared 3-tab navigation used by all root tabs and the player fallback.
@@ -52,7 +50,9 @@ class AppBottomNav extends StatelessWidget {
height: AppDimens.navHeight, height: AppDimens.navHeight,
decoration: BoxDecoration( decoration: BoxDecoration(
color: backgroundColor.withValues(alpha: 0.95), color: backgroundColor.withValues(alpha: 0.95),
border: const Border(top: BorderSide(color: AppColors.border)), border: const Border(
top: BorderSide(color: AppColors.outlineVariantDark),
),
), ),
child: Row( child: Row(
children: [for (final item in items) _NavDestination(item: item)], children: [for (final item in items) _NavDestination(item: item)],
@@ -94,7 +94,7 @@ class _NavDestination extends StatelessWidget {
: Colors.white.withValues(alpha: 0.45), : Colors.white.withValues(alpha: 0.45),
), ),
), ),
const SizedBox(height: 3), const SizedBox(height: 2),
Text( Text(
item.label, item.label,
maxLines: 1, maxLines: 1,
@@ -108,18 +108,6 @@ class _NavDestination extends StatelessWidget {
letterSpacing: 0, letterSpacing: 0,
), ),
), ),
const SizedBox(height: 3),
AnimatedContainer(
key: item.indicatorKey,
duration: const Duration(milliseconds: 160),
curve: Curves.easeOut,
width: AppDimens.navDot,
height: AppDimens.navDot,
decoration: BoxDecoration(
color: item.selected ? AppColors.blue : Colors.transparent,
shape: BoxShape.circle,
),
),
], ],
), ),
), ),
+1 -1
View File
@@ -13,7 +13,7 @@ class GlassPanel extends StatelessWidget {
super.key, super.key,
required this.child, required this.child,
this.radius = AppDimens.glassRadius, this.radius = AppDimens.glassRadius,
this.blur = AppDimens.glassBlur, this.blur = 10,
this.padding, this.padding,
this.borderColor = AppColors.glassBorder, this.borderColor = AppColors.glassBorder,
this.gradient, this.gradient,
+22 -1
View File
@@ -15,6 +15,14 @@
"discoverTab": "Discover", "discoverTab": "Discover",
"searchDiscover": "Search dramas, actors, genres...", "searchDiscover": "Search dramas, actors, genres...",
"featured": "Featured", "featured": "Featured",
"recommended": "Recommended",
"fruitShortsBrand": "FruitShorts",
"authLandingSubtitle": "Sign in to sync progress, favorites and coins",
"comingSoon": "Coming soon",
"authLegal": "By continuing, you agree to the Terms of Service and Privacy Policy.",
"authContinueGoogle": "Continue with Google",
"authPhoneSignIn": "Phone sign in",
"authEmailSignIn": "Email sign in",
"suspense": "Suspense", "suspense": "Suspense",
"romance": "Romance", "romance": "Romance",
"actionGenre": "Action", "actionGenre": "Action",
@@ -31,6 +39,19 @@
"startWatching": "Start Watching", "startWatching": "Start Watching",
"showLess": "Less", "showLess": "Less",
"views": "views", "views": "views",
"episodeOfTotal": "Episode {current} / {total}",
"@episodeOfTotal": {
"placeholders": {
"current": {"type": "int"},
"total": {"type": "int"}
}
},
"minutesAgo": "{count} min ago",
"@minutesAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "Artwork", "artworkOnly": "Artwork",
"artworkOnlyNotice": "This is a visual catalog item. No video is available yet.", "artworkOnlyNotice": "This is a visual catalog item. No video is available yet.",
"videoPending": "Video pending", "videoPending": "Video pending",
@@ -52,7 +73,7 @@
}, },
"resume": "Resume", "resume": "Resume",
"history": "History", "history": "History",
"watchlist": "Watchlist", "watchlist": "Favorites",
"likedTab": "Liked", "likedTab": "Liked",
"all": "All", "all": "All",
"inProgress": "In Progress", "inProgress": "In Progress",
+22 -1
View File
@@ -12,9 +12,17 @@
"openQuickMenu": "Buka menu cepat", "openQuickMenu": "Buka menu cepat",
"quickAccess": "Akses cepat", "quickAccess": "Akses cepat",
"currentSeries": "Serial saat ini", "currentSeries": "Serial saat ini",
"discoverTab": "Jelajahi", "discoverTab": "Temukan",
"searchDiscover": "Cari drama, aktor, genre...", "searchDiscover": "Cari drama, aktor, genre...",
"featured": "Unggulan", "featured": "Unggulan",
"recommended": "Rekomendasi",
"fruitShortsBrand": "FruitShorts",
"authLandingSubtitle": "Masuk untuk menyinkronkan progres, favorit, dan koin",
"comingSoon": "Segera hadir",
"authLegal": "Dengan melanjutkan, Anda menyetujui Ketentuan Layanan dan Kebijakan Privasi.",
"authContinueGoogle": "Lanjutkan dengan Google",
"authPhoneSignIn": "Masuk dengan ponsel",
"authEmailSignIn": "Masuk dengan email",
"suspense": "Thriller", "suspense": "Thriller",
"romance": "Romantis", "romance": "Romantis",
"actionGenre": "Aksi", "actionGenre": "Aksi",
@@ -31,6 +39,19 @@
"startWatching": "Mulai Menonton", "startWatching": "Mulai Menonton",
"showLess": "Lebih sedikit", "showLess": "Lebih sedikit",
"views": "tayangan", "views": "tayangan",
"episodeOfTotal": "Episode {current} / {total}",
"@episodeOfTotal": {
"placeholders": {
"current": {"type": "int"},
"total": {"type": "int"}
}
},
"minutesAgo": "{count} mnt lalu",
"@minutesAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "Gambar", "artworkOnly": "Gambar",
"artworkOnlyNotice": "Ini hanya gambar katalog. Videonya belum tersedia.", "artworkOnlyNotice": "Ini hanya gambar katalog. Videonya belum tersedia.",
"videoPending": "Video belum siap", "videoPending": "Video belum siap",
+61 -1
View File
@@ -190,6 +190,54 @@ abstract class AppLocalizations {
/// **'Featured'** /// **'Featured'**
String get featured; String get featured;
/// No description provided for @recommended.
///
/// In en, this message translates to:
/// **'Recommended'**
String get recommended;
/// No description provided for @fruitShortsBrand.
///
/// In en, this message translates to:
/// **'FruitShorts'**
String get fruitShortsBrand;
/// No description provided for @authLandingSubtitle.
///
/// In en, this message translates to:
/// **'Sign in to sync progress, favorites and coins'**
String get authLandingSubtitle;
/// No description provided for @comingSoon.
///
/// In en, this message translates to:
/// **'Coming soon'**
String get comingSoon;
/// No description provided for @authLegal.
///
/// In en, this message translates to:
/// **'By continuing, you agree to the Terms of Service and Privacy Policy.'**
String get authLegal;
/// No description provided for @authContinueGoogle.
///
/// In en, this message translates to:
/// **'Continue with Google'**
String get authContinueGoogle;
/// No description provided for @authPhoneSignIn.
///
/// In en, this message translates to:
/// **'Phone sign in'**
String get authPhoneSignIn;
/// No description provided for @authEmailSignIn.
///
/// In en, this message translates to:
/// **'Email sign in'**
String get authEmailSignIn;
/// No description provided for @suspense. /// No description provided for @suspense.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -286,6 +334,18 @@ abstract class AppLocalizations {
/// **'views'** /// **'views'**
String get views; String get views;
/// No description provided for @episodeOfTotal.
///
/// In en, this message translates to:
/// **'Episode {current} / {total}'**
String episodeOfTotal(int current, int total);
/// No description provided for @minutesAgo.
///
/// In en, this message translates to:
/// **'{count} min ago'**
String minutesAgo(int count);
/// No description provided for @artworkOnly. /// No description provided for @artworkOnly.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -373,7 +433,7 @@ abstract class AppLocalizations {
/// No description provided for @watchlist. /// No description provided for @watchlist.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Watchlist'** /// **'Favorites'**
String get watchlist; String get watchlist;
/// No description provided for @likedTab. /// No description provided for @likedTab.
+37 -1
View File
@@ -54,6 +54,32 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get featured => 'Featured'; String get featured => 'Featured';
@override
String get recommended => 'Recommended';
@override
String get fruitShortsBrand => 'FruitShorts';
@override
String get authLandingSubtitle =>
'Sign in to sync progress, favorites and coins';
@override
String get comingSoon => 'Coming soon';
@override
String get authLegal =>
'By continuing, you agree to the Terms of Service and Privacy Policy.';
@override
String get authContinueGoogle => 'Continue with Google';
@override
String get authPhoneSignIn => 'Phone sign in';
@override
String get authEmailSignIn => 'Email sign in';
@override @override
String get suspense => 'Suspense'; String get suspense => 'Suspense';
@@ -102,6 +128,16 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get views => 'views'; String get views => 'views';
@override
String episodeOfTotal(int current, int total) {
return 'Episode $current / $total';
}
@override
String minutesAgo(int count) {
return '$count min ago';
}
@override @override
String get artworkOnly => 'Artwork'; String get artworkOnly => 'Artwork';
@@ -149,7 +185,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get history => 'History'; String get history => 'History';
@override @override
String get watchlist => 'Watchlist'; String get watchlist => 'Favorites';
@override @override
String get likedTab => 'Liked'; String get likedTab => 'Liked';
+37 -1
View File
@@ -46,7 +46,7 @@ class AppLocalizationsId extends AppLocalizations {
String get currentSeries => 'Serial saat ini'; String get currentSeries => 'Serial saat ini';
@override @override
String get discoverTab => 'Jelajahi'; String get discoverTab => 'Temukan';
@override @override
String get searchDiscover => 'Cari drama, aktor, genre...'; String get searchDiscover => 'Cari drama, aktor, genre...';
@@ -54,6 +54,32 @@ class AppLocalizationsId extends AppLocalizations {
@override @override
String get featured => 'Unggulan'; String get featured => 'Unggulan';
@override
String get recommended => 'Rekomendasi';
@override
String get fruitShortsBrand => 'FruitShorts';
@override
String get authLandingSubtitle =>
'Masuk untuk menyinkronkan progres, favorit, dan koin';
@override
String get comingSoon => 'Segera hadir';
@override
String get authLegal =>
'Dengan melanjutkan, Anda menyetujui Ketentuan Layanan dan Kebijakan Privasi.';
@override
String get authContinueGoogle => 'Lanjutkan dengan Google';
@override
String get authPhoneSignIn => 'Masuk dengan ponsel';
@override
String get authEmailSignIn => 'Masuk dengan email';
@override @override
String get suspense => 'Thriller'; String get suspense => 'Thriller';
@@ -102,6 +128,16 @@ class AppLocalizationsId extends AppLocalizations {
@override @override
String get views => 'tayangan'; String get views => 'tayangan';
@override
String episodeOfTotal(int current, int total) {
return 'Episode $current / $total';
}
@override
String minutesAgo(int count) {
return '$count mnt lalu';
}
@override @override
String get artworkOnly => 'Gambar'; String get artworkOnly => 'Gambar';
+36 -2
View File
@@ -45,7 +45,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get currentSeries => '当前短剧'; String get currentSeries => '当前短剧';
@override @override
String get discoverTab => '找剧'; String get discoverTab => '发现';
@override @override
String get searchDiscover => '搜索短剧、演员、类型…'; String get searchDiscover => '搜索短剧、演员、类型…';
@@ -53,6 +53,30 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get featured => '精选'; String get featured => '精选';
@override
String get recommended => '推荐';
@override
String get fruitShortsBrand => '水果短剧';
@override
String get authLandingSubtitle => '登录后同步进度、收藏与金币权益';
@override
String get comingSoon => '即将开放';
@override
String get authLegal => '继续即代表同意《用户协议》与《隐私政策》';
@override
String get authContinueGoogle => '使用 Google 继续';
@override
String get authPhoneSignIn => '手机号登录';
@override
String get authEmailSignIn => '邮箱登录';
@override @override
String get suspense => '悬疑'; String get suspense => '悬疑';
@@ -101,6 +125,16 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get views => '次播放'; String get views => '次播放';
@override
String episodeOfTotal(int current, int total) {
return '第 $current 集 / 共 $total 集';
}
@override
String minutesAgo(int count) {
return '$count 分钟前';
}
@override @override
String get artworkOnly => '海报展示'; String get artworkOnly => '海报展示';
@@ -146,7 +180,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get history => '历史'; String get history => '历史';
@override @override
String get watchlist => '追剧'; String get watchlist => '收藏';
@override @override
String get likedTab => '喜欢'; String get likedTab => '喜欢';
+23 -2
View File
@@ -12,9 +12,17 @@
"openQuickMenu": "打开快捷菜单", "openQuickMenu": "打开快捷菜单",
"quickAccess": "快捷入口", "quickAccess": "快捷入口",
"currentSeries": "当前短剧", "currentSeries": "当前短剧",
"discoverTab": "找剧", "discoverTab": "发现",
"searchDiscover": "搜索短剧、演员、类型…", "searchDiscover": "搜索短剧、演员、类型…",
"featured": "精选", "featured": "精选",
"recommended": "推荐",
"fruitShortsBrand": "水果短剧",
"authLandingSubtitle": "登录后同步进度、收藏与金币权益",
"comingSoon": "即将开放",
"authLegal": "继续即代表同意《用户协议》与《隐私政策》",
"authContinueGoogle": "使用 Google 继续",
"authPhoneSignIn": "手机号登录",
"authEmailSignIn": "邮箱登录",
"suspense": "悬疑", "suspense": "悬疑",
"romance": "都市爱情", "romance": "都市爱情",
"actionGenre": "动作", "actionGenre": "动作",
@@ -31,6 +39,19 @@
"startWatching": "开始观看", "startWatching": "开始观看",
"showLess": "收起", "showLess": "收起",
"views": "次播放", "views": "次播放",
"episodeOfTotal": "第 {current} 集 / 共 {total} 集",
"@episodeOfTotal": {
"placeholders": {
"current": {"type": "int"},
"total": {"type": "int"}
}
},
"minutesAgo": "{count} 分钟前",
"@minutesAgo": {
"placeholders": {
"count": {"type": "int"}
}
},
"artworkOnly": "海报展示", "artworkOnly": "海报展示",
"artworkOnlyNotice": "这张海报仅用于目录展示,暂时没有可播放视频。", "artworkOnlyNotice": "这张海报仅用于目录展示,暂时没有可播放视频。",
"videoPending": "视频待提供", "videoPending": "视频待提供",
@@ -52,7 +73,7 @@
}, },
"resume": "继续播放", "resume": "继续播放",
"history": "历史", "history": "历史",
"watchlist": "追剧", "watchlist": "收藏",
"likedTab": "喜欢", "likedTab": "喜欢",
"all": "全部", "all": "全部",
"inProgress": "未看完", "inProgress": "未看完",
+4 -7
View File
@@ -245,11 +245,10 @@ class _HomeTabsState extends State<_HomeTabs> {
items: [ items: [
AppBottomNavItem( AppBottomNavItem(
tabKey: const Key('tab-home'), tabKey: const Key('tab-home'),
icon: Icons.home_outlined, icon: Icons.play_circle_outline_rounded,
selectedIcon: Icons.home_rounded, selectedIcon: Icons.play_circle_fill_rounded,
label: l10n.homeTab, label: l10n.homeTab,
selected: logic.tab == 0, selected: logic.tab == 0,
indicatorKey: const Key('tab-home-indicator'),
onTap: () => _selectTab(context, 0), onTap: () => _selectTab(context, 0),
), ),
AppBottomNavItem( AppBottomNavItem(
@@ -258,16 +257,14 @@ class _HomeTabsState extends State<_HomeTabs> {
selectedIcon: Icons.explore_rounded, selectedIcon: Icons.explore_rounded,
label: l10n.discoverTab, label: l10n.discoverTab,
selected: logic.tab == 1, selected: logic.tab == 1,
indicatorKey: const Key('tab-discover-indicator'),
onTap: () => _selectTab(context, 1), onTap: () => _selectTab(context, 1),
), ),
AppBottomNavItem( AppBottomNavItem(
tabKey: const Key('tab-my'), tabKey: const Key('tab-my'),
icon: Icons.person_outline_rounded, icon: Icons.video_library_outlined,
selectedIcon: Icons.person_rounded, selectedIcon: Icons.video_library_rounded,
label: l10n.myTab, label: l10n.myTab,
selected: logic.tab == 2, selected: logic.tab == 2,
indicatorKey: const Key('tab-my-indicator'),
onTap: () => _selectTab(context, 2, myProfile: true), onTap: () => _selectTab(context, 2, myProfile: true),
), ),
], ],
+5 -5
View File
@@ -6,18 +6,18 @@ import 'package:fruitshorts/core/theme/app_dimens.dart';
void main() { void main() {
test('should_expose_the_k20_spacing_and_type_scale', () { test('should_expose_the_k20_spacing_and_type_scale', () {
expect(AppDimens.pageHorizontal, 16); expect(AppDimens.pageHorizontal, 16);
expect(AppDimens.cardRadius, 16); expect(AppDimens.cardRadius, 12);
expect(AppDimens.innerRadius, 12); expect(AppDimens.innerRadius, 10);
expect(AppDimens.caption, 12); expect(AppDimens.caption, 12);
expect(AppDimens.body, 14); expect(AppDimens.body, 14);
expect(AppDimens.listTitle, 15); expect(AppDimens.listTitle, 15);
expect(AppDimens.sectionTitle, 16); expect(AppDimens.sectionTitle, 16);
expect(AppDimens.pageTitle, 20); expect(AppDimens.pageTitle, 20);
expect(AppDimens.navLabel, 11); expect(AppDimens.navLabel, 9);
expect(AppDimens.buttonHeight, 48); expect(AppDimens.buttonHeight, 48);
expect(AppDimens.primaryButtonHeight, 52); expect(AppDimens.primaryButtonHeight, 52);
expect(AppDimens.tapTarget, 44); expect(AppDimens.tapTarget, 44);
expect(AppDimens.navHeight, 64); expect(AppDimens.navHeight, 54);
expect(AppDimens.progressHeight, 4); expect(AppDimens.progressHeight, 3);
}); });
} }
+4 -2
View File
@@ -21,8 +21,10 @@ void main() {
expect(AppColors.secondary, const Color(0xFF9278EA)); expect(AppColors.secondary, const Color(0xFF9278EA));
expect(AppColors.gold, const Color(0xFFFBBF24)); expect(AppColors.gold, const Color(0xFFFBBF24));
expect(AppColors.ink, const Color(0xFF0B0C10)); expect(AppColors.ink, const Color(0xFF0B0C10));
expect(AppColors.card, const Color(0xFF16181F)); expect(AppColors.card, const Color(0xFF1B1C21));
expect(AppColors.raised, const Color(0xFF1D1E28)); expect(AppColors.cardAlt, const Color(0xFF1C1D21));
expect(AppColors.raised, const Color(0xFF1C1D21));
expect(AppColors.bar, const Color(0xFF0D0D11));
expect(AppColors.success, const Color(0xFF34D399)); expect(AppColors.success, const Color(0xFF34D399));
expect(AppColors.onPrimary, const Color(0xFFFFFFFF)); expect(AppColors.onPrimary, const Color(0xFFFFFFFF));
expect( expect(