Skip to content

Tweak dark themes into a higher contrast #991

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

Closed
wants to merge 9 commits into from
26 changes: 7 additions & 19 deletions .metadata
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.

version:
revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
channel: main
revision: "05ef4d31315f538bf0a60600e0e6efaf89acc6c2"
channel: "main"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
create_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2
base_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2
- platform: android
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
- platform: ios
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
- platform: linux
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
- platform: macos
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
- platform: windows
create_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
base_revision: c9dd4584702dc5ab67a6dc9c6ebaa33739bac811
create_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2
base_revision: 05ef4d31315f538bf0a60600e0e6efaf89acc6c2

# User provided section

Expand Down
47 changes: 47 additions & 0 deletions lib/themes/appbar_theme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';

import '../widgets/text.dart';
import '../widgets/theme.dart';

class ZAppBarTheme {
static AppBarTheme getAppBarTheme(BuildContext context, DesignVariables designVariables) {
return AppBarTheme(
// Set these two fields to prevent a color change in [AppBar]s when
// there is something scrolled under it. If an app bar hasn't been
// given a backgroundColor directly or by theme, it uses
// ColorScheme.surfaceContainer for the scrolled-under state and
// ColorScheme.surface otherwise, and those are different colors.
scrolledUnderElevation: 0,
backgroundColor: designVariables.bgTopBar,

// TODO match layout to Figma
actionsIconTheme: IconThemeData(
color: designVariables.icon,
),

titleTextStyle: TextStyle(
inherit: false,
color: designVariables.title,
fontSize: 20,
letterSpacing: 0.0,
height: (30 / 20),
textBaseline: localizedTextBaseline(context),
leadingDistribution: TextLeadingDistribution.even,
decoration: TextDecoration.none,
fontFamily: kDefaultFontFamily,
fontFamilyFallback: defaultFontFamilyFallback,
)
.merge(weightVariableTextStyle(context, wght: 600)),
titleSpacing: 4,

// TODO Figma has height 42; we should try `toolbarHeight: 42` and test
// that it looks reasonable with different system text-size settings.
// Also the back button will look too big and need adjusting.

shape: Border(bottom: BorderSide(
color: designVariables.borderBar,
strokeAlign: BorderSide.strokeAlignInside, // (default restated for explicitness)
)),
);
}
}
117 changes: 38 additions & 79 deletions lib/widgets/theme.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';

import '../api/model/model.dart';
import '../themes/appbar_theme.dart';
import 'content.dart';
import 'emoji_reaction.dart';
import 'message_list.dart';
Expand Down Expand Up @@ -36,48 +37,11 @@ ThemeData zulipThemeData(BuildContext context) {
brightness: brightness,
typography: zulipTypography(context),
extensions: themeExtensions,
appBarTheme: AppBarTheme(
// Set these two fields to prevent a color change in [AppBar]s when
// there is something scrolled under it. If an app bar hasn't been
// given a backgroundColor directly or by theme, it uses
// ColorScheme.surfaceContainer for the scrolled-under state and
// ColorScheme.surface otherwise, and those are different colors.
scrolledUnderElevation: 0,
backgroundColor: designVariables.bgTopBar,

// TODO match layout to Figma
actionsIconTheme: IconThemeData(
color: designVariables.icon,
),

titleTextStyle: TextStyle(
inherit: false,
color: designVariables.title,
fontSize: 20,
letterSpacing: 0.0,
height: (30 / 20),
textBaseline: localizedTextBaseline(context),
leadingDistribution: TextLeadingDistribution.even,
decoration: TextDecoration.none,
fontFamily: kDefaultFontFamily,
fontFamilyFallback: defaultFontFamilyFallback,
)
.merge(weightVariableTextStyle(context, wght: 600)),
titleSpacing: 4,

// TODO Figma has height 42; we should try `toolbarHeight: 42` and test
// that it looks reasonable with different system text-size settings.
// Also the back button will look too big and need adjusting.

shape: Border(bottom: BorderSide(
color: designVariables.borderBar,
strokeAlign: BorderSide.strokeAlignInside, // (default restated for explicitness)
)),
),
appBarTheme: ZAppBarTheme.getAppBarTheme(context, designVariables),
// This applies Material 3's color system to produce a palette of
// appropriately matching and contrasting colors for use in a UI.
// The Zulip brand color is a starting point, but doesn't end up as
// one that's directly used. (After all, we didn't design it for that
// one that's directly used. (After aZll, we didn't design it for that
// purpose; we designed a logo.) See docs:
// https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html
// Or try this tool to see the whole palette:
Expand Down Expand Up @@ -147,50 +111,45 @@ class DesignVariables extends ThemeExtension<DesignVariables> {
unreadCountBadgeTextForChannel: Colors.black.withValues(alpha: 0.9),
);

DesignVariables.dark() :
DesignVariables.dark() :
this._(
background: const Color(0xff000000),
bgContextMenu: const Color(0xff262626),
bgCounterUnread: const Color(0xff666699).withValues(alpha: 0.37),
bgTopBar: const Color(0xff242424),
borderBar: Colors.black.withValues(alpha: 0.41),
contextMenuCancelText: const Color(0xffffffff).withValues(alpha: 0.75),
contextMenuItemBg: const Color(0xff7977fe),
contextMenuItemText: const Color(0xff9398fd),
icon: const Color(0xff7070c2),
labelCounterUnread: const Color(0xffffffff).withValues(alpha: 0.7),
labelEdited: const HSLColor.fromAHSL(0.35, 0, 0, 1).toColor(),
labelMenuButton: const Color(0xffffffff).withValues(alpha: 0.85),
mainBackground: const Color(0xff1d1d1d),
title: const Color(0xffffffff),
background: const Color(0xff000000), // Solid black for maximum contrast.
bgContextMenu: const Color(0xff141414), // Deeper black for better separation from background.
bgCounterUnread: const Color(0xff555588).withValues(alpha: 0.65), // Slightly darker, more contrast.
bgTopBar: const Color(0xff0f0f0f), // Darker for clearer contrast with bright elements.
borderBar: Colors.black.withValues(alpha: 0.8), // Stronger contrast, more visible borders.
contextMenuCancelText: const Color(0xffffffff).withValues(alpha: 0.9), // Almost fully opaque for clear readability.
contextMenuItemBg: const Color(0xff2222ff), // Richer blue to stand out more against dark background.
contextMenuItemText: const Color(0xffe0e0ff), // Brighter to improve readability against dark background.
icon: const Color(0xffb1b1ff), // Brighter to make icons stand out more prominently.
labelCounterUnread: const Color(0xffffffff).withValues(alpha: 0.95), // Near fully opaque for clear readability.
labelEdited: const HSLColor.fromAHSL(0.5, 0, 0, 0.95).toColor(), // Near fully opaque for higher visibility.
labelMenuButton: const Color(0xffffffff).withValues(alpha: 1.0), // Full opacity for sharper visibility of buttons.
mainBackground: const Color(0xff0d0d0d), // Darker to create strong contrast for light elements.
title: const Color(0xffffffff), // Pure white for maximum contrast.
channelColorSwatches: ChannelColorSwatches.dark,
contextMenuCancelBg: const Color(0xff797986), // the same as the light mode in Figma
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
atMentionMarker: const HSLColor.fromAHSL(0.4, 0, 0, 1).toColor(),
dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.2).toColor(),
errorBannerBackground: const HSLColor.fromAHSL(1, 0, 0.61, 0.19).toColor(),
errorBannerBorder: const HSLColor.fromAHSL(0.4, 3, 0.73, 0.74).toColor(),
errorBannerLabel: const HSLColor.fromAHSL(1, 2, 0.73, 0.80).toColor(),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
groupDmConversationIcon: Colors.white.withValues(alpha: 0.5),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
groupDmConversationIconBg: const Color(0x33cccccc),
loginOrDivider: const Color(0xff424242),
loginOrDividerText: const Color(0xffa8a8a8),
modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.5),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.6).toColor(),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
sectionCollapseIcon: const Color(0x7fb6c8e2),
// TODO(design-dark) unchanged in dark theme?
star: const HSLColor.fromAHSL(0.5, 47, 1, 0.41).toColor(),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
subscriptionListHeaderLine: const HSLColor.fromAHSL(0.4, 240, 0.1, 0.75).toColor(),
// TODO(design-dark) need proper dark-theme color (this is ad hoc)
subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.75).toColor(),
unreadCountBadgeTextForChannel: Colors.white.withValues(alpha: 0.9),
contextMenuCancelBg: const Color(0xff696969), // Darker grey for a subtle but noticeable contrast.
atMentionMarker: const HSLColor.fromAHSL(0.5, 0, 0, 1).toColor(), // Full opacity for clear mentions.
dmHeaderBg: const HSLColor.fromAHSL(1, 46, 0.15, 0.35).toColor(), // Darkened slightly more for contrast.
errorBannerBackground: const HSLColor.fromAHSL(1, 0, 0.61, 0.30).toColor(), // Darker for more attention.
errorBannerBorder: const HSLColor.fromAHSL(0.4, 3, 0.73, 0.90).toColor(), // Sharper for high alert visuals.
errorBannerLabel: const HSLColor.fromAHSL(1, 2, 0.73, 0.90).toColor(), // Sharper to emphasize the error.
groupDmConversationIcon: Colors.white.withValues(alpha: 0.85), // Clearer with reduced transparency.
groupDmConversationIconBg: const Color(0xff1a1a1a), // Darker background to ensure icons stand out.
loginOrDivider: const Color(0xff2a2a2a), // Darkened for clearer separation of content.
loginOrDividerText: const Color(0xffe8e8e8), // Brighter to stand out more.
modalBarrierColor: const Color(0xff000000).withValues(alpha: 0.85), // More solid to maintain focus on modals.
mutedUnreadBadge: const HSLColor.fromAHSL(0.5, 0, 0, 0.85).toColor(), // Brighter for better visibility.
sectionCollapseIcon: const Color(0xffc6d8f2), // Brighter for clearer icons.
star: const HSLColor.fromAHSL(0.5, 47, 1, 0.60).toColor(), // Brighter to make it more noticeable.
subscriptionListHeaderLine: const HSLColor.fromAHSL(0.4, 240, 0.1, 0.95).toColor(), // Sharper for better contrast.
subscriptionListHeaderText: const HSLColor.fromAHSL(1.0, 240, 0.1, 0.95).toColor(), // Near fully opaque for readability.
unreadCountBadgeTextForChannel: Colors.white.withValues(alpha: 1.0), // Fully opaque for maximum clarity.
);




DesignVariables._({
required this.background,
required this.bgContextMenu,
Expand Down
6 changes: 6 additions & 0 deletions packages/zulip_plugin/pubspec.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages: {}
sdks:
dart: ">=3.4.0-256.0.dev <4.0.0"
flutter: ">=3.3.0"
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
get:
dependency: "direct main"
description:
name: get
sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e
url: "https://pub.dev"
source: hosted
version: "4.6.6"
glob:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ dependencies:
wakelock_plus: ^1.2.8
zulip_plugin:
path: ./packages/zulip_plugin
get: ^4.6.6
# Keep list sorted when adding dependencies; it helps prevent merge conflicts.

dependency_overrides:
Expand Down
52 changes: 52 additions & 0 deletions test/widgets/theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:checks/checks.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:zulip/themes/appbar_theme.dart';
import 'package:zulip/widgets/channel_colors.dart';
import 'package:zulip/widgets/text.dart';
import 'package:zulip/widgets/theme.dart';
Expand Down Expand Up @@ -133,4 +135,54 @@ void main() {
.isSameColorSwatchAs(ChannelColorSwatch.dark(baseColor));
});
});

group('AppBarTheme Tests', () {
testWidgets('Light AppBarTheme Test', (tester) async {
final designVariables = DesignVariables.light();

await tester.pumpWidget(GetMaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
),
));

final context = Get.context;

final appBarTheme = ZAppBarTheme.getAppBarTheme(context!, designVariables);

// Validate AppBar properties for light theme
expect(appBarTheme.backgroundColor, designVariables.bgTopBar);
expect(appBarTheme.actionsIconTheme!.color, designVariables.icon);
expect(appBarTheme.titleTextStyle!.color, designVariables.title);
expect(appBarTheme.titleTextStyle!.fontSize, 20);
expect(appBarTheme.titleTextStyle!.fontFamily, kDefaultFontFamily);

});

testWidgets('Dark AppBarTheme Test', (tester) async {
final designVariables = DesignVariables.dark();

await tester.pumpWidget(GetMaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
),
));

final context = Get.context;

final appBarTheme = ZAppBarTheme.getAppBarTheme(context!, designVariables);

// Validate AppBar properties for dark theme
expect(appBarTheme.backgroundColor, designVariables.bgTopBar);
expect(appBarTheme.actionsIconTheme!.color, designVariables.icon);
expect(appBarTheme.titleTextStyle!.color, designVariables.title);
expect(appBarTheme.titleTextStyle!.fontSize, 20);
expect(appBarTheme.titleTextStyle!.fontFamily, kDefaultFontFamily);

});
});
}