Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,9 @@ env.g.dart
# Non-CI golden files and failures
**/test/**/goldens/**/*.*
**/test/**/failures/**/*.*
!**/test/**/goldens/ci/*.*
!**/test/**/goldens/ci/*.*

# Transient Dart file written, analyzed and deleted by
# theme_code_generator_compiles_test.dart - only left behind if that test
# crashes mid-run.
apps/design_system_gallery/test/core/generated/
Comment on lines +150 to +153

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As explained in the file, the theme_code_generator_compiles_test creates a file and runs an analyzer on it. After the test the file should be deleted, but if something goes wrong the file might stick around. We don't want to commit the file to git, so safest way is to ignore it.

49 changes: 41 additions & 8 deletions apps/design_system_gallery/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,30 @@ apps/design_system_gallery/
│ │ ├── spacing.dart # StreamSpacing showcase
│ │ └── colors.dart # StreamColors showcase
│ ├── config/
│ │ ├── theme_color_slot.dart # ThemeColorSlot/ThemeSeedSlot enums (source of truth for colors)
│ │ ├── theme_studio_sections.dart # Section/group layout shared by the panel, export page & codegen
│ │ ├── component_theme_descriptors.dart # Editable Color props per component theme + name lookup
│ │ ├── theme_configuration.dart # Theme state (colors, brightness, etc.)
│ │ ├── theme_export_configuration.dart # Export page state: a light + dark config, plus link state
│ │ └── preview_configuration.dart # Preview state (device, text scale)
│ ├── core/
│ │ └── preview_wrapper.dart # Wraps use cases with theme/device frame
│ │ ├── preview_wrapper.dart # Wraps use cases with theme/device frame
│ │ └── theme_code_generator.dart # Generates copy-pasteable Dart for the export page
│ └── widgets/
│ ├── toolbar/ # Top toolbar widgets
│ └── theme_studio/ # Theme customization panel widgets
│ ├── theme_studio/ # Theme customization panel widgets
│ └── theme_export/ # Export page widgets (linked color rows, message preview)
```

The gallery also has a `test/` directory (`melos run test:flutter` picks it up automatically). It currently covers:

- **`theme_color_slot.dart` vs. `StreamColorScheme`** — pins the slot list so an added SDK color fails loudly instead of being silently missed.
- **Component themes** — that every `ComponentThemeDescriptor` matches the real `StreamTheme` API.
- **`theme_export_configuration.dart`** — seeding from the studio, link/unlink semantics, and that export never writes back.
- **`color_picker_tile.dart`** — the default/customized states, and that a tile keeps a constant height either way.
- **The export page** — the light/dark columns, link toggles, the responsive side-by-side/tabs split, and the component theme picker.
- **The code generator** — const naming (shared vs. `Light`/`Dark`-suffixed), chrome derivation, and a check that the generated snippet actually **type-checks against the real API** (`theme_code_generator_compiles_test.dart` writes it to a real `.dart` file and runs `dart analyze` over it).

Comment thread
coderabbitai[bot] marked this conversation as resolved.
## Common Commands

```bash
Expand Down Expand Up @@ -369,19 +384,37 @@ Use `context.read<ThemeConfiguration>()` for calling methods (no rebuild on chan

```dart
// For calling setters/methods - use read
context.read<ThemeConfiguration>().setAccentPrimary(color);
context.read<ThemeConfiguration>().setOverride(ThemeColorSlot.accentPrimary, color);
context.read<ThemeConfiguration>().resetToDefaults();
```

Use `context.watch<ThemeConfiguration>()` only when you need to rebuild on changes (typically only in `gallery_app.dart`).

### Adding New Theme Properties

1. Add private field and getter in `theme_configuration.dart`
2. Add setter using `_update()` pattern
3. Include in `_rebuildTheme()` colorScheme.copyWith()
4. Add to `resetToDefaults()`
5. Add UI control in `theme_customization_panel.dart`
Every plain `Color?` parameter of `StreamColorScheme` is represented once, as a
`ThemeColorSlot` value — `ThemeConfiguration` stores overrides in a single
`Map<ThemeColorSlot, Color>` rather than one field/getter/setter/reset per
color, and the studio panel renders `themeStudioSections` instead of
hand-written tiles. This is what keeps the panel, the export page, and the
code generator from drifting apart (they used to — `textOnInverse`,
`borderOnInverse` and `borderDisabledOnSurface` existed on `StreamColorScheme`
but were missing from the studio for a while).

To add a new SDK color:

1. Add a `ThemeColorSlot` value in `theme_color_slot.dart` (parameter name +
a `_readXxx(StreamColorScheme s) => s.xxx;` top-level reader — enum-constant
arguments must be constant expressions, so the reader is a function
tear-off, not an inline closure).
2. Add the same parameter to the `_rebuildTheme()` call in
`theme_configuration.dart` (`xxx: _overrides[ThemeColorSlot.xxx]`).
3. Add the slot to a group in `theme_studio_sections.dart` — this alone adds
its UI control to the panel, the export page, and code generation.
4. Update `theme_color_slot_test.dart`'s pinned parameter-name list.

`brand`/`chrome` (swatch-valued, see `ThemeSeedSlot`) and `avatarPalette`
(a list, not a color) are handled separately and don't go through this path.

### Best Practices

Expand Down
15 changes: 12 additions & 3 deletions apps/design_system_gallery/lib/app/gallery_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook;
import '../config/preview_configuration.dart';
import '../config/theme_configuration.dart';
import 'gallery_shell.dart';
import 'theme_export_page.dart';

/// Stream Design System Gallery
///
Expand All @@ -30,6 +31,10 @@ class _StreamDesignSystemGalleryState extends State<StreamDesignSystemGallery> {
super.dispose();
}

void _openThemeExport(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute<void>(builder: (_) => const ThemeExportPage()));
}

@override
Widget build(BuildContext context) {
return MultiProvider(
Expand All @@ -49,9 +54,13 @@ class _StreamDesignSystemGalleryState extends State<StreamDesignSystemGallery> {
theme: materialTheme,
darkTheme: materialTheme,
themeMode: isDark ? .dark : .light,
home: GalleryShell(
showThemePanel: _showThemePanel,
onToggleThemePanel: () => setState(() => _showThemePanel = !_showThemePanel),
home: Builder(
// Builder gives a context below the [MaterialApp]'s [Navigator].
builder: (context) => GalleryShell(
showThemePanel: _showThemePanel,
onToggleThemePanel: () => setState(() => _showThemePanel = !_showThemePanel),
onExportTheme: () => _openThemeExport(context),
),
),
);
},
Expand Down
3 changes: 3 additions & 0 deletions apps/design_system_gallery/lib/app/gallery_shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ class GalleryShell extends StatelessWidget {
super.key,
required this.showThemePanel,
required this.onToggleThemePanel,
required this.onExportTheme,
});

final bool showThemePanel;
final VoidCallback onToggleThemePanel;
final VoidCallback onExportTheme;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -55,6 +57,7 @@ class GalleryShell extends StatelessWidget {
GalleryToolbar(
showThemePanel: showThemePanel,
onToggleThemePanel: onToggleThemePanel,
onExportTheme: onExportTheme,
),
// Content area below toolbar
Expanded(
Expand Down
Loading
Loading