Skip to content

ui: Set letter spacing to 1% in buttons #635

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

Merged
merged 2 commits into from
Apr 30, 2024

Conversation

chrisbobbe
Copy link
Collaborator

Stacked atop #629. Screenshots coming soon.

Fixes: #548

@chrisbobbe chrisbobbe added the a-design Visual and UX design label Apr 25, 2024
@chrisbobbe chrisbobbe requested a review from gnprice April 25, 2024 22:22
@chrisbobbe chrisbobbe force-pushed the pr-letter-spacing-1% branch from 03d16b8 to 562bd17 Compare April 25, 2024 22:45
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Apr 25, 2024

Seen here with the "Mark all messages as read" button:

Before After
3A87FF95-A5FF-4A14-856B-E5B7F720B42B 688FD86A-6983-4B79-BBCE-C7A8CAE5E055

@chrisbobbe chrisbobbe force-pushed the pr-letter-spacing-1% branch from 562bd17 to 3f822eb Compare April 25, 2024 23:28
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Apr 25, 2024

(There were some tests failing, and I also caught a bug when resolving those; pushed fixes.)

@chrisbobbe chrisbobbe force-pushed the pr-letter-spacing-1% branch from 3f822eb to f7669ea Compare April 25, 2024 23:31
@chrisbobbe
Copy link
Collaborator Author

chrisbobbe commented Apr 25, 2024

Do reaction chips count as buttons? We can give 1% letter spacing to their label text (which shows either a number or the reacting users' names); that wouldn't be hard.

code
--- lib/widgets/emoji_reaction.dart
+++ lib/widgets/emoji_reaction.dart
@@ -1,5 +1,6 @@
 import 'package:flutter/foundation.dart';
+++ lib/widgets/emoji_reaction.dart
@@ -1,5 +1,6 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:zulip/widgets/theme.dart';
 
 import '../api/model/model.dart';
 import '../api/route/messages.dart';
@@ -153,6 +154,7 @@ class ReactionChip extends StatelessWidget {
                 //   which we learn about especially late).
                 final maxLabelWidth = (maxRowWidth - 6) * 0.75; // 6 is padding
 
+                final labelScaler = _labelTextScalerClamped(context);
                 return Row(
                   mainAxisSize: MainAxisSize.min,
                   crossAxisAlignment: CrossAxisAlignment.center,
@@ -168,9 +170,13 @@ class ReactionChip extends StatelessWidget {
                         constraints: BoxConstraints(maxWidth: maxLabelWidth),
                         child: Text(
                           textWidthBasis: TextWidthBasis.longestLine,
-                          textScaler: _labelTextScalerClamped(context),
+                          textScaler: labelScaler,
                           style: TextStyle(
                             fontSize: (14 * 0.90),
+                            letterSpacing: proportionalLetterSpacing(context,
+                              kButtonTextLetterSpacingProportion,
+                              baseFontSize: (14 * 0.90),
+                              textScaler: labelScaler),
                             height: 13 / (14 * 0.90),
                             color: labelColor,
                           ).merge(weightVariableTextStyle(context,

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

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

Thanks @chrisbobbe! Comments below.

Comment on lines 152 to 121
theme: theme,
theme: outerThemeData(context),
Copy link
Member

Choose a reason for hiding this comment

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

The commit message says:

theme [nfc]: Pull `outerThemeData` out from app.dart to new theme.dart file

but this makes another change too: it computes this ThemeData from within the builder callback for GlobalStoreWidget.child. Is that intended?

If the method computing this theme data isn't going to be trying to use the store, then it's probably cleanest to keep the computation outside there, with e.g. final theme = outerThemeData(context); at the top of the build method. That way it's clear we compute it once eagerly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed; thanks for noticing this!

Comment on lines 55 to 57
elevatedButtonTheme: ElevatedButtonThemeData( style: _commonButtonStyle(context)),
filledButtonTheme: FilledButtonThemeData( style: _commonButtonStyle(context)),
iconButtonTheme: IconButtonThemeData( style: _commonButtonStyle(context)),
Copy link
Member

Choose a reason for hiding this comment

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

Seems cleanest to factor this common style out as a local — that way we only compute it once.

);
}

double kButtonTextLetterSpacingProportion = 0.01;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
double kButtonTextLetterSpacingProportion = 0.01;
const kButtonTextLetterSpacingProportion = 0.01;


import 'text.dart';

ThemeData outerThemeData(BuildContext context) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there a coherent description we can give for what the meaning of this "outer ThemeData" is?

It seems like it's basically an intermediate step in the calculation of the "inner ThemeData", which is the theme data we actually want to use in the app.

In that case, can we have one function that just computes the desired ThemeData start to finish, from a single BuildContext? I think that'd make it easier to reason about how the caller should use the function. That function could even have this intermediate step as a ThemeData local variable, if that's helpful.

Comment on lines 58 to 54
child: MessageListPage(
narrow: DmNarrow(
allRecipientIds: [eg.selfUser.userId, eg.otherUser.userId],
selfUserId: eg.selfUser.userId,
))))));
await tester.pumpWidget(const ZulipApp());
await tester.pump();
final navigator = await ZulipApp.navigator;
navigator.push(MessageListPage.buildRoute(accountId: eg.selfAccount.id,
narrow: DmNarrow(
Copy link
Member

Choose a reason for hiding this comment

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

These rearrangements of existing tests are probably clearest as a separate prep commit.

Comment on lines 57 to 65
)));

// global store, per-account store, and message list get loaded
await tester.pumpAndSettle();
Copy link
Member

Choose a reason for hiding this comment

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

This comment is no longer accurate. Perhaps just cut it:

Suggested change
)));
// global store, per-account store, and message list get loaded
await tester.pumpAndSettle();
)));
await tester.pumpAndSettle();

And perhaps it can be reduced to just a pump?

Comment on lines 489 to 490
// Restate [FilledButton]'s default…
themeData.textTheme.labelLarge!.merge(filledButtonThemeTextStyle)
Copy link
Member

Choose a reason for hiding this comment

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

Huh.

Does this mean we could just set .textTheme.labelLarge on our main ThemeData, and skip reaching into all the individual button styles? That seems like it'd be cleaner.

In principle that has a slightly broader effect than specified in #548, in that it affects any other Material widgets that use the labelLarge text style. But fundamentally those are widgets that the Material designers intended to have a similar text style to that of buttons (even though they're not called "buttons"), so it's likely that that'll be the right answer anyway.

If we end up with such a widget where Vlad wants a different spacing, we can address that then… and I think it's fairly likely that in that case he'd want something that wasn't the Material widget in any event.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a good idea! Done.

@@ -109,37 +109,6 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {

@override
Widget build(BuildContext context) {
final theme = ThemeData(
Copy link
Member

Choose a reason for hiding this comment

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

Cool, good to get this pulled out of ZulipApp.

@chrisbobbe chrisbobbe force-pushed the pr-letter-spacing-1% branch from f7669ea to 71949f1 Compare April 27, 2024 00:55
@chrisbobbe
Copy link
Collaborator Author

Thanks for the review! Revision pushed, with the simpler approach that you outlined in #635 (comment) and that we talked about in the office.

Copy link
Member

@gnprice gnprice left a comment

Choose a reason for hiding this comment

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

Thanks! This all looks good; just small comments below.

(I'll review #629 separately, but it also looks good modulo small comments.)

Comment on lines 32 to 33
return Builder(builder: (context) =>
buttonBuilder(context, buttonText));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return Builder(builder: (context) =>
buttonBuilder(context, buttonText));
return buttonBuilder(context, buttonText);

This would have the same effect, right? It doesn't seem like this build context is effectively any different from its parent.

Comment on lines 23 to 24
double? expectedFontSize;
double? expectedLetterSpacing;
Copy link
Member

Choose a reason for hiding this comment

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

Can tighten up expectations here slightly:

Suggested change
double? expectedFontSize;
double? expectedLetterSpacing;
late final double expectedFontSize;
late final double expectedLetterSpacing;

That way it's clear (and enforced) that these only get set once, and that they always get set before they're read.

Comment on lines 45 to 46
buttonBuilder: (context, text) => ElevatedButton(onPressed: () {},
child: Text(text)));
Copy link
Member

Choose a reason for hiding this comment

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

It seems like none of these builders actually use the context. So they can be simplified to just widgets (by pulling up the buttonText constant too):

Suggested change
buttonBuilder: (context, text) => ElevatedButton(onPressed: () {},
child: Text(text)));
button: ElevatedButton(onPressed: () {}, child: const Text(buttonText)));

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah right, cool!

TextTheme.labelLarge is the default label style in all the various
Material buttons; see:

  [ElevatedButton.defaultStyleOf], [FilledButton.defaultStyleOf],
  [MenuItemButton.defaultStyleOf], [MenuItemButton.defaultStyleOf],
  [OutlinedButton.defaultStyleOf], `defaults` in [SegmentedButton.build],
  [TextButton.defaultStyleOf]

. Since our buttons are all Material buttons, changing this will
effectively "set letter spacing to 1% in buttons", which is zulip#548.

(...Actually, I guess there's one kind of button that's not a
Material button: reaction chips in the message list. So, adjust
those separately, also in this commit.)

Greg mentioned, when suggesting this approach,

> In principle that has a slightly broader effect than specified in
> zulip#548, in that it affects any other Material widgets that use the
> `labelLarge` text style. But fundamentally those are widgets that
> the Material designers intended to have a similar text style to
> that of buttons (even though they're not called "buttons"), so
> it's likely that that'll be the right answer anyway.

> If we end up with such a widget where Vlad wants a different
> spacing, we can address that then… and I think it's fairly likely
> that in that case he'd want something that wasn't the Material
> widget in any event.

Which makes sense and works for me.

Fixes: zulip#548
@chrisbobbe chrisbobbe force-pushed the pr-letter-spacing-1% branch from 71949f1 to d882f50 Compare April 30, 2024 01:09
@chrisbobbe
Copy link
Collaborator Author

Thanks! Revision pushed.

@gnprice gnprice merged commit d882f50 into zulip:main Apr 30, 2024
@gnprice
Copy link
Member

gnprice commented Apr 30, 2024

Thanks! LGTM; merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a-design Visual and UX design
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Set letter spacing to 1% in buttons
2 participants