Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
121 changes: 99 additions & 22 deletions lib/core/app_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ class AppColors extends ThemeExtension<AppColors> {
final Color messageSent;
final Color messageReceived;
final Color systemMessage;

/// Dark-gold color used for the notification count badge.
final Color badgeGold;

/// Amber used for time-sensitive warnings (running timers, backup nags).
final Color warningAmber;

Expand Down Expand Up @@ -146,9 +148,16 @@ class AppColors extends ThemeExtension<AppColors> {
// ── Order Book redesign palette ───────────────────────────────────────────────

/// Palette of the "Mostro UX Redesign" mock (Claude Design, screen
/// #3 · Order book). Applied only to the redesigned Order Book screen while
/// the rest of the app migrates screen by screen; dark values follow the mock
/// except where the mock fails WCAG AA (4.5:1) on its real rendered surface —
/// #3 · Order book), on the v1 tonal recipe: app bar, tabs and card share one
/// surface tone ([bg] == [bgCard]) while the list area behind the cards sits
/// on the slightly lighter [bgWell] — v1's inverted contrast: dark cards on a
/// lighter well, deepened by [cardShadow] and the [border] hairline, with
/// inner panels one more step up on [bgElevated]. The green glow of the
/// "Card Contrast Options" mock (option 07) stays, reserved for the one card
/// per screen that is selected / needs action.
/// Applied only to the redesigned Order Book screen while
/// the rest of the app migrates screen by screen; values follow the mock
/// except where it fails WCAG AA (4.5:1) on its real rendered surface —
/// [textTertiary] and [red] are lightened just enough to pass. The mock is
/// dark-only, so [light] is a legibility mapping onto the existing light
/// surfaces, darkened where needed to pass AA. Every text-role/surface pair is
Expand All @@ -157,6 +166,7 @@ class AppColors extends ThemeExtension<AppColors> {
class OrderBookPalette {
const OrderBookPalette({
required this.bg,
required this.bgWell,
required this.bgCard,
required this.bgElevated,
required this.border,
Expand All @@ -172,9 +182,17 @@ class OrderBookPalette {
required this.blueFill,
required this.amber,
required this.red,
required this.glowBorder,
required this.glowRing,
required this.cardShadow,
});

final Color bg;

/// Background of the list area behind the offer cards, one step lighter
/// than [bg] (v1's `dark1` well): the cards — which share [bg]'s tone —
/// read as darker panels floating on it.
final Color bgWell;
final Color bgCard;
final Color bgElevated;
final Color border;
Expand All @@ -195,15 +213,34 @@ class OrderBookPalette {
final Color amber;
final Color red;

/// Border of the one selected / action-required card per screen
/// (mock: `rgba(143,224,74,0.55)`). Pair with [glowRing]; every other
/// card keeps the plain [border] hairline — if all cards glow, none does.
final Color glowBorder;

/// Soft green halo behind the selected card
/// (mock: `0 0 0 1px rgba(143,224,74,0.25), 0 0 22px rgba(143,224,74,0.18)`).
final List<BoxShadow> glowRing;

/// Depth shadow every offer card carries. With [bgCard] equal to [bg]
/// (the v1 recipe) this shadow — not fill contrast — is what makes the
/// card read as a card: a strong drop below plus a subtle sheen along the
/// top edge (v1's `AppTheme.cardShadow`).
final List<BoxShadow> cardShadow;

static const dark = OrderBookPalette(
bg: Color(0xFF0F151C),
bgCard: Color(0xFF1A2029),
bgElevated: Color(0xFF222A35),
border: Color(0x0FFFFFFF), // rgba(255,255,255,0.06)
// v1's exact surface family (mobile `AppTheme`): chrome and card share
// `backgroundDark`, the list well is `dark1`, inner panels sit on
// `backgroundCard`.
bg: Color(0xFF171A23),
bgWell: Color(0xFF1D212C),
bgCard: Color(0xFF171A23),
bgElevated: Color(0xFF1E2230),
border: Color(0x0DFFFFFF), // rgba(255,255,255,0.05), v1's hairline
textPrimary: Color(0xFFF2F4F7),
textSecondary: Color(0xFFA8B0BC),
// Mock #6B7280 is 3.4:1 on the card — lightened to pass AA (4.8:1).
textTertiary: Color(0xFF848C9A),
// Mock #6B7280 is 3.5:1 on the card — lightened to pass AA (4.6:1).
textTertiary: Color(0xFF8B93A1),
tabInactive: Color(0xFF4A5060),
green: Color(0xFF8FE04A),
greenDim: Color(0xFF2A4015),
Expand All @@ -212,13 +249,40 @@ class OrderBookPalette {
blue: Color(0xFF7BB4F0),
blueFill: Color(0xFF1E2B42),
amber: Color(0xFFE89C3C),
// Mock #E5484D is 3.7:1 on its 13% pill fill — lightened to pass AA.
red: Color(0xFFF27D81),
// Mock #E5484D is 3.8:1 on its 13% pill fill — lightened to pass AA.
red: Color(0xFFF48489),
glowBorder: Color(0x8C8FE04A), // rgba(143,224,74,0.55)
glowRing: [
BoxShadow(
color: Color(0x408FE04A), // rgba(143,224,74,0.25)
spreadRadius: 1,
),
BoxShadow(
color: Color(0x2E8FE04A), // rgba(143,224,74,0.18)
blurRadius: 22,
),
],
cardShadow: [
BoxShadow(
color: Color(0xB3000000), // black 70%
blurRadius: 15,
offset: Offset(0, 5),
spreadRadius: -3,
),
BoxShadow(
color: Color(0x12FFFFFF), // white 7% — top-edge sheen
blurRadius: 1,
offset: Offset(0, -1),
),
],
);

static const light = OrderBookPalette(
// Same structural recipe as dark: white cards on a faintly darker list
// well, depth from [cardShadow]; inner panels one more step down.
bg: Color(0xFFFFFFFF),
bgCard: Color(0xFFF5F5F5),
bgWell: Color(0xFFF4F4F6),
bgCard: Color(0xFFFFFFFF),
bgElevated: Color(0xFFEEEEEE),
border: Color(0x14000000),
textPrimary: Color(0xFF1A1A1A),
Expand All @@ -233,6 +297,20 @@ class OrderBookPalette {
blueFill: Color(0x2635638F),
amber: Color(0xFF845010),
red: Color(0xFFAE3333),
// Same alphas as dark over the light theme's AA-safe green.
glowBorder: Color(0x8C426800),
glowRing: [
BoxShadow(color: Color(0x40426800), spreadRadius: 1),
BoxShadow(color: Color(0x2E426800), blurRadius: 22),
],
cardShadow: [
BoxShadow(
color: Color(0x1F000000), // black 12% — soft daylight drop
blurRadius: 15,
offset: Offset(0, 5),
spreadRadius: -3,
),
],
);

static OrderBookPalette of(BuildContext context) =>
Expand Down Expand Up @@ -325,16 +403,16 @@ const _light = AppColors(
// ── ThemeData factories ────────────────────────────────────────────────────────

ThemeData buildDarkTheme() => _buildTheme(
brightness: Brightness.dark,
colors: _dark,
scaffold: const Color(0xFF1B1E28),
);
brightness: Brightness.dark,
colors: _dark,
scaffold: const Color(0xFF1B1E28),
);

ThemeData buildLightTheme() => _buildTheme(
brightness: Brightness.light,
colors: _light,
scaffold: const Color(0xFFFFFFFF),
);
brightness: Brightness.light,
colors: _light,
scaffold: const Color(0xFFFFFFFF),
);

ThemeData _buildTheme({
required Brightness brightness,
Expand Down Expand Up @@ -467,6 +545,5 @@ class _NoTransitionBuilder extends PageTransitionsBuilder {
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
child;
) => child;
}
105 changes: 57 additions & 48 deletions lib/features/home/screens/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ class _HomeScreenState extends ConsumerState<HomeScreen>
final isDesktop = screenWidth >= AppBreakpoints.desktop;

// ── Order list: responsive column count ──────────────────────────────────
final columns = screenWidth >= AppBreakpoints.desktop
? 3
: screenWidth >= AppBreakpoints.tablet
final columns =
screenWidth >= AppBreakpoints.desktop
? 3
: screenWidth >= AppBreakpoints.tablet
? 2
: 1;

Expand Down Expand Up @@ -155,10 +156,7 @@ class _HomeScreenState extends ConsumerState<HomeScreen>
fontWeight: FontWeight.w700,
letterSpacing: 1,
),
tabs: [
Tab(text: l10n.tabBuyBtc),
Tab(text: l10n.tabSellBtc),
],
tabs: [Tab(text: l10n.tabBuyBtc), Tab(text: l10n.tabSellBtc)],
),
),

Expand All @@ -171,7 +169,10 @@ class _HomeScreenState extends ConsumerState<HomeScreen>
// 320px-wide screens without a RenderFlex overflow.
Flexible(
child: Material(
color: pal.bgCard,
// bgElevated, not bgCard: with the v1 recipe bgCard equals
// the page tone, which would make the pill invisible (v1's
// filter uses its lighter input tone for the same reason).
color: pal.bgElevated,
shape: StadiumBorder(side: BorderSide(color: pal.border)),
child: InkWell(
customBorder: const StadiumBorder(),
Expand Down Expand Up @@ -229,52 +230,63 @@ class _HomeScreenState extends ConsumerState<HomeScreen>
),

// Order list — shimmer while loading, error state, or live data.
// The well is one step lighter than the chrome (v1's `dark1`
// container): the cards share the chrome's tone, so this inverted
// contrast is what makes them read as panels.
Expanded(
child: ref.watch(orderBookProvider).when(
loading: () => const OrderListSkeleton(),
error: (e, _) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
l10n.errorLoadingOrders,
style: TextStyle(color: pal.textSecondary),
textAlign: TextAlign.center,
),
const SizedBox(height: AppSpacing.md),
TextButton(
onPressed: () => ref.invalidate(orderBookProvider),
child: Text(l10n.retry),
child: ColoredBox(
color: pal.bgWell,
child: ref
.watch(orderBookProvider)
.when(
loading: () => const OrderListSkeleton(),
error:
(e, _) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
l10n.errorLoadingOrders,
style: TextStyle(color: pal.textSecondary),
textAlign: TextAlign.center,
),
const SizedBox(height: AppSpacing.md),
TextButton(
onPressed:
() => ref.invalidate(orderBookProvider),
child: Text(l10n.retry),
),
],
),
),
],
),
data: (_) => orderContent(onOrderTap),
),
data: (_) => orderContent(onOrderTap),
),
),
),
],
);

// ── Scaffold layout ───────────────────────────────────────────────────────
// Desktop: persistent sidebar + main content in a Row (no overlay drawer).
// Mobile/tablet: Stack with optional overlay drawer.
final body = isDesktop
? Row(
children: [
const DrawerMenu(persistent: true),
const VerticalDivider(width: 1),
Expanded(child: mainContent),
],
)
: Stack(
children: [
mainContent,
if (_drawerOpen)
DrawerMenu(
onClose: () => setState(() => _drawerOpen = false),
),
],
);
final body =
isDesktop
? Row(
children: [
const DrawerMenu(persistent: true),
const VerticalDivider(width: 1),
Expanded(child: mainContent),
],
)
: Stack(
children: [
mainContent,
if (_drawerOpen)
DrawerMenu(
onClose: () => setState(() => _drawerOpen = false),
),
],
);

// The scaffold background is overridden at the theme level so shared
// chrome that reads scaffoldBackgroundColor (bottom nav) matches the
Expand All @@ -294,10 +306,7 @@ class _HomeScreenState extends ConsumerState<HomeScreen>
/// Custom app bar per the mock: hamburger left, notification bell right,
/// empty center, 52px tall over a 1px hairline.
class _MostroAppBar extends StatelessWidget {
const _MostroAppBar({
required this.palette,
required this.onMenuTap,
});
const _MostroAppBar({required this.palette, required this.onMenuTap});

final OrderBookPalette palette;

Expand Down
Loading
Loading