Mihr UI logoMihr UI

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.

main.dart
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.

Dart
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).

Dart
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.

Dart
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.

Dart
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.

Dart
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:

?
Text fill color?TextColorscontext.textColors.primary
?
Surface / container background?BackgroundColorscontext.bgColors.secondary
?
Border, divider, or outline?BorderColorscontext.borderColors.primary
?
Icon, indicator, or dot?ForegroundColorscontext.fgColors.brandPrimary
?
Badge, tag, or chart color?UtilityColorscontext.utilityColors.blue.shade100
?
Overlay or scrim?AlphaColorscontext.alphaColors.black50
?
Component-specific token?ComponentColorscontext.componentColors.avatarBg

When in doubt, prefer TextColors for text, BackgroundColors for surfaces, and ForegroundColors for icons — those three cover most component needs.