Customization
Mihr UI customizes progressively. Everything flows through one immutable MihrThemeConfig — start with a brand seed, then reach for the button theme, per-instance styles, or the Material escape hatch only when you need them.
Brand & presetsEasiest
Pass a MihrThemeConfig via the config: parameter. Set a brand seed and all 100+ semantic tokens re-derive automatically — light and dark.
final config = MihrThemeConfig(
brand: AccentColors.violet, // any of the 17 accents
fontFamily: 'Roboto',
);
MaterialApp(
theme: MihrTheme.light(config: config),
darkTheme: MihrTheme.dark(config: config),
);The brand is a ColorScale. Use a built-in accent, or generate a full 12-shade scale from any hex with ColorScaleGenerator.fromHex() — it auto-corrects shades that fail WCAG AA contrast.
MihrThemeConfig(
brand: ColorScaleGenerator.fromHex('#E63946'),
)You can also override the neutral and semantic palettes — gray, error, warning, success. Gray ships with 7 variants in GrayVariants (grayBlue, grayCool, grayModern, grayNeutral, grayIron, grayTrue, grayWarm).
MihrThemeConfig(
brand: AccentColors.violet,
gray: GrayVariants.grayCool,
error: ColorScaleGenerator.fromHex('#DC2626'),
)Button theme
Reshape every button through MihrButtonThemeData on the config — global shape, shadow preset, and per-variant style overrides.
MihrThemeConfig(
buttonTheme: MihrButtonThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
shadows: MihrButtonShadows.flat,
primaryStyle: const ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.indigo),
),
),
)shape is any OutlinedBorder and defaults to a RoundedRectangleBorder at MihrRadius.borderMd. shadows accepts the standard, flat, and subtle presets. Per-variant overrides: primaryStyle, secondaryStyle, tertiaryStyle, linkStyle, destructiveStyle.Per-instance
Override a single widget without touching the theme. Every button accepts a style or a styleBuilder that receives the resolved style so you can tweak just what you need.
MihrPrimaryButton(
onPressed: _save,
style: const ButtonStyle(
backgroundColor: WidgetStatePropertyAll(Colors.teal),
),
child: const Text('Save'),
)Material escape hatchAdvanced
materialOverrides hands you the fully-built ThemeData so you can patch any Material sub-theme using Mihr's own tokens.
MihrThemeConfig(
brand: AccentColors.indigo,
materialOverrides: (base) => base.copyWith(
scaffoldBackgroundColor: base.bgColors.secondary,
appBarTheme: base.appBarTheme.copyWith(
centerTitle: true,
backgroundColor: base.bgColors.brandSolid,
foregroundColor: base.textColors.white,
),
cardTheme: base.cardTheme.copyWith(color: base.bgColors.tertiary),
),
)Accessing tokens
Read semantic tokens off the BuildContext (or a ThemeData). Pick the right category by what you're coloring:
context.textColors.primarycontext.bgColors.secondarycontext.borderColors.primarycontext.fgColors.brandPrimarycontext.utilityColors.blue.shade100context.alphaColors.black50context.componentColors.avatarBgWhen in doubt, prefer TextColors for text, BackgroundColors for surfaces, and ForegroundColors for icons — those three cover most component needs.