import "package:flutter/widgets.dart"; import "package:google_fonts/google_fonts.dart"; import "package:garage_ui/theme/garage_theme.dart"; // The apps type scale. shadcn exposed a big Typography object; the GarageUI widgets // only reach for the small set below. `small` is a size style, `medium`/`normal` // are weight styles, and `mono` swaps the font family while inheriting the // ambient size/weight unless a caller overrides them. class Typography { const Typography({ this.normal = const TextStyle(fontWeight: FontWeight.w400), this.medium = const TextStyle(fontWeight: FontWeight.w500), this.semiBold = const TextStyle(fontWeight: FontWeight.w600), // `small` is the shared control font used by buttons, fields and selects. // Prefer [Typography.forDensity] over setting this by hand - the size // and line height belong to the density, and the whole control geometry // chain hangs off them. this.small = const TextStyle( fontSize: 10, height: 1.1, fontWeight: FontWeight.w400, ), }); /// Builds the control type from the density, so `small` can never drift from /// the line box the control heights are derived from. factory Typography.forDensity(Density density) => Typography( small: TextStyle( fontSize: density.fontSize, height: density.lineHeight, fontWeight: FontWeight.w400, ), ); final TextStyle normal; final TextStyle medium; /// Emphasis above [medium] - section labels, a dialog's title. Was a /// FontWeight.w600 literal in menu.dart, properties.dart and toast.dart. final TextStyle semiBold; final TextStyle small; TextStyle sansStyle(TextStyle style) => GoogleFonts.geist(textStyle: style, fontWeight: style.fontWeight); TextStyle get mono => GoogleFonts.geistMono(); TextStyle monoStyle(TextStyle style) => GoogleFonts.geistMono(textStyle: style, fontWeight: style.fontWeight); } // shadcn hung these little text helpers off every widget (usually a Text). they // merge a style change over whatever DefaultTextStyle is in scope. sizes are // multiplied by the theme scaling so they track the rest of the ui. extension TextStyleExtension on Widget { // sizes come off the density's text ladder, NOT a literal times scaling - // see Density.textXs for why. scaling is left out on purpose: Density isn't // scaled, and these have to stay in step with the control font. Widget xSmall() => _StyledText( child: this, style: (t) => TextStyle(fontSize: t.density.textXs), ); Widget small() => _StyledText( child: this, style: (t) => TextStyle(fontSize: t.density.textSm), ); Widget large() => _StyledText( child: this, style: (t) => TextStyle(fontSize: t.density.textLg), ); Widget medium() => _StyledText( child: this, style: (t) => const TextStyle(fontWeight: FontWeight.w500), ); Widget semiBold() => _StyledText( child: this, style: (t) => const TextStyle(fontWeight: FontWeight.w600), ); Widget bold() => _StyledText( child: this, style: (t) => const TextStyle(fontWeight: FontWeight.w700), ); Widget muted() => _StyledText( child: this, style: (t) => TextStyle(color: t.colorScheme.mutedForeground), ); } typedef _StyleFromTheme = TextStyle Function(ThemeData theme); class _StyledText extends StatelessWidget { const _StyledText({required this.child, required this.style}); final Widget child; final _StyleFromTheme style; @override Widget build(BuildContext context) { final theme = GarageTheme.of(context); return DefaultTextStyle.merge(style: style(theme), child: child); } }