Skip to content

Instantly share code, notes, and snippets.

@fredgrott
Created January 23, 2026 16:54
Show Gist options
  • Select an option

  • Save fredgrott/e642387708e6957d1d8400beb4787275 to your computer and use it in GitHub Desktop.

Select an option

Save fredgrott/e642387708e6957d1d8400beb4787275 to your computer and use it in GitHub Desktop.
M3ECustomTypography
/// Material 3 Expressive typography tokens.
/// - `base` starts from **M3 (Typography.material2021)**.
/// - Optionally remaps fonts: brand (UI) for display/headline/title/label
/// and plain (reading) for body.
/// - Adds an emphasized set (weight/tracking tweaks) for expressive hierarchy.
@immutable
class M3ECustomTypography {
final TextTheme base;
final M3ECustomEmphasized emphasized;
const M3ECustomTypography({required this.base, required this.emphasized});
/// Build default M3E typography from M3.
///
/// [brandFontFamily] is typically your UI/brand face (e.g., Roboto Flex).
/// [plainFontFamily] is typically your reading face (e.g., Roboto Serif).
/// If you pass neither, you’ll get pure M3 defaults (no family swap),
/// but still keep the M3E emphasized set for optional use.
factory M3ECustomTypography.defaultFor(
Brightness brightness, {
String? brandFontFamily,
String? plainFontFamily,
TextTheme? baseOverride,
}) {
// 1) Start from Material 3 baseline type.
final m3 = Typography.material2021();
final TextTheme m3Base = baseOverride ?? (brightness == Brightness.dark ? m3.white : m3.black);
// 2) Optionally map brand/plain families to role groups (M3E guidance).
final TextTheme baseWithFamilies = _applyFamilies(m3Base, brand: brandFontFamily, plain: plainFontFamily);
// 3) Provide emphasized deltas (weights/tracking).
return M3ECustomTypography(base: baseWithFamilies, emphasized: M3ECustomEmphasized.forBrightness(brightness));
}
/// Lerp the full token set.
static M3ECustomTypography lerp(M3ECustomTypography a, M3ECustomTypography b, double t) => M3ECustomTypography(
base: TextTheme.lerp(a.base, b.base, t),
emphasized: M3ECustomEmphasized.lerp(a.emphasized, b.emphasized, t),
);
/// Apply brand/plain families: brand → display/headline/title/label,
/// plain → body. If a family is null, keep the original.
static TextTheme _applyFamilies(TextTheme t, {String? brand, String? plain}) {
TextStyle? _withFam(TextStyle? s, String? fam) => fam == null ? s : s?.copyWith(fontFamily: fam);
return t.copyWith(
// Brand / UI voice
displayLarge: _withFam(t.displayLarge, brand),
displayMedium: _withFam(t.displayMedium, brand),
displaySmall: _withFam(t.displaySmall, brand),
headlineLarge: _withFam(t.headlineLarge, brand),
headlineMedium: _withFam(t.headlineMedium, brand),
headlineSmall: _withFam(t.headlineSmall, brand),
titleLarge: _withFam(t.titleLarge, brand),
titleMedium: _withFam(t.titleMedium, brand),
titleSmall: _withFam(t.titleSmall, brand),
labelLarge: _withFam(t.labelLarge, brand),
labelMedium: _withFam(t.labelMedium, brand),
labelSmall: _withFam(t.labelSmall, brand),
// Reading voice
bodyLarge: _withFam(t.bodyLarge, plain),
bodyMedium: _withFam(t.bodyMedium, plain),
bodySmall: _withFam(t.bodySmall, plain),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment