/// inou Design System /// Source: https://inou.com/static/style.css /// Base: 16px, Sora font, line-height 1.5 /// /// Using LOCAL Sora font (fonts/Sora-*.ttf), not google_fonts package. import 'package:flutter/material.dart'; class InouTheme { InouTheme._(); // Font family - local asset static const String _fontFamily = 'Sora'; // =========================================== // COLORS (from :root CSS variables) // =========================================== static const Color bg = Color(0xFFF8F7F6); // --bg static const Color bgCard = Color(0xFFFFFFFF); // --bg-card static const Color border = Color(0xFFE5E2DE); // --border static const Color borderHover = Color(0xFFC4BFB8); // --border-hover static const Color text = Color(0xFF1C1917); // --text static const Color textMuted = Color(0xFF78716C); // --text-muted static const Color textSubtle = Color(0xFFA8A29E); // --text-subtle static const Color accent = Color(0xFFB45309); // --accent static const Color accentHover = Color(0xFF92400E); // --accent-hover static const Color accentLight = Color(0xFFFEF3C7); // --accent-light static const Color danger = Color(0xFFDC2626); // --danger static const Color dangerLight = Color(0xFFFEF2F2); // --danger-light static const Color success = Color(0xFF059669); // --success static const Color successLight = Color(0xFFECFDF5); // --success-light // Indicator colors (data sections) static const Color indicatorImaging = Color(0xFFB45309); static const Color indicatorLabs = Color(0xFF059669); static const Color indicatorUploads = Color(0xFF6366F1); static const Color indicatorVitals = Color(0xFFEC4899); static const Color indicatorMedications = Color(0xFF8B5CF6); static const Color indicatorRecords = Color(0xFF06B6D4); static const Color indicatorJournal = Color(0xFFF59E0B); static const Color indicatorPrivacy = Color(0xFF64748B); static const Color indicatorGenetics = Color(0xFF10B981); // Message border colors static const Color errorBorder = Color(0xFFFECACA); // #FECACA static const Color infoBorder = Color(0xFFFDE68A); // #FDE68A static const Color successBorder = Color(0xFFA7F3D0); // #A7F3D0 // =========================================== // SPACING // =========================================== static const double spaceXs = 4.0; static const double spaceSm = 8.0; static const double spaceMd = 12.0; static const double spaceLg = 16.0; static const double spaceXl = 24.0; static const double spaceXxl = 32.0; static const double spaceXxxl = 48.0; // =========================================== // BORDER RADIUS // =========================================== static const double radiusSm = 4.0; static const double radiusMd = 6.0; static const double radiusLg = 8.0; static const double radiusXl = 12.0; static BorderRadius get borderRadiusSm => BorderRadius.circular(radiusSm); static BorderRadius get borderRadiusMd => BorderRadius.circular(radiusMd); static BorderRadius get borderRadiusLg => BorderRadius.circular(radiusLg); // =========================================== // LAYOUT // =========================================== static const double maxWidth = 1200.0; static const double maxWidthNarrow = 800.0; static const double maxWidthForm = 360.0; // =========================================== // TYPOGRAPHY // CSS base: body { font-size: 16px; line-height: 1.5; font-weight: 400; } // All rem values calculated as: rem * 16 // =========================================== // h1: 2.25rem (36px), weight 300, line-height 1.2, letter-spacing -0.03em static TextStyle get h1 => const TextStyle( fontFamily: _fontFamily, fontSize: 36.0, // 2.25 * 16 fontWeight: FontWeight.w300, height: 1.2, letterSpacing: -0.03 * 36.0, // -0.03em color: text, ); // Page title (styleguide): 2.5rem (40px), weight 700 static TextStyle get pageTitle => const TextStyle( fontFamily: _fontFamily, fontSize: 40.0, // 2.5 * 16 fontWeight: FontWeight.w800, // ExtraBold letterSpacing: -0.5, // Tighter tracking to match CSS height: 1.2, // Line height to match CSS defaults color: text, ); // h2: 1.5rem (24px), weight 300, letter-spacing -0.02em static TextStyle get h2 => const TextStyle( fontFamily: _fontFamily, fontSize: 24.0, // 1.5 * 16 fontWeight: FontWeight.w300, letterSpacing: -0.02 * 24.0, // -0.02em color: text, ); // Section title (styleguide): 1.4rem (22.4px), weight 600 static TextStyle get sectionTitle => const TextStyle( fontFamily: _fontFamily, fontSize: 22.4, // 1.4 * 16 fontWeight: FontWeight.w600, color: text, ); // h3: 1.125rem (18px), weight 500 static TextStyle get h3 => const TextStyle( fontFamily: _fontFamily, fontSize: 18.0, // 1.125 * 16 fontWeight: FontWeight.w500, color: text, ); // Subsection title (styleguide): 1.1rem (17.6px), weight 600 static TextStyle get subsectionTitle => const TextStyle( fontFamily: _fontFamily, fontSize: 17.6, // 1.1 * 16 fontWeight: FontWeight.w600, color: text, ); // Intro text: 1.15rem (18.4px), weight 300 static TextStyle get intro => const TextStyle( fontFamily: _fontFamily, fontSize: 18.4, // 1.15 * 16 fontWeight: FontWeight.w300, height: 1.8, color: textMuted, ); // Body light (long-form): 1rem (16px), weight 300 static TextStyle get bodyLight => const TextStyle( fontFamily: _fontFamily, fontSize: 16.0, fontWeight: FontWeight.w300, height: 1.5, color: text, ); // Body regular (UI labels): 1rem (16px), weight 400 static TextStyle get body => const TextStyle( fontFamily: _fontFamily, fontSize: 16.0, fontWeight: FontWeight.w400, height: 1.5, color: text, ); // Small text: 0.85rem (13.6px), weight 400 static TextStyle get bodySmall => const TextStyle( fontFamily: _fontFamily, fontSize: 13.6, // 0.85 * 16 fontWeight: FontWeight.w400, color: text, ); // Label/Category: 0.75rem (12px), weight 600, uppercase, letter-spacing 0.1em static TextStyle get labelCaps => const TextStyle( fontFamily: _fontFamily, fontSize: 12.0, // 0.75 * 16 fontWeight: FontWeight.w600, letterSpacing: 0.1 * 12.0, // 0.1em color: textSubtle, ); // Button/label: 1rem (16px), weight 500 static TextStyle get label => const TextStyle( fontFamily: _fontFamily, fontSize: 16.0, fontWeight: FontWeight.w500, color: text, ); static TextStyle get labelLarge => label; // alias // Badge text: 1rem (16px), weight 500 static TextStyle get badge => const TextStyle( fontFamily: _fontFamily, fontSize: 16.0, fontWeight: FontWeight.w500, ); static TextStyle get badgeText => badge; // alias // Logo: 1.75rem (28px), weight 700, letter-spacing -0.02em static TextStyle get logo => const TextStyle( fontFamily: _fontFamily, fontSize: 28.0, // 1.75 * 16 fontWeight: FontWeight.w700, letterSpacing: -0.02 * 28.0, // -0.02em ); // Logo tagline: 0.95rem (15.2px), weight 300, letter-spacing 0.04em static TextStyle get logoTagline => const TextStyle( fontFamily: _fontFamily, fontSize: 15.2, // 0.95 * 16 fontWeight: FontWeight.w300, letterSpacing: 0.04 * 15.2, // 0.04em color: textMuted, ); // Mono: SF Mono, 0.85rem (13.6px) static TextStyle get mono => const TextStyle( fontFamily: 'SF Mono', fontFamilyFallback: ['Monaco', 'Consolas', 'monospace'], fontSize: 13.6, // 0.85 * 16 fontWeight: FontWeight.w400, color: text, ); // Profile card h3: 1.25rem (20px) static TextStyle get profileName => const TextStyle( fontFamily: _fontFamily, fontSize: 20.0, // 1.25 * 16 fontWeight: FontWeight.w600, color: text, ); // =========================================== // THEME DATA // =========================================== static ThemeData get light => ThemeData( useMaterial3: true, brightness: Brightness.light, fontFamily: _fontFamily, scaffoldBackgroundColor: bg, colorScheme: ColorScheme.light( primary: accent, onPrimary: Colors.white, secondary: accentLight, onSecondary: accent, surface: bgCard, onSurface: text, error: danger, onError: Colors.white, outline: border, ), textTheme: TextTheme( displayLarge: pageTitle, displayMedium: h1, headlineMedium: sectionTitle, headlineSmall: subsectionTitle, bodyLarge: body, bodyMedium: body, bodySmall: bodySmall, labelLarge: label, labelSmall: labelCaps, ), appBarTheme: AppBarTheme( backgroundColor: bg, foregroundColor: text, elevation: 0, centerTitle: false, ), cardTheme: CardTheme( color: bgCard, elevation: 0, shape: RoundedRectangleBorder( borderRadius: borderRadiusLg, side: BorderSide(color: border), ), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: accent, foregroundColor: Colors.white, elevation: 0, padding: EdgeInsets.symmetric(horizontal: spaceLg, vertical: spaceMd), shape: RoundedRectangleBorder(borderRadius: borderRadiusMd), textStyle: label, ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: text, side: BorderSide(color: border), padding: EdgeInsets.symmetric(horizontal: spaceLg, vertical: spaceMd), shape: RoundedRectangleBorder(borderRadius: borderRadiusMd), textStyle: label, ), ), inputDecorationTheme: InputDecorationTheme( filled: true, fillColor: bgCard, border: OutlineInputBorder( borderRadius: borderRadiusMd, borderSide: BorderSide(color: border), ), enabledBorder: OutlineInputBorder( borderRadius: borderRadiusMd, borderSide: BorderSide(color: border), ), focusedBorder: OutlineInputBorder( borderRadius: borderRadiusMd, borderSide: BorderSide(color: accent, width: 1), ), contentPadding: EdgeInsets.symmetric(horizontal: spaceMd, vertical: spaceMd), ), dividerTheme: DividerThemeData( color: border, thickness: 1, ), ); }