Skip to content

feat(orderbook): pixel-exact Order Book screen from Claude Design mock #3 - #243

Merged
grunch merged 2 commits into
mainfrom
feat/order-book-claude-design
Jul 29, 2026
Merged

feat(orderbook): pixel-exact Order Book screen from Claude Design mock #3#243
grunch merged 2 commits into
mainfrom
feat/order-book-claude-design

Conversation

@grunch

@grunch grunch commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Ports screen #3 · "Order book with reasons to pick" from the Claude Design project Mostro UX Redesign EN to the Flutter home screen, matching the mock's colors, proportions, and type scale exactly. Scope is strictly this screen — no other screens or shared-widget styling were touched.

What changed

  • OrderBookPalette (lib/core/app_theme.dart): the mock's exact dark tokens — bg #0F151C, card #1A2029, elevated #222A35, green #8FE04A, greenDim #2A4015, gold #FFC940/#3A2D0A, blue #7BB4F0/#1E2B42, amber #E89C3C, red #E5484D, text #F2F4F7/#A8B0BC/#6B7280/#4A5060, hairline rgba(255,255,255,0.06). The mock is dark-only, so light mode gets a legibility mapping onto the existing light surfaces. AppColors and every other screen are untouched.
  • HomeScreen: 52px app bar (hamburger + notification bell, empty center per the mock, 1px hairline divider); BUY/SELL tabs with 13px/700/1px-letter-spacing labels, active in green with a full-width 2px underline, inactive in textDisabled over a 1px rule; rounded (999) FILTER pill (card bg, 1px border, icon + FILTER + · N offers) with the "Sort: best premium ▾" caption right-aligned; list padding 16/8/90 with 12px card gaps. The scaffold background is theme-overridden locally so the bottom nav blends with #0F151C seam-free.
  • OrderListItem rebuilt to the mock's card: radius 16, padding 14/12/14/14; reason pill (3×8, radius 8, 11px/600) + relative timestamp; 26px/700 amount with 14px/600 currency, 18px flag and 13%-alpha premium pill; "Market price" caption; elevated reputation strip (radius 10, 12×10 padding, gold star 16px, bold rating, · separators, bold counts with secondary unit words); payment-method line. Skeleton and empty state re-tinted.
  • l10n (en/es/fr/de/it): orderReputationStatsreputationTradesLabel + reputationDaysLabel (allows the mock's bold-number/secondary-word styling), new sortBestPremium.
  • Regenerated order_list_item goldens (dark + light).

Deliberate deviations from the mock (functional, minimal)

  • The mock's cards have no buy/sell pill; kept only a "yours" pill on own orders (the type pill was dropped as in the mock — tabs already scope the side).
  • Status bar / home indicator are OS chrome; FAB, bottom nav, and bell keep their existing shared implementations (out of scope — they already match the mock's structure).
  • The mock uses the Inter font; the app defines no custom font family, and adding one is a global change out of this PR's scope.

Test plan / Manual testing guide

  1. flutter run -d linux (or android/chrome) and open the home screen (Order Book).
  2. Colors: page background #0F151C; cards #1A2029; reputation strip #222A35; active tab and best-premium pill green #8FE04A; star #FFC940.
  3. Tabs: BUY BTC active shows a green 2px full-width underline and green label; SELL BTC label is dimmed (#4A5060); a 1px hairline runs under the whole row.
  4. Filter row: rounded pill reading FILTER · N offers, tap opens the existing filter dialog; right side shows the sort caption.
  5. Cards: reason pill (⚡/⭐/🆕) top-left with relative time top-right; large bold amount + currency + flag with premium pill right; "Market price" caption; ★ 4.9 · 47 trades · 312 days strip with bold numbers; payment methods below.
  6. States: loading shows re-tinted skeleton; error shows retry; empty shows inbox icon.
  7. Regression: drawer opens from the hamburger; bell navigates to notifications; tapping a card routes to take/my-order as before; tablet/desktop grids unchanged in behavior.
  8. flutter analyze → no issues; flutter test → 144/144 passing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VQUKX88xRBCPsHQTXBEiYa

Summary by CodeRabbit

  • New Features
    • Rolled out the refreshed “Mostro” order book UI with pixel-precise layout updates, including header/nav controls and redesigned order cards.
    • Improved the order list rendering (pills, reputation strip, formatting) and updated skeleton/empty/loading visuals.
    • Added a new sort option: newest first (replacing “best premium” sorting).
    • Improved theme support via a dedicated order-book color palette.
  • Localization
    • Updated English, German, Spanish, French, and Italian copy to split reputation into separate Trades and Days labels, and added the Sort: newest first caption.
  • Tests
    • Added accessibility contrast checks to enforce WCAG contrast for the order-book palette.

…#3

Port the 'Mostro UX Redesign' mock (screen #3 · Order book with reasons
to pick) to the Flutter home screen, matching colors, spacing, and type
scale exactly:

- Add OrderBookPalette to app_theme.dart with the mock's exact dark
  tokens (bg #0F151C, card #1A2029, elevated #222A35, green #8FE04A,
  gold #FFC940, etc.) plus a legibility mapping for light mode (the
  mock is dark-only). Scoped to this screen; AppColors untouched.
- Restyle HomeScreen: 52px app bar (hamburger + bell, empty center,
  1px hairline), BUY/SELL tabs with green 13px/700 active label and
  2px full-width underline, rounded FILTER pill with offer count and
  'Sort: best premium' caption, 16/8/90 list padding with 12px gaps.
  Scaffold background themed so the bottom nav matches seamlessly.
- Rebuild OrderListItem card per the mock: reason pill + timestamp row,
  26px/700 amount with currency/flag and 13%-alpha premium pill,
  'Market price' caption, elevated reputation strip (gold star, bold
  numbers, dot separators), payment-method line. The mock carries no
  buy/sell pill (tabs scope the side); only a 'yours' pill is kept on
  own orders. Skeleton and empty state re-tinted to the palette.
- l10n: replace orderReputationStats with reputationTradesLabel /
  reputationDaysLabel and add sortBestPremium (en/es/fr/de/it).
- Regenerate order-list-item goldens.
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 24189034-daee-4ac0-938f-a05f8bf086aa

📥 Commits

Reviewing files that changed from the base of the PR and between 8adba75 and 99222a9.

⛔ Files ignored due to path filters (2)
  • test/features/home/goldens/order_list_item_dark.png is excluded by !**/*.png
  • test/features/home/goldens/order_list_item_light.png is excluded by !**/*.png
📒 Files selected for processing (16)
  • lib/core/app_theme.dart
  • lib/features/home/screens/home_screen.dart
  • lib/features/home/widgets/order_list_item.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart
  • lib/shared/widgets/order_list_skeleton.dart
  • test/core/order_book_palette_contrast_test.dart
🚧 Files skipped from review as they are similar to previous changes (6)
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_it.dart
  • lib/features/home/screens/home_screen.dart
  • lib/features/home/widgets/order_list_item.dart
  • lib/l10n/app_localizations_es.dart

Walkthrough

The order book screen now uses dedicated palette tokens, redesigned offer cards and layout spacing, simplified app-bar behavior, palette-based loading and empty states, contrast validation, and separate localized labels for reputation statistics and newest sorting.

Changes

Order book redesign

Layer / File(s) Summary
Order book palette contract
lib/core/app_theme.dart, test/core/order_book_palette_contrast_test.dart
Adds immutable dark and light OrderBookPalette tokens with brightness-based selection and WCAG contrast tests for text, pills, and tabs.
Localization contract
lib/l10n/app_localizations*, lib/l10n/app_*.arb
Replaces combined reputation-stat formatting with separate pluralized trades/days labels and adds the newest sort caption in supported locales.
Home screen layout integration
lib/features/home/screens/home_screen.dart
Updates order-list spacing, tabs, filters, loading/error states, scaffold background, and the simplified responsive app bar.
Offer card and state rendering
lib/features/home/widgets/order_list_item.dart, lib/shared/widgets/order_list_skeleton.dart
Redesigns offer cards, reason/premium/yours pills, reputation statistics, payment rows, skeletons, and empty-state styling using the new palette and localized labels.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HomeScreen
  participant OrderBookPalette
  participant orderBookProvider
  participant OrderListItem
  HomeScreen->>OrderBookPalette: select palette from BuildContext
  HomeScreen->>orderBookProvider: read order book state
  orderBookProvider-->>HomeScreen: loading, error, or orders
  HomeScreen->>OrderListItem: render each order with palette and localization
Loading

Possibly related PRs

Suggested reviewers: andreadiazcorreia, ermeme

Poem

I’m a bunny with a freshly painted book,
Red cards, green stars, and a brighter look.
Trades and days now hop apart,
Newest sorting gets a brand-new start.
Hop, hop—the order board is smart!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: a pixel-exact Order Book screen port from design mock #3.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/order-book-claude-design

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8adba752dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

),
const Spacer(),
Text(
l10n.sortBestPremium,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the displayed sort match the actual order

The new caption tells users that offers are sorted by best premium, but filteredOrdersProvider still sorts by createdAt descending (lib/features/home/providers/home_order_providers.dart:212), and this caption has no interaction that changes the ordering. Whenever premium order differs from publication order, users may select a worse offer under the false impression that the best premium is first; implement the advertised sort or label the ordering that is actually applied. The active contract also specifies expiration-time ordering in specs/004-mostro-p2p-client/spec.md:328, so any intentional contract change needs to be reconciled there.

AGENTS.md reference: AGENTS.md:L74-L75

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 99222a9 — relabeled the caption to "Sort: newest first ▾" (all five locales), which is the createdAt-descending order filteredOrdersProvider actually applies. Implementing a premium sort would change the protocol-layer ordering contract (spec 004 §ordering) and is out of this PR's UI scope; the ARB description now states the caption must describe the ordering actually applied.

size: 16,
color: colors?.textSecondary,
padding: const EdgeInsets.fromLTRB(16, 14, 16, 6),
child: Row(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow the localized filter row to fit narrow screens

This Row gives both the filter pill and sort caption their full intrinsic widths, with only a Spacer between them and no flexible or wrapping child. On a common 320px-wide device, longer supported translations such as German (FILTERN · N Angebote plus Sortierung: beste Prämie ▾) exceed the available 288px and produce a horizontal RenderFlex overflow/clipping; constrain, flex, or wrap these labels so the localized screen remains usable.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 99222a9 — the FILTER pill and the sort caption are now both Flexible with TextOverflow.ellipsis (the offer-count inside the pill too), so long de/fr strings degrade gracefully on 320px screens instead of overflowing the RenderFlex.

final colors = Theme.of(context).extension<AppColors>();
final cardBg = colors?.backgroundCard ?? const Color(0xFF1E2230);
final shimmer = colors?.backgroundInput ?? const Color(0xFF252A3A);
final pal = OrderBookPalette.of(context);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Wire the redesigned skeleton into the loading state

The palette-aware skeleton being updated here is dead code: a repo-wide search for OrderListItemSkeleton finds only this declaration, while the home loading branch still creates the shared OrderListSkeleton. That rendered widget retains the old hard-coded dark colors, 12px radius, and 100px blocks, so loading never shows this redesign and remains especially incorrect in light mode; use this skeleton from the loading list or update the shared widget that is actually instantiated.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 99222a9 — the rendered OrderListSkeleton (shared widget actually instantiated by the loading branch) now reads OrderBookPalette for shimmer base/highlight, uses radius 16, ~172px cards, and the redesigned 16/8/90 list padding with 12px gaps, correct in both themes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/features/home/widgets/order_list_item.dart (1)

89-115: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reason pill is unbounded while the "yours" pill is Flexible.

In row 1 the reason _Pill has no width constraint, so a long localized label (German/French are noticeably longer than English) on a narrow card can overflow the Row instead of ellipsizing — the _Pill TextOverflow.ellipsis only helps once the pill itself is bounded. Wrapping it in Flexible matches the treatment already applied to mineLabel.

♻️ Proposed change
                 if (reasonLabel != null) ...[
-                  _Pill(
-                    label: reasonLabel,
-                    color: reasonColor!,
-                    background: reasonBg!,
-                  ),
+                  Flexible(
+                    child: _Pill(
+                      label: reasonLabel,
+                      color: reasonColor!,
+                      background: reasonBg!,
+                    ),
+                  ),
                   const SizedBox(width: 6),
                 ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/home/widgets/order_list_item.dart` around lines 89 - 115, Wrap
the reasonLabel _Pill in the first Row’s reasonLabel branch with Flexible,
matching the existing mineLabel handling, so its width is constrained and its
internal ellipsis can apply on narrow cards. Preserve the existing label,
colors, spacing, and the rest of the Row layout.
🧹 Nitpick comments (2)
lib/features/home/widgets/order_list_item.dart (1)

369-378: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Empty-state copy is low-contrast in light theme.

textTertiary is #888888, giving roughly 3.5:1 on the light bg (#FFFFFF) for 14px regular text — under AA. Since this is the only content on screen, textSecondary would be safer here.

#!/bin/bash
python - <<'PY'
def lin(c):
    c/=255
    return c/12.92 if c<=0.03928 else ((c+0.055)/1.055)**2.4
def L(h):
    r,g,b=(int(h[i:i+2],16) for i in (0,2,4))
    return 0.2126*lin(r)+0.7152*lin(g)+0.0722*lin(b)
def ratio(a,b):
    la,lb=L(a),L(b)
    hi,lo=max(la,lb),min(la,lb)
    return (hi+0.05)/(lo+0.05)
for fg in ("888888","666666","AAAAAA"):
    print(fg, round(ratio(fg,"FFFFFF"),2))
PY
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/home/widgets/order_list_item.dart` around lines 369 - 378,
Update the empty-state styling in the visible order-list widget to use
OrderBookPalette.textSecondary instead of textTertiary for the no-orders Icon
and Text, preserving the existing layout, copy, and palette lookup.
lib/features/home/screens/home_screen.dart (1)

222-223: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use palette colors for the loading skeleton shimmer.

OrderListSkeleton still uses 0xFF1E2230/0xFF2A2D35 and AppSpacing padding, while OrderListItemSkeleton now reads OrderBookPalette. Make the loading state follow the redesigned palette so the skeleton matches the cards, especially in light theme.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/home/screens/home_screen.dart` around lines 222 - 223, Update
the OrderListSkeleton loading state used by orderBookProvider to use
OrderBookPalette colors for its shimmer and replace AppSpacing padding with the
redesigned skeleton/card spacing, matching OrderListItemSkeleton across light
and dark themes. Leave the surrounding loading/error/data handling unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/features/home/screens/home_screen.dart`:
- Around line 170-210: Replace the FILTER pill’s GestureDetector in
lib/features/home/screens/home_screen.dart lines 170-210 with an InkWell using
BorderRadius.circular(999), preserving showOrderFilterDialog(context) and the
existing pill content. In lib/features/home/widgets/order_list_item.dart lines
76-78, replace the card GestureDetector and HitTestBehavior.opaque with an
InkWell or Semantics(button: true), preserving card activation while making each
offer keyboard-accessible and announced as a button.
- Around line 151-152: Update the inactive tab label color in the tab
configuration near labelColor to use pal.textSecondary instead of
pal.textDisabled, preserving the active pal.green color and dark-theme behavior.

In `@lib/features/home/widgets/order_list_item.dart`:
- Around line 46-47: Update the numeric formatting in the order card, including
premiumText, _formatRating, and _StatText values, to use locale-aware intl
NumberFormat based on Localizations.localeOf(context) instead of toStringAsFixed
or raw interpolation. Preserve the existing signs, precision, and displayed
values while emitting localized decimal separators and digits for the active
locale.
- Around line 189-204: Update the _StatText usages for trade and day counts to
use localized ICU plural messages, such as the generated
reputationTradesLabel(count) and reputationDaysLabel(count), instead of
concatenating raw numbers with static labels. Add or update these pluralized
messages in the Dart ARB localization files for every supported locale,
preserving the existing localized output for each count.

In `@lib/l10n/app_localizations.dart`:
- Around line 3869-3885: Replace the static getters reputationTradesLabel and
reputationDaysLabel with ICU pluralized counted getters accepting an int count,
following the existing sibling counted-getter pattern. Regenerate the
localization files and update all order-card call sites to pass the
corresponding trade and active-day counts instead of concatenating raw unit
labels.

---

Outside diff comments:
In `@lib/features/home/widgets/order_list_item.dart`:
- Around line 89-115: Wrap the reasonLabel _Pill in the first Row’s reasonLabel
branch with Flexible, matching the existing mineLabel handling, so its width is
constrained and its internal ellipsis can apply on narrow cards. Preserve the
existing label, colors, spacing, and the rest of the Row layout.

---

Nitpick comments:
In `@lib/features/home/screens/home_screen.dart`:
- Around line 222-223: Update the OrderListSkeleton loading state used by
orderBookProvider to use OrderBookPalette colors for its shimmer and replace
AppSpacing padding with the redesigned skeleton/card spacing, matching
OrderListItemSkeleton across light and dark themes. Leave the surrounding
loading/error/data handling unchanged.

In `@lib/features/home/widgets/order_list_item.dart`:
- Around line 369-378: Update the empty-state styling in the visible order-list
widget to use OrderBookPalette.textSecondary instead of textTertiary for the
no-orders Icon and Text, preserving the existing layout, copy, and palette
lookup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c963df81-dae5-4356-bf38-303f5e073442

📥 Commits

Reviewing files that changed from the base of the PR and between 1931ac0 and 8adba75.

⛔ Files ignored due to path filters (2)
  • test/features/home/goldens/order_list_item_dark.png is excluded by !**/*.png
  • test/features/home/goldens/order_list_item_light.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • lib/core/app_theme.dart
  • lib/features/home/screens/home_screen.dart
  • lib/features/home/widgets/order_list_item.dart
  • lib/l10n/app_de.arb
  • lib/l10n/app_en.arb
  • lib/l10n/app_es.arb
  • lib/l10n/app_fr.arb
  • lib/l10n/app_it.arb
  • lib/l10n/app_localizations.dart
  • lib/l10n/app_localizations_de.dart
  • lib/l10n/app_localizations_en.dart
  • lib/l10n/app_localizations_es.dart
  • lib/l10n/app_localizations_fr.dart
  • lib/l10n/app_localizations_it.dart

Comment thread lib/features/home/screens/home_screen.dart Outdated
Comment thread lib/features/home/screens/home_screen.dart Outdated
Comment thread lib/features/home/widgets/order_list_item.dart Outdated
Comment thread lib/features/home/widgets/order_list_item.dart
Comment thread lib/l10n/app_localizations.dart Outdated

@ermeme ermeme Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Strict review — changes requested

I reviewed commit 8adba752dcb7f3adfc562f593480dde50005f258 after reading the full existing conversation and current thread state.

I did not duplicate prior inline comments. The existing unresolved threads about the advertised sort not matching the actual order, the narrow localized filter row overflowing, and the redesigned loading skeleton not being wired into the rendered loading state are still valid blockers on this head.

I found one additional blocking accessibility issue in the new palette; see the inline comment. The current golden tests do not enforce contrast or cover the full HomeScreen/responsive states.

CI is green for this exact head, but those runtime/UI issues are not exercised by the current suite.

Comment thread lib/core/app_theme.dart Outdated
border: Color(0x14000000),
textPrimary: Color(0xFF1A1A1A),
textSecondary: Color(0xFF666666),
textTertiary: Color(0xFF888888),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Blocking: the new palette's “legibility mapping” does not provide legible text contrast on the surfaces where these tokens are rendered. This is broader than the existing inactive-tab/empty-state comments. Using WCAG relative luminance against the actual composited backgrounds:

  • textTertiary is only 3.39:1 on the dark card and 3.25:1 on the light card, but it renders the 11px timestamp and “Market price” text; the sort caption is 3.80:1 dark / 3.54:1 light on the page.
  • The light active tab (green on white) is 3.23:1 at 13px/700.
  • Light green and amber premium pills are 2.61:1 and 2.66:1 after their 13% fills are composited over bgCard; the light green/gold reason pills are about 2.56:1 / 2.57:1 on their 15% fills. The dark red premium pill is 3.70:1.

All of these are normal-sized text and need 4.5:1. Please choose role-specific colors that meet the threshold on each real rendered surface and add deterministic contrast tests so the light/dark legibility contract cannot regress.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 99222a9 — role-specific colors adjusted to meet 4.5:1 on the actual composited surfaces: dark textTertiary #6B7280→#848C9A (4.83:1 on the card, 5.42:1 on the page) and dark red #E5484D→#F27D81 (5.12:1 on its 13% fill); light green→#426800, gold→#7E5C09, amber→#845010, blue→#35638F, textTertiary→#696969, red→#AE3333, and the light active tab now uses the darkened green (5.84:1). Added test/core/order_book_palette_contrast_test.dart, which composites the translucent pill fills over their surfaces and asserts AA for every text-role/surface pair in both palettes, so the legibility contract cannot regress. Dark values not flagged (green/gold/blue pills, secondary text) were verified ≥5.7:1 and are also locked by the test.

…8n plurals/numbers

Addresses all Codex, CodeRabbit, and ermeme review findings on #243:

- Palette (ermeme, blocking): every text role now meets WCAG AA 4.5:1 on
  its real composited surface. Dark textTertiary #6B7280→#848C9A and
  red #E5484D→#F27D81 (13% pill fill was 3.7:1); light green/gold/amber/
  blue/red/textTertiary darkened. textDisabled replaced by a tabInactive
  role — dark keeps the mock's dimmed tab, light uses AA-compliant gray.
  New deterministic test locks every pair (order_book_palette_contrast_test).
- Sort caption (Codex): relabeled to 'Sort: newest first' — the caption
  now describes the createdAt-descending order the provider actually
  applies (spec 004 ordering contract unchanged).
- Filter row (Codex): FILTER pill and sort caption are Flexible with
  ellipsis so long de/fr strings fit 320px screens without overflow.
- Loading skeleton (Codex + CodeRabbit nit): OrderListSkeleton now uses
  OrderBookPalette shimmer colors, radius 16, and the redesigned list
  padding, in both themes.
- A11y semantics (CodeRabbit): FILTER pill and offer cards use
  Material+InkWell (focusable, keyboard-activatable, button semantics)
  instead of raw GestureDetector.
- Locale numbers (CodeRabbit): premium, rating, and counts formatted via
  intl NumberFormat with the active locale (decimal comma in es/fr/de/it).
- Plural agreement (CodeRabbit): reputationTradesLabel/reputationDaysLabel
  are ICU plural messages taking the count ('1 trade' / '2 trades') in all
  five locales.
- Reason pill wrapped in Flexible (CodeRabbit, outside diff) so long
  localized labels ellipsize instead of overflowing.
- Empty state uses textSecondary (CodeRabbit nit).
- Goldens regenerated.
@grunch

grunch commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

Review round 1 addressed in 99222a9 — all 9 findings from Codex, CodeRabbit (incl. nitpicks and the outside-diff comment), and ermeme's blocking a11y review are fixed: WCAG AA palette adjustments locked by a new deterministic contrast test, honest sort caption, overflow-safe filter row, palette-aware loading skeleton, InkWell semantics, locale-aware number formatting, and ICU plurals. flutter analyze clean, 153/153 tests passing, goldens regenerated. Per-thread replies posted.

@grunch
grunch merged commit ac2e6be into main Jul 29, 2026
4 checks passed
@grunch
grunch deleted the feat/order-book-claude-design branch July 29, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant