Skip to content

Commit a302c47

Browse files
committed
feat(bull_ui): design-system package with official-theme foundation tokens
1 parent d3b4e4d commit a302c47

41 files changed

Lines changed: 504 additions & 378 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/core/themes/app_theme.dart

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,46 @@ enum AppThemeType { light, dark }
1111
/// palette per brightness, so `AppColors` stays the single source of truth.
1212
BullTheme _bullThemeFrom(AppColors colors) {
1313
return BullTheme(
14-
red: colors.primary,
15-
onRed: colors.onPrimary,
16-
surface: colors.surface,
17-
card: colors.cardBackground,
18-
text: colors.text,
19-
muted: colors.textMuted,
20-
info: colors.info,
21-
success: colors.success,
22-
warning: colors.warning,
23-
btc: colors.onTertiary,
24-
outlineVariant: colors.outlineVariant,
25-
shimmerBase: colors.shimmerBase,
26-
shimmerHighlight: colors.shimmerHighlight,
14+
primary: colors.primary,
15+
onPrimary: colors.onPrimary,
16+
primaryFixed: colors.primaryFixed,
17+
onPrimaryFixed: colors.onPrimaryFixed,
2718
secondary: colors.secondary,
2819
onSecondary: colors.onSecondary,
20+
secondaryFixed: colors.secondaryFixed,
2921
secondaryFixedDim: colors.secondaryFixedDim,
30-
border: colors.border,
22+
onSecondaryFixed: colors.onSecondaryFixed,
23+
tertiary: colors.tertiary,
24+
onTertiary: colors.onTertiary,
25+
tertiaryContainer: colors.tertiaryContainer,
26+
background: colors.background,
27+
surface: colors.surface,
28+
surfaceContainer: colors.surfaceContainer,
29+
surfaceContainerHighest: colors.surfaceContainerHighest,
30+
surfaceBright: colors.surfaceBright,
3131
onSurface: colors.onSurface,
3232
onSurfaceVariant: colors.onSurfaceVariant,
33+
inverseSurface: colors.inverseSurface,
34+
cardBackground: colors.cardBackground,
35+
text: colors.text,
36+
textMuted: colors.textMuted,
37+
border: colors.border,
38+
outline: colors.outline,
39+
outlineVariant: colors.outlineVariant,
40+
error: colors.error,
41+
onError: colors.onError,
42+
errorContainer: colors.errorContainer,
43+
success: colors.success,
44+
warning: colors.warning,
45+
warningContainer: colors.warningContainer,
46+
info: colors.info,
3347
scrim: colors.scrim,
48+
overlay: colors.overlay,
49+
transparent: colors.transparent,
50+
surfaceFixed: colors.surfaceFixed,
51+
onSurfaceFixed: colors.onSurfaceFixed,
52+
shimmerBase: colors.shimmerBase,
53+
shimmerHighlight: colors.shimmerHighlight,
3454
);
3555
}
3656

packages/bull_ui/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## Unreleased
44

5+
- **Foundation tokens now follow the design system / official theme.** `BullTheme`
6+
is the full 1:1 `AppColors` mirror (all ~40 fields, official names); `BullRadius`
7+
adopts the design scale (`0/4/8/12/16/28/32/999`) and `BullSpacing` the design
8+
scale (`0/4/8/12/16/24/32/48/64`); `BullTextStyles` is **removed** — type comes
9+
from the Material `TextTheme` (`AppFonts`). All `Bull*` components were remapped
10+
(`context.bull.red→primary`, `btc→onTertiary`, `muted→textMuted`,
11+
`card→cardBackground`, radius/spacing → new scales). Tokens are fixed (not
12+
screen-scaled); responsiveness is a layout concern.
13+
- `BullSwipeAction` action panel wrapped in `FittedBox(scaleDown)` so the
14+
icon+label never overflows a short row.
15+
516
- Expand `BullTheme` with `secondary`, `onSecondary`, `secondaryFixedDim`,
617
`border`, `onSurface`, `onSurfaceVariant` and `scrim` (plus `copyWith`/`lerp`
718
and the app-side `_bullThemeFrom` builder), sourced from `AppColors`.

packages/bull_ui/README.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,25 @@ feature-by-feature in later scoped PRs as usages migrate. Nothing is moved or re
1414
`bull_ui` is **brightness-agnostic** and never hardcodes a colour. The app injects the
1515
palette: it builds a `BullTheme` (a `ThemeExtension`) from `AppColors.light` and another
1616
from `AppColors.dark`, and registers each on the matching `ThemeData` via `extensions:`.
17-
Components read colours through `context.bull` (e.g. `context.bull.red`); derived fills use
18-
`tokenColor.withValues(alpha: …)` so they adapt to both light and dark automatically.
17+
Components read colours through `context.bull` (e.g. `context.bull.primary`). `BullTheme` is a
18+
**1:1 mirror of the app's `AppColors`** — same field names — so the app wires every field through
19+
without translation. Derived fills use `tokenColor.withValues(alpha: …)` so they adapt to both
20+
light and dark automatically.
1921

20-
Brightness-invariant tokens (radii, spacing, type over Golos Text) are static consts:
21-
`BullRadius`, `BullSpacing`, `BullTextStyles`. Icons are wrapped via `BullIcon(BullIcons.…)`.
22+
## Foundation tokens
23+
24+
Brightness-invariant tokens are static consts, from the design system:
25+
26+
- **`BullRadius`**`zero·xs·sm·md·lg·xl·xxl·full` = `0·4·8·12·16·28·32·999`.
27+
- **`BullSpacing`**`zero·xxs·xs·sm·md·lg·xl·xxl·xxxl` = `0·4·8·12·16·24·32·48·64`.
28+
- **Type** comes from the Material `TextTheme` (`Theme.of(context).textTheme`, sourced from
29+
`AppFonts`) — there is no separate text-token class.
30+
- **Icons** via `BullIcon(BullIcons.…)`.
31+
32+
**Tokens are fixed — never scaled by screen size.** Responsiveness is a *layout* concern
33+
(breakpoints, `LayoutBuilder`, max-width wrappers, adaptive columns), not a token concern; text
34+
already honours the user's `MediaQuery.textScaler`. The catalogue (`packages/bull_ui_catalogue`)
35+
has a `Foundation/` section showcasing colours, radius, spacing and the text scale.
2236

2337
Fonts (Golos Text, Bebas Neue) are declared at the app root and resolve by family name
2438
across the workspace — `bull_ui` references them by name and does not re-ship the `.ttf`s.

packages/bull_ui/lib/src/buttons/bull_button.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class BullButton extends StatelessWidget {
9191

9292
@override
9393
Widget build(BuildContext context) {
94-
final radius = BorderRadius.circular(BullRadius.button);
94+
final radius = BorderRadius.circular(BullRadius.xs);
9595

9696
final image = iconData != null
9797
? Icon(iconData, size: 20, color: textColor)
@@ -102,7 +102,7 @@ class BullButton extends StatelessWidget {
102102
label,
103103
maxLines: 1,
104104
overflow: TextOverflow.ellipsis,
105-
style: textStyle ?? BullTextStyles.title,
105+
style: textStyle ?? Theme.of(context).textTheme.headlineMedium,
106106
color: textColor,
107107
),
108108
);

packages/bull_ui/lib/src/buttons/bull_tab_menu_vertical_button.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'package:bull_ui/src/data_display/bull_text.dart';
22
import 'package:bull_ui/src/theme/bull_theme.dart';
3-
import 'package:bull_ui/src/theme/bull_tokens.dart';
43
import 'package:flutter/material.dart';
54
import 'package:gap/gap.dart';
65

@@ -49,7 +48,7 @@ class BullTabMenuVerticalButton extends StatelessWidget {
4948
const Gap(8),
5049
BullText(
5150
title,
52-
style: BullTextStyles.title.copyWith(
51+
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
5352
fontWeight: FontWeight.w700,
5453
),
5554
),

packages/bull_ui/lib/src/buttons/bull_tool_button.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class BullToolButton extends StatelessWidget {
3535
@override
3636
Widget build(BuildContext context) {
3737
final colors = context.bull;
38-
final fg = primary ? colors.onRed : colors.text;
39-
final bg = primary ? colors.red : colors.muted.withValues(alpha: 0.12);
38+
final fg = primary ? colors.onPrimary : colors.text;
39+
final bg = primary ? colors.primary : colors.textMuted.withValues(alpha: 0.12);
4040

4141
return Semantics(
4242
button: true,
@@ -50,9 +50,11 @@ class BullToolButton extends StatelessWidget {
5050
onTap: onPressed,
5151
child: Container(
5252
height: 56,
53+
constraints: const BoxConstraints(minWidth: 72),
54+
padding: const EdgeInsets.symmetric(horizontal: BullSpacing.sm),
5355
decoration: BoxDecoration(
5456
color: bg,
55-
borderRadius: BorderRadius.circular(BullRadius.small),
57+
borderRadius: BorderRadius.circular(BullRadius.xs),
5658
),
5759
child: Column(
5860
mainAxisAlignment: MainAxisAlignment.center,
@@ -61,9 +63,9 @@ class BullToolButton extends StatelessWidget {
6163
const SizedBox(height: 4),
6264
Text(
6365
label,
64-
style: BullTextStyles.pill.copyWith(
66+
style: Theme.of(context).textTheme.labelLarge?.copyWith(
67+
fontWeight: FontWeight.w600,
6568
color: fg,
66-
fontSize: 11.5,
6769
),
6870
),
6971
],

packages/bull_ui/lib/src/buttons/bull_viewer_action_button.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'package:bull_ui/src/data_display/bull_text.dart';
22
import 'package:bull_ui/src/theme/bull_theme.dart';
3-
import 'package:bull_ui/src/theme/bull_tokens.dart';
43
import 'package:flutter/material.dart';
54
import 'package:gap/gap.dart';
65

@@ -39,11 +38,11 @@ class BullViewerActionButton extends StatelessWidget {
3938
child: Row(
4039
mainAxisAlignment: MainAxisAlignment.center,
4140
children: [
42-
Icon(icon, size: 14, color: colors.red),
41+
Icon(icon, size: 14, color: colors.primary),
4342
const Gap(4),
4443
BullText(
4544
label,
46-
style: BullTextStyles.body,
45+
style: Theme.of(context).textTheme.bodyMedium,
4746
color: colors.secondary,
4847
),
4948
],

packages/bull_ui/lib/src/chrome/bull_selection_action_bar.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,20 @@ class BullSelectionActionBar extends StatelessWidget {
3636
child: SafeArea(
3737
top: false,
3838
child: Padding(
39-
padding: const EdgeInsets.fromLTRB(12, 10, 12, 22),
39+
padding: const EdgeInsets.fromLTRB(12, 8, 12, 24),
4040
child: Column(
4141
mainAxisSize: MainAxisSize.min,
4242
crossAxisAlignment: CrossAxisAlignment.stretch,
4343
children: [
4444
Text(
4545
summary,
46-
style: BullTextStyles.bodyEmphasis.copyWith(color: colors.text),
46+
style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w600, color: colors.text),
4747
),
48-
const SizedBox(height: 10),
48+
const SizedBox(height: 8),
4949
Row(
5050
children: [
5151
for (var i = 0; i < actions.length; i++) ...[
52-
if (i > 0) const SizedBox(width: BullSpacing.s),
52+
if (i > 0) const SizedBox(width: BullSpacing.xs),
5353
Expanded(child: actions[i]),
5454
],
5555
],

packages/bull_ui/lib/src/chrome/bull_top_bar.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'package:bull_ui/src/theme/bull_icon.dart';
22
import 'package:bull_ui/src/theme/bull_theme.dart';
3-
import 'package:bull_ui/src/theme/bull_tokens.dart';
43
import 'package:flutter/material.dart';
54

65
/// Top bar — back button + centered title + an optional trailing action.
@@ -58,7 +57,7 @@ class BullTopBar extends StatelessWidget {
5857
alignment: Alignment.bottomCenter,
5958
child: Text(
6059
title,
61-
style: BullTextStyles.title.copyWith(color: colors.text),
60+
style: Theme.of(context).textTheme.headlineMedium?.copyWith(color: colors.text),
6261
),
6362
),
6463
),
@@ -81,7 +80,7 @@ class BullTopBar extends StatelessWidget {
8180
width: 8,
8281
height: 8,
8382
decoration: BoxDecoration(
84-
color: colors.red,
83+
color: colors.primary,
8584
shape: BoxShape.circle,
8685
),
8786
),

packages/bull_ui/lib/src/controls/bull_segmented.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class _BullSegmentedState extends State<BullSegmented> {
4444
final colors = context.bull;
4545
return Center(
4646
child: ClipRRect(
47-
borderRadius: BorderRadius.circular(BullRadius.button),
47+
borderRadius: BorderRadius.circular(BullRadius.xs),
4848
clipBehavior: Clip.antiAliasWithSaveLayer,
4949
child: CustomSlidingSegmentedControl<String>(
5050
initialValue: widget.initialValue ?? widget.items.first,
@@ -59,27 +59,27 @@ class _BullSegmentedState extends State<BullSegmented> {
5959
isStretch: true,
6060
customSegmentSettings: CustomSegmentSettings(),
6161
decoration: BoxDecoration(
62-
color: colors.muted.withValues(alpha: 0.1),
63-
borderRadius: BorderRadius.circular(BullRadius.button),
62+
color: colors.textMuted.withValues(alpha: 0.1),
63+
borderRadius: BorderRadius.circular(BullRadius.xs),
6464
),
6565
thumbDecoration: BoxDecoration(
6666
color: colors.surface,
67-
borderRadius: BorderRadius.circular(BullRadius.button),
67+
borderRadius: BorderRadius.circular(BullRadius.xs),
6868
),
6969
children: {
7070
for (final item in widget.items)
7171
item: Text(
7272
item,
7373
style: item == selectedSegment
74-
? BullTextStyles.label.copyWith(
74+
? Theme.of(context).textTheme.labelMedium?.copyWith(
7575
fontWeight: FontWeight.w500,
76-
color: colors.red,
76+
color: colors.primary,
7777
)
7878
: widget.disabledItems.contains(item)
79-
? BullTextStyles.label.copyWith(
80-
color: colors.muted.withValues(alpha: 0.5),
79+
? Theme.of(context).textTheme.labelMedium?.copyWith(
80+
color: colors.textMuted.withValues(alpha: 0.5),
8181
)
82-
: BullTextStyles.label.copyWith(color: colors.text),
82+
: Theme.of(context).textTheme.labelMedium?.copyWith(color: colors.text),
8383
),
8484
},
8585
),

0 commit comments

Comments
 (0)