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(0xFF6366F1); // indigo-500

  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(0xFF6366F1))  // ❌

// 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 / TV):

SnackBar

Screen TypeStyle
MobileDefault floating style
DesktopFixed width 480px, centered
TVFixed width 600px, larger padding

FilledButton

Screen TypeMinimum Size
Non-TV (Mobile / Tablet / Desktop)88 × 44
TV120 × 56

The actual code (app_theme.dart) only splits into two tiers based on isTv; Mobile / Tablet / Desktop share 88×44.

Dialog Maximum Width

Screen TypeMaximum Width
Mobile300px
Tablet400px
Desktop480px
TV600px

TV-Specific Sizes

The TV platform uses dedicated constants defined in the TvTheme class:

PropertyValueDescription
Title font24spfontSizeTitle
Body font20spfontSizeBody
Caption font16spfontSizeCaption
Focus border4pxfocusBorderWidth
Focus scale1.05xfocusScale
Grid columns4gridColumns
Content padding48pxcontentPadding

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: indigo-500 (#6366F1)
    • Added responsive theme adaptation (Mobile / Tablet / Desktop / TV)
    • Added TV-specific theme constants (TvTheme)
    • Added cover color extraction feature