Skip to content

Songloft Color System Guide

This document explains the color system used in the Songloft project and the guidelines for using it.

📚 Table of Contents


Flutter Material 3 Color System

The Songloft Flutter frontend uses the Material 3 design system, generating a complete color scheme automatically via ColorScheme.fromSeed.

Core Configuration

dart
// songloft-player/lib/core/theme/app_theme.dart
class AppTheme {
  static const Color _seedColor = Color(0xFF415F91); // M3 Blue baseline

  static ThemeData lightTheme({ScreenType screenType = ScreenType.mobile}) {
    return _buildTheme(Brightness.light, screenType);
  }

  static ThemeData darkTheme({ScreenType screenType = ScreenType.mobile}) {
    return _buildTheme(Brightness.dark, screenType);
  }
}

Advantages

  1. Automatic palette generation: Generates complete light/dark color schemes automatically from a seed color
  2. Semantic roles: Semantic color roles such as primary, secondary, tertiary, error
  3. Guaranteed contrast: Material 3 automatically ensures text-to-background contrast meets accessibility standards
  4. Consistency: All components automatically use a unified color scheme

ColorScheme Color Roles

RolePurposeExample
primaryPrimary actions, emphasis elementsPlay button, selected navigation state
onPrimaryText/icons on top of primaryButton text
primaryContainerPrimary-tinted container backgroundSelected card background
secondarySecondary actionsAuxiliary buttons
tertiaryThird-level emphasisTags, badges
errorError statesDelete button, error messages
surfacePage/card backgroundScaffold background
onSurfaceText on top of surfacePrimary text
onSurfaceVariantSecondary textSubtitles, descriptive text
outlineBordersInput field borders, dividers
outlineVariantDe-emphasized bordersList dividers

Theme Configuration

Theme Modes

Songloft supports three theme modes:

  • Light mode: A bright interface style
  • Dark mode: An eye-friendly dark interface
  • Follow system: Automatically follows the operating system setting

Theme switching is implemented through the ThemeSelector component, with state managed by themeModeProvider.

Font Configuration

dart
ThemeData(
  fontFamilyFallback: const ['NotoSansSC', 'sans-serif'],
  // ...
)
  • Uses the system font by default
  • Chinese falls back to Noto Sans SC (bundled with the app)

Component Theme Customization

dart
ThemeData(
  useMaterial3: true,
  appBarTheme: const AppBarTheme(centerTitle: false, elevation: 0),
  cardTheme: CardThemeData(elevation: 0, shape: RoundedRectangleBorder(...)),
  inputDecorationTheme: InputDecorationTheme(border: OutlineInputBorder(...), filled: true),
  navigationBarTheme: const NavigationBarThemeData(height: 64, ...),
  // ...
)

Color Usage Guidelines

1. Obtain Colors Through Theme

dart
// Get the ColorScheme
final colorScheme = Theme.of(context).colorScheme;

// Primary color
Container(color: colorScheme.primary)
Text('Title', style: TextStyle(color: colorScheme.onSurface))

// Secondary text
Text('Description', style: TextStyle(color: colorScheme.onSurfaceVariant))

// Error state
Icon(Icons.error, color: colorScheme.error)

2. Use TextTheme

dart
final textTheme = Theme.of(context).textTheme;

Text('Large title', style: textTheme.headlineMedium)
Text('Body text', style: textTheme.bodyLarge)
Text('Caption', style: textTheme.bodySmall)

3. Use the Built-in Colors of Material Components

dart
// FilledButton automatically uses the primary color
FilledButton(onPressed: () {}, child: Text('Primary action'))

// OutlinedButton automatically uses the outline color
OutlinedButton(onPressed: () {}, child: Text('Secondary action'))

// TextButton automatically uses the primary color
TextButton(onPressed: () {}, child: Text('Text action'))

❌ Avoid

dart
// Do not hardcode color values
Container(color: Color(0xFF415F91))  // ❌

// Do not use Colors constants (they do not follow the theme)
Text('Text', style: TextStyle(color: Colors.grey))  // ❌

// Use Theme instead
Container(color: Theme.of(context).colorScheme.primary)  // ✅
Text('Text', style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant))  // ✅

Responsive Theme Adaptation

The theme dynamically adjusts component sizes based on screen type (Mobile / Tablet / Desktop):

SnackBar

Screen TypeStyle
MobileDefault floating style
DesktopFixed width 480px, centered

FilledButton / OutlinedButton / TextButton

Screen TypeMinimum Size
Desktop88 × 44
Mobile / TabletFlutter framework default (not customized)

In the actual code (app_theme.dart), all three of filledButtonTheme / outlinedButtonTheme / textButtonTheme are gated by isDesktop: the theme is only set for Desktop (Desktop → 88×44). For Mobile / Tablet they are null, falling back to the Flutter framework default sizes.

Dialog Maximum Width

Screen TypeMaximum Width
Mobile300px
Tablet400px
Desktop480px

Cover Color Extraction

Songloft uses the palette_generator library to extract dominant colors from song cover images, used for dynamic coloring of the player interface:

dart
// songloft-player/lib/core/utils/color_extraction.dart
// Extract dominant colors from the cover image, applied to scenarios such as the player background gradient

Changelog

  • 2026-04-14: Migrated to the Flutter Material 3 color system
    • Main frontend migrated to Flutter, using ColorScheme.fromSeed for automatic palette generation
    • seedColor: M3 Blue baseline (#415F91)
    • Added responsive theme adaptation (Mobile / Tablet / Desktop)
    • Added cover color extraction feature