ux: /trade layout — flatten nested chrome, independent Recent trades, toggle side panels (no drag-resize) #561

Closed
opened 2026-08-18 12:08:51 +00:00 by PlasticDigits · 13 comments
PlasticDigits commented 2026-08-18 12:08:51 +00:00 (Migrated from gitlab.com)

Related: order-ticket heading, compact Connect Wallet, and Buy/Sell colors are #563.

Summary

The /trade desktop workspace stacks nested cards, embeds Recent trades in the chart column behind a drag-to-resize split, and exposes always-on splitter bars. That makes the chart feel boxed-in, forces traders to drag a thin handle to see the tape, and lets a user collapse the chart out of the visual hierarchy. Treat the chart as the dominant surface: one chrome layer per region, tape as an independent bottom row, and toggle/hide for the order book and order ticket (chart expands into the vacated space). Do not ship drag-resize in the default layout.

This is a product-completeness UX change for a live-money trading screen. Persistent splitter chrome reads as unfinished.

Current codebase

Desktop (lg+, min-width: 1024px) is a nested react-resizable-panels tree in TradePage.tsx:

horizontal PanelGroup
  Panel 24%  → OrderBookPanel (own `card-glass`)
  PanelResizeHandle  (`trade-book-chart-resize-handle`, `w-1.5 bg-white/10`)
  Panel 52%  → vertical PanelGroup
                 Panel 58% → extra `card-glass` wrapping `TradeChartSlot` (`wrapInCard={false}`)
                              → `PriceChart` already renders `shell-panel-strong`
                 PanelResizeHandle (`trade-chart-tape-resize-handle`)
                 collapsible Panel 6% / 42% → another `card-glass` “Recent trades”
  PanelResizeHandle  (`trade-ticket-resize-handle`)
  Panel 24%  → `TradeOrderTicket` (`card-glass !p-0`)

PriceChart (PriceChart.tsx) is already a shell-panel-strong region (headline, interval chips, fullscreen). Wrapping it again in card-glass is the visible box-in-box. Sub-desktop TradeChartSlot still uses wrapInCard={true} (same double chrome).

Recent trades on desktop live inside the chart column (trade-desktop-tape-panel, default collapsed per #417). Users expand via trade-desktop-tape-toggle or by dragging the horizontal handle. Sub-desktop already places tape as an independent grid row (md:row-start-3 + TradeWorkspaceDisclosure).

Playwright trade-page-responsive.spec.ts P10 currently requires dragging trade-ticket-resize-handle and asserting the ticket width changes. AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md currently says keep react-resizable-panels unless there is a product decision — this issue is that decision.

Design system today: docs/design-system.md maps shell-panel* to page sections and card-glass to nested blocks, but does not forbid wrapping a section that already has chrome.

Why the new implementation is needed

  1. Nested boxes — Chart shell-panel-strong inside a second card-glass (plus the workspace frame) reads as clutter, not hierarchy. Retail DEX terminals use one surface per region.
  2. Tape placement — Embedding Recent trades under the candles (and default-collapsing it into a 6% sliver) hides a primary market-context panel. Traders should not drag a splitter to see prints. Sub-desktop already treats tape as its own row; desktop should match.
  3. Splitters — Always-visible drag handles let users squash the chart (the intended hero) and look like a debug layout. A larger chart is better served by hiding the book and/or ticket (and the existing chart Expand fullscreen control) than by free-form column drag.
  4. Finish — Live-money UI should not advertise resizable lab chrome.

Constraints / guardrails

  1. Chart stays dominant. Default desktop proportions keep the candle pane the largest region. Hiding a side panel gives that width to the chart, not to the other side panel.
  2. No default drag-resize. Remove PanelResizeHandle chrome from the default /trade layout. Do not add a Settings “Customize layout” / numeric column editor in this issue. If drag-resize is ever revived, it must be opt-in behind an advanced setting and off by default — out of scope here.
  3. One TradeOrderTicket mount (#178). Hide with CSS + inert (or equivalent) rather than a second instance. Do not remount a second ticket on lg vs sub-lg.
  4. Hidden ticket is inert. When the order ticket is hidden: not focusable, not submittable, no wallet-modal open from that tree. Book row Edit / cancel (#162) re-shows the ticket before applying a draft.
  5. Chart flex chain (#151): wrappers around PriceChart stay flex flex-col min-h-0 / h-full so candles are not clipped. Flattening chrome must not restore a fixed chart height.
  6. Ticket footer dock (#527 / #500): when the ticket is visible, Place limit / Market / Connect Wallet stay in trade-ticket-submit-footer. Hiding the ticket hides that footer with it (header Connect Wallet remains).
  7. Sub-desktop grid (#146) stays CSS grid (trade-sub-lg-workspace). No new splitter bars below lg. Flatten double chrome on the sub-lg chart card. Tape may remain a disclosure on small viewports.
  8. Tape data path unchanged. Keep getTrades / TradeRecentTradesSection / indexer outage copy (#164, #174). Do not change tape amount/price formatting (that is #557).
  9. Persistence is boolean flags only — extend tradeWorkspacePanels.ts ('1' / '0'). Ignore any other stored string. No JSON, no eval, no query-param layout (no shareable “hidden ticket” phishing URLs required).
  10. Pause / blacklist / outage banners stay visible when panels hide (#395, #388).
  11. Pair selector + invert + portal listbox (#181, #524) must not shift or clip when panels toggle.
  12. Chart fullscreen (price-chart-fullscreen) stays. Panel toggles complement it; they do not replace it.
  13. #489 copy: toggle labels ≤ ~5 words (Order book, Ticket, Recent trades). No instructional essays.
  14. Design principle: add “one surface per region / no box-in-box” to docs/design-system.md and the design-system agent skill. First apply on /trade. Audit Swap / Pool / Limits / Charts for card-glass wrapping shell-panel* (or card-in-card). Fix trivial extra wrappers in the same change; do not rewrite those routes’ IA.
  15. Do not change on-chain messages, quote paths, or wallet connect semantics.

Relevant files

File Role
frontend-dapp/src/pages/TradePage.tsx Desktop PanelGroup, resize handles, chart/tape nesting
frontend-dapp/src/pages/TradePage.test.tsx Desktop tape toggle / layout RTL
frontend-dapp/src/utils/tradeWorkspacePanels.ts Tape expand persistence — extend for book/ticket visibility
frontend-dapp/src/utils/__tests__/tradeWorkspacePanels.test.ts Persistence unit tests
frontend-dapp/src/utils/tradePageLayout.ts lg media query
frontend-dapp/src/components/trade/TradeRecentTradesSection.tsx Tape body
frontend-dapp/src/components/trade/TradeWorkspaceDisclosure.tsx Sub-lg / wallet history disclosure
frontend-dapp/src/components/charts/PriceChart.tsx Already shell-panel-strong
frontend-dapp/src/components/trade/OrderBookPanel.tsx Book card-glass
frontend-dapp/src/components/trade/TradeOrderTicket.tsx Ticket card + footer
frontend-dapp/e2e/trade-page-responsive.spec.ts P10 resize — must be rewritten
docs/frontend.md § Trade page — responsive layout / onboarding IA Desktop PanelGroup + collapsed tape invariants
docs/design-system.md New no-box-in-box principle
skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md Replace “keep resizable panels” rule
skills/AGENTS_FRONTEND_DESIGN_SYSTEM.md Principle for agents
skills/AGENTS_FRONTEND_TRADE_ONBOARDING_IA.md Tape default-collapsed copy
  1. Design principle — In the Principles table: one chrome layer per region. Do not wrap shell-panel / shell-panel-strong / card-glass in another of the same family. Page background → section surface → content.
  2. Desktop layout — CSS grid (preferred) or a non-resizable PanelGroup (disabled handles removed from the DOM). Regions: book | chart | ticket on the top row; Recent trades full-width (or chart-column-aligned) bottom row, not a vertical split inside the chart cell.
  3. Flatten chart — Drop the extra card-glass around TradeChartSlot on desktop and set wrapInCard={false} on sub-lg too (or drop the wrapper helper). Let PriceChart be the only surface.
  4. Tape — Independent panel with its own heading. Default can stay compact/collapsed on first visit (#417) without stealing chart height via a splitter. Expand/collapse is a button, not a drag. Persist via existing TRADE_TAPE_EXPANDED_KEY.
  5. Toggles — Icon/text controls (workspace chrome or pair-row) to hide Order book and Order ticket. Persist cl8y-dex-trade-book-visible / cl8y-dex-trade-ticket-visible (names illustrative). Both hidden → chart uses the full workspace width. Always show a control to restore a hidden panel (do not leave a dead end).
  6. Book row actions — If ticket is hidden, show it, then apply the draft.
  7. Rewrite P10 — Assert absence of trade-ticket-resize-handle / trade-book-chart-resize-handle / trade-chart-tape-resize-handle on desktop. Add tests for hide/show book and ticket and chart width growth.
  8. Docs / skills — Update docs/frontend.md desktop row, onboarding tape invariant, design-system principle, and AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md rule 1.

Acceptance criteria

  • L1. /trade desktop chart is a single surface (PriceChart shell-panel-strong). No card-glass wrapping it.
  • L2. Design-system + agent skill document no box-in-box. Swap / Pool / Limits / Charts audited; trivial extra wrappers removed or listed with reason.
  • L3. Desktop Recent trades is an independent bottom-row panel, not inside the chart vertical stack. No horizontal splitter between candles and tape.
  • L4. No PanelResizeHandle (or equivalent drag chrome) in the default /trade layout. P10 rewritten.
  • L5. User can hide Order book; chart expands into that width. Restore control remains visible.
  • L6. User can hide Order ticket; chart expands. Restore control remains visible. Hidden ticket is inert (no focus, no submit).
  • L7. Hiding both side panels gives the chart the full workspace width. Chart remains the largest region in the default (both visible) state.
  • L8. Tape expand/collapse still persists. Book/ticket visibility persists across reload. Corrupt localStorage values fall back to defaults (both side panels visible; tape first-visit compact).
  • L9. Sub-lg grid unchanged in structure (#146); chart double-wrap flattened. Still exactly one TradeOrderTicket mount (#178).
  • L10. Visible ticket keeps footer CTA dock (#527). Chart candles not clipped (#151). Pair selector portal does not shift layout (#181).
  • L11. Book Edit while ticket hidden re-shows the ticket and applies the draft.
  • L12. docs/frontend.md, design-system, and AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md match the new layout. make verify-issue-<iid> covers unit + targeted e2e.

Test plan — functional paths

Layout / chrome

  • Desktop lg (1280×720 and 1440×900): three columns; chart widest; no resize handles in DOM (getByTestId absent).
  • Sub-lg phone and tablet (trade-sub-lg-workspace): order of book / ticket / chart / tape per #146; no desktop handles.
  • Chart region: one bordered surface; no inner rounded frame around the same chart.
  • Fullscreen chart still works; exiting restores the grid.

Recent trades

  • Tape is below the three-column row, not overlaid on candles.
  • First visit (cleared cl8y-dex-trade-tape-expanded): compact/collapsed tape without covering the plot.
  • Expand → TradesTable (or outage / retry) visible; Collapse returns to compact. Reload preserves.
  • Indexer down: tape unavailable notice still renders (#164).
  • Pair switch: tape follows the new pair (no stale rows).

Panel toggles

  • Hide book → book unmounted or inert/not visible; chart width increases; ticket width unchanged (or not expanded at book’s expense).
  • Hide ticket → ticket inert; chart width increases; footer CTA gone; header Connect Wallet still works.
  • Hide both → chart uses remaining workspace; restore controls visible for both.
  • Show again → form fields (limit price, amount, side, tab) preserved if hide used inert rather than unmount (document the choice; if unmount, defaults restore — pick one and test it).
  • Reload with book hidden / ticket hidden restores that state.
  • Keyboard: Tab never lands inside a hidden panel. Restore control is reachable.

Ticket / book interactions

  • Visible ticket: Place limit / Market / Connect Wallet still in trade-ticket-submit-footer; expiry scroll still above footer (existing P6/P7).
  • Book row Edit with ticket hidden: ticket becomes visible, draft applied, no second ticket.
  • Pair invert pill and pair selector still work with any panel combination.

Persistence helpers

  • readTradePanelExpanded / new visibility helpers: '1' / '0' only; null → default; garbage → default; localStorage throw → default.

Test plan — attack, hack, and abuse vectors

  • A1. Stored layout injection. Set visibility keys to true, {"hidden":true}, <script>, very long strings. UI uses defaults or boolean parse only; no DOM injection; no crash.
  • A2. Hidden-ticket submit. With ticket hidden, dispatch click / Enter on trade-limit-submit / trade-market-submit (if still in DOM). Must not open wallet modal, must not broadcast.
  • A3. Focus theft. autofocus / programmatic focus must not pull keyboard into aria-hidden ticket. Restore control remains operable.
  • A4. Phishing-lite chrome. Hiding ticket/book must not remove pause, blacklist, indexer-outage, or invalid-pair alerts. Chart-only is not a “trusted fullscreen spoof” of another origin — no postMessage layout API.
  • A5. Query / hash layout. /trade/:pair?layout= or hash flags must not hide the ticket or book (no shareable “deposit-looking” stripped ticket). Toggles are local only.
  • A6. Click-jacking via leftover handles. After removing splitters, no 6px invisible hit target over the chart or ticket that swallows Place / interval clicks.
  • A7. Double mount. Resize across lg breakpoint repeatedly: still one ticket; no duplicate Place limit.
  • A8. Quota / private mode. localStorage throws: toggles still work for the session; no uncaught exception white-screen.
  • A9. Stale pair + hidden ticket. Switch pair while ticket hidden; showing ticket must bind to the current pairAddr, not a previous pair’s draft, unless the draft is explicitly cleared (document). Do not place on the wrong pair.

Verification criteria

make lint-frontend
make test-frontend
# targeted:
#   frontend-dapp/src/pages/TradePage.test.tsx
#   frontend-dapp/src/utils/__tests__/tradeWorkspacePanels.test.ts
#   frontend-dapp/e2e/trade-page-responsive.spec.ts
python3 scripts/check_design_tokens.py
make verify-issue-<iid>   # add Makefile target + skill pointers

Manual (dark + light, 1280×720 and 1440×900): /trade on a hub pair — single chart surface, tape in the bottom row, no white splitter bars, hide/show book and ticket with chart expansion, Place limit footer only when ticket visible, fullscreen still works. Phone/tablet: no splitters, tape still reachable. Nested-card audit notes in the MR for Swap / Pool / Limits / Charts.

**Related:** order-ticket heading, compact Connect Wallet, and Buy/Sell colors are [#563](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/563). ## Summary The `/trade` desktop workspace stacks **nested cards**, embeds **Recent trades** in the chart column behind a **drag-to-resize** split, and exposes **always-on splitter bars**. That makes the chart feel boxed-in, forces traders to drag a thin handle to see the tape, and lets a user collapse the chart out of the visual hierarchy. Treat the chart as the dominant surface: **one chrome layer per region**, **tape as an independent bottom row**, and **toggle/hide** for the order book and order ticket (chart expands into the vacated space). Do **not** ship drag-resize in the default layout. This is a product-completeness UX change for a live-money trading screen. Persistent splitter chrome reads as unfinished. ## Current codebase Desktop (`lg+`, `min-width: 1024px`) is a nested `react-resizable-panels` tree in [`TradePage.tsx`](frontend-dapp/src/pages/TradePage.tsx): ``` horizontal PanelGroup Panel 24% → OrderBookPanel (own `card-glass`) PanelResizeHandle (`trade-book-chart-resize-handle`, `w-1.5 bg-white/10`) Panel 52% → vertical PanelGroup Panel 58% → extra `card-glass` wrapping `TradeChartSlot` (`wrapInCard={false}`) → `PriceChart` already renders `shell-panel-strong` PanelResizeHandle (`trade-chart-tape-resize-handle`) collapsible Panel 6% / 42% → another `card-glass` “Recent trades” PanelResizeHandle (`trade-ticket-resize-handle`) Panel 24% → `TradeOrderTicket` (`card-glass !p-0`) ``` `PriceChart` ([`PriceChart.tsx`](frontend-dapp/src/components/charts/PriceChart.tsx)) is already a `shell-panel-strong` region (headline, interval chips, fullscreen). Wrapping it again in `card-glass` is the visible **box-in-box**. Sub-desktop `TradeChartSlot` still uses `wrapInCard={true}` (same double chrome). **Recent trades** on desktop live **inside the chart column** (`trade-desktop-tape-panel`, default collapsed per [#417](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/417)). Users expand via `trade-desktop-tape-toggle` or by dragging the horizontal handle. Sub-desktop already places tape as an independent grid row (`md:row-start-3` + `TradeWorkspaceDisclosure`). Playwright [`trade-page-responsive.spec.ts`](frontend-dapp/e2e/trade-page-responsive.spec.ts) **P10** currently **requires** dragging `trade-ticket-resize-handle` and asserting the ticket width changes. [`AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md`](skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md) currently says keep `react-resizable-panels` unless there is a product decision — **this issue is that decision**. Design system today: [`docs/design-system.md`](docs/design-system.md) maps `shell-panel*` to page sections and `card-glass` to nested blocks, but does **not** forbid wrapping a section that already has chrome. ## Why the new implementation is needed 1. **Nested boxes** — Chart `shell-panel-strong` inside a second `card-glass` (plus the workspace frame) reads as clutter, not hierarchy. Retail DEX terminals use one surface per region. 2. **Tape placement** — Embedding Recent trades under the candles (and default-collapsing it into a 6% sliver) hides a primary market-context panel. Traders should not drag a splitter to see prints. Sub-desktop already treats tape as its own row; desktop should match. 3. **Splitters** — Always-visible drag handles let users squash the chart (the intended hero) and look like a debug layout. A larger chart is better served by **hiding the book and/or ticket** (and the existing chart **Expand** fullscreen control) than by free-form column drag. 4. **Finish** — Live-money UI should not advertise resizable lab chrome. ## Constraints / guardrails 1. **Chart stays dominant.** Default desktop proportions keep the candle pane the largest region. Hiding a side panel **gives that width to the chart**, not to the other side panel. 2. **No default drag-resize.** Remove `PanelResizeHandle` chrome from the default `/trade` layout. Do **not** add a Settings “Customize layout” / numeric column editor in this issue. If drag-resize is ever revived, it must be opt-in behind an advanced setting and **off** by default — out of scope here. 3. **One `TradeOrderTicket` mount** ([#178](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/178)). Hide with CSS + `inert` (or equivalent) rather than a second instance. Do not remount a second ticket on `lg` vs sub-`lg`. 4. **Hidden ticket is inert.** When the order ticket is hidden: not focusable, not submittable, no wallet-modal open from that tree. Book row **Edit** / cancel ([#162](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/162)) **re-shows** the ticket before applying a draft. 5. **Chart flex chain** ([#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)): wrappers around `PriceChart` stay `flex flex-col min-h-0` / `h-full` so candles are not clipped. Flattening chrome must not restore a fixed chart height. 6. **Ticket footer dock** ([#527](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/527) / [#500](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/500)): when the ticket is **visible**, Place limit / Market / Connect Wallet stay in `trade-ticket-submit-footer`. Hiding the ticket hides that footer with it (header Connect Wallet remains). 7. **Sub-desktop grid** ([#146](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/146)) stays CSS grid (`trade-sub-lg-workspace`). No new splitter bars below `lg`. Flatten double chrome on the sub-lg chart card. Tape may remain a disclosure on small viewports. 8. **Tape data path unchanged.** Keep `getTrades` / `TradeRecentTradesSection` / indexer outage copy ([#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164), [#174](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/174)). Do not change tape amount/price formatting (that is [#557](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/557)). 9. **Persistence is boolean flags only** — extend [`tradeWorkspacePanels.ts`](frontend-dapp/src/utils/tradeWorkspacePanels.ts) (`'1'` / `'0'`). Ignore any other stored string. No JSON, no `eval`, no query-param layout (no shareable “hidden ticket” phishing URLs required). 10. **Pause / blacklist / outage banners stay visible** when panels hide ([#395](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/395), [#388](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/388)). 11. **Pair selector + invert + portal listbox** ([#181](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/181), [#524](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/524)) must not shift or clip when panels toggle. 12. **Chart fullscreen** (`price-chart-fullscreen`) stays. Panel toggles complement it; they do not replace it. 13. **#489 copy:** toggle labels ≤ ~5 words (**Order book**, **Ticket**, **Recent trades**). No instructional essays. 14. **Design principle:** add “one surface per region / no box-in-box” to [`docs/design-system.md`](docs/design-system.md) and the design-system agent skill. First apply on `/trade`. Audit Swap / Pool / Limits / Charts for `card-glass` wrapping `shell-panel*` (or card-in-card). Fix trivial extra wrappers in the same change; do not rewrite those routes’ IA. 15. **Do not** change on-chain messages, quote paths, or wallet connect semantics. ## Relevant files | File | Role | |------|------| | [`frontend-dapp/src/pages/TradePage.tsx`](frontend-dapp/src/pages/TradePage.tsx) | Desktop `PanelGroup`, resize handles, chart/tape nesting | | [`frontend-dapp/src/pages/TradePage.test.tsx`](frontend-dapp/src/pages/TradePage.test.tsx) | Desktop tape toggle / layout RTL | | [`frontend-dapp/src/utils/tradeWorkspacePanels.ts`](frontend-dapp/src/utils/tradeWorkspacePanels.ts) | Tape expand persistence — extend for book/ticket visibility | | [`frontend-dapp/src/utils/__tests__/tradeWorkspacePanels.test.ts`](frontend-dapp/src/utils/__tests__/tradeWorkspacePanels.test.ts) | Persistence unit tests | | [`frontend-dapp/src/utils/tradePageLayout.ts`](frontend-dapp/src/utils/tradePageLayout.ts) | `lg` media query | | [`frontend-dapp/src/components/trade/TradeRecentTradesSection.tsx`](frontend-dapp/src/components/trade/TradeRecentTradesSection.tsx) | Tape body | | [`frontend-dapp/src/components/trade/TradeWorkspaceDisclosure.tsx`](frontend-dapp/src/components/trade/TradeWorkspaceDisclosure.tsx) | Sub-lg / wallet history disclosure | | [`frontend-dapp/src/components/charts/PriceChart.tsx`](frontend-dapp/src/components/charts/PriceChart.tsx) | Already `shell-panel-strong` | | [`frontend-dapp/src/components/trade/OrderBookPanel.tsx`](frontend-dapp/src/components/trade/OrderBookPanel.tsx) | Book `card-glass` | | [`frontend-dapp/src/components/trade/TradeOrderTicket.tsx`](frontend-dapp/src/components/trade/TradeOrderTicket.tsx) | Ticket card + footer | | [`frontend-dapp/e2e/trade-page-responsive.spec.ts`](frontend-dapp/e2e/trade-page-responsive.spec.ts) | P10 resize — **must be rewritten** | | [`docs/frontend.md`](docs/frontend.md) § Trade page — responsive layout / onboarding IA | Desktop `PanelGroup` + collapsed tape invariants | | [`docs/design-system.md`](docs/design-system.md) | New no-box-in-box principle | | [`skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md`](skills/AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md) | Replace “keep resizable panels” rule | | [`skills/AGENTS_FRONTEND_DESIGN_SYSTEM.md`](skills/AGENTS_FRONTEND_DESIGN_SYSTEM.md) | Principle for agents | | [`skills/AGENTS_FRONTEND_TRADE_ONBOARDING_IA.md`](skills/AGENTS_FRONTEND_TRADE_ONBOARDING_IA.md) | Tape default-collapsed copy | ## Recommended direction 1. **Design principle** — In the Principles table: one chrome layer per region. Do not wrap `shell-panel` / `shell-panel-strong` / `card-glass` in another of the same family. Page background → section surface → content. 2. **Desktop layout** — CSS grid (preferred) or a **non-resizable** `PanelGroup` (`disabled` handles removed from the DOM). Regions: **book \| chart \| ticket** on the top row; **Recent trades** full-width (or chart-column-aligned) **bottom row**, not a vertical split inside the chart cell. 3. **Flatten chart** — Drop the extra `card-glass` around `TradeChartSlot` on desktop and set `wrapInCard={false}` on sub-lg too (or drop the wrapper helper). Let `PriceChart` be the only surface. 4. **Tape** — Independent panel with its own heading. Default can stay compact/collapsed on first visit ([#417](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/417)) **without** stealing chart height via a splitter. Expand/collapse is a button, not a drag. Persist via existing `TRADE_TAPE_EXPANDED_KEY`. 5. **Toggles** — Icon/text controls (workspace chrome or pair-row) to hide **Order book** and **Order ticket**. Persist `cl8y-dex-trade-book-visible` / `cl8y-dex-trade-ticket-visible` (names illustrative). Both hidden → chart uses the full workspace width. Always show a control to restore a hidden panel (do not leave a dead end). 6. **Book row actions** — If ticket is hidden, show it, then apply the draft. 7. **Rewrite P10** — Assert **absence** of `trade-ticket-resize-handle` / `trade-book-chart-resize-handle` / `trade-chart-tape-resize-handle` on desktop. Add tests for hide/show book and ticket and chart width growth. 8. **Docs / skills** — Update `docs/frontend.md` desktop row, onboarding tape invariant, design-system principle, and `AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md` rule 1. ## Acceptance criteria - [ ] **L1.** `/trade` desktop chart is a single surface (`PriceChart` `shell-panel-strong`). No `card-glass` wrapping it. - [ ] **L2.** Design-system + agent skill document **no box-in-box**. Swap / Pool / Limits / Charts audited; trivial extra wrappers removed or listed with reason. - [ ] **L3.** Desktop **Recent trades** is an independent bottom-row panel, not inside the chart vertical stack. No horizontal splitter between candles and tape. - [ ] **L4.** No `PanelResizeHandle` (or equivalent drag chrome) in the default `/trade` layout. P10 rewritten. - [ ] **L5.** User can hide Order book; chart expands into that width. Restore control remains visible. - [ ] **L6.** User can hide Order ticket; chart expands. Restore control remains visible. Hidden ticket is inert (no focus, no submit). - [ ] **L7.** Hiding both side panels gives the chart the full workspace width. Chart remains the largest region in the default (both visible) state. - [ ] **L8.** Tape expand/collapse still persists. Book/ticket visibility persists across reload. Corrupt `localStorage` values fall back to defaults (both side panels visible; tape first-visit compact). - [ ] **L9.** Sub-`lg` grid unchanged in structure ([#146](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/146)); chart double-wrap flattened. Still exactly one `TradeOrderTicket` mount ([#178](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/178)). - [ ] **L10.** Visible ticket keeps footer CTA dock ([#527](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/527)). Chart candles not clipped ([#151](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/151)). Pair selector portal does not shift layout ([#181](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/181)). - [ ] **L11.** Book **Edit** while ticket hidden re-shows the ticket and applies the draft. - [ ] **L12.** `docs/frontend.md`, design-system, and `AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md` match the new layout. `make verify-issue-<iid>` covers unit + targeted e2e. ## Test plan — functional paths ### Layout / chrome - [ ] Desktop `lg` (1280×720 and 1440×900): three columns; chart widest; no resize handles in DOM (`getByTestId` absent). - [ ] Sub-lg phone and tablet (`trade-sub-lg-workspace`): order of book / ticket / chart / tape per [#146](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/146); no desktop handles. - [ ] Chart region: one bordered surface; no inner rounded frame around the same chart. - [ ] Fullscreen chart still works; exiting restores the grid. ### Recent trades - [ ] Tape is below the three-column row, not overlaid on candles. - [ ] First visit (cleared `cl8y-dex-trade-tape-expanded`): compact/collapsed tape **without** covering the plot. - [ ] Expand → `TradesTable` (or outage / retry) visible; Collapse returns to compact. Reload preserves. - [ ] Indexer down: tape unavailable notice still renders ([#164](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/164)). - [ ] Pair switch: tape follows the new pair (no stale rows). ### Panel toggles - [ ] Hide book → book unmounted or `inert`/not visible; chart `width` increases; ticket width unchanged (or not expanded at book’s expense). - [ ] Hide ticket → ticket inert; chart width increases; footer CTA gone; header Connect Wallet still works. - [ ] Hide both → chart uses remaining workspace; restore controls visible for both. - [ ] Show again → form fields (limit price, amount, side, tab) preserved if hide used `inert` rather than unmount (document the choice; if unmount, defaults restore — pick one and test it). - [ ] Reload with book hidden / ticket hidden restores that state. - [ ] Keyboard: Tab never lands inside a hidden panel. Restore control is reachable. ### Ticket / book interactions - [ ] Visible ticket: Place limit / Market / Connect Wallet still in `trade-ticket-submit-footer`; expiry scroll still above footer (existing P6/P7). - [ ] Book row Edit with ticket hidden: ticket becomes visible, draft applied, no second ticket. - [ ] Pair invert pill and pair selector still work with any panel combination. ### Persistence helpers - [ ] `readTradePanelExpanded` / new visibility helpers: `'1'` / `'0'` only; `null` → default; garbage → default; `localStorage` throw → default. ## Test plan — attack, hack, and abuse vectors - [ ] **A1. Stored layout injection.** Set visibility keys to `true`, `{"hidden":true}`, `<script>`, very long strings. UI uses defaults or boolean parse only; no DOM injection; no crash. - [ ] **A2. Hidden-ticket submit.** With ticket hidden, dispatch click / Enter on `trade-limit-submit` / `trade-market-submit` (if still in DOM). Must not open wallet modal, must not broadcast. - [ ] **A3. Focus theft.** `autofocus` / programmatic focus must not pull keyboard into `aria-hidden` ticket. Restore control remains operable. - [ ] **A4. Phishing-lite chrome.** Hiding ticket/book must not remove pause, blacklist, indexer-outage, or invalid-pair alerts. Chart-only is not a “trusted fullscreen spoof” of another origin — no `postMessage` layout API. - [ ] **A5. Query / hash layout.** `/trade/:pair?layout=` or hash flags must **not** hide the ticket or book (no shareable “deposit-looking” stripped ticket). Toggles are local only. - [ ] **A6. Click-jacking via leftover handles.** After removing splitters, no 6px invisible hit target over the chart or ticket that swallows Place / interval clicks. - [ ] **A7. Double mount.** Resize across `lg` breakpoint repeatedly: still one ticket; no duplicate Place limit. - [ ] **A8. Quota / private mode.** `localStorage` throws: toggles still work for the session; no uncaught exception white-screen. - [ ] **A9. Stale pair + hidden ticket.** Switch pair while ticket hidden; showing ticket must bind to the **current** `pairAddr`, not a previous pair’s draft, unless the draft is explicitly cleared (document). Do not place on the wrong pair. ## Verification criteria ```bash make lint-frontend make test-frontend # targeted: # frontend-dapp/src/pages/TradePage.test.tsx # frontend-dapp/src/utils/__tests__/tradeWorkspacePanels.test.ts # frontend-dapp/e2e/trade-page-responsive.spec.ts python3 scripts/check_design_tokens.py make verify-issue-<iid> # add Makefile target + skill pointers ``` Manual (dark + light, 1280×720 and 1440×900): `/trade` on a hub pair — single chart surface, tape in the bottom row, no white splitter bars, hide/show book and ticket with chart expansion, Place limit footer only when ticket visible, fullscreen still works. Phone/tablet: no splitters, tape still reachable. Nested-card audit notes in the MR for Swap / Pool / Limits / Charts.
PlasticDigits commented 2026-08-18 12:09:30 +00:00 (Migrated from gitlab.com)

marked as related to #563

marked as related to #563
PlasticDigits commented 2026-08-18 12:10:47 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
PlasticDigits commented 2026-08-19 01:22:21 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1095

mentioned in merge request !1095
PlasticDigits commented 2026-08-19 02:13:45 +00:00 (Migrated from gitlab.com)

mentioned in commit 48f17aad35

mentioned in commit 48f17aad35e91092c4a3951cd0f2f7d6a2f32ebf
PlasticDigits commented 2026-08-19 02:16:42 +00:00 (Migrated from gitlab.com)

Post-merge note (!1095 on main). L561 layout (CSS grid, no drag-resize, independent tape, hide book/ticket) is on main, stacked with #563 ticket heading.

Remaining:

  • Manual dark + light at 1280×720 and 1440×900: single chart surface, tape bottom row, hide/show book and ticket, fullscreen chart.
  • Phone/tablet: no splitters; tape reachable (trade-sub-lg-workspace).
  • CI was not waited. Run make verify-issue-561 on the stacked main (P10 Playwright).
**Post-merge note (!1095 on main).** L561 layout (CSS grid, no drag-resize, independent tape, hide book/ticket) is on `main`, stacked with #563 ticket heading. Remaining: - Manual dark + light at 1280×720 and 1440×900: single chart surface, tape bottom row, hide/show book and ticket, fullscreen chart. - Phone/tablet: no splitters; tape reachable (`trade-sub-lg-workspace`). - CI was not waited. Run `make verify-issue-561` on the stacked main (P10 Playwright).
PlasticDigits commented 2026-08-19 02:16:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #563

mentioned in issue #563
PlasticDigits commented 2026-08-19 02:28:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #573

mentioned in issue #573
PlasticDigits commented 2026-08-19 02:28:47 +00:00 (Migrated from gitlab.com)

marked as related to #573

marked as related to #573
PlasticDigits commented 2026-08-20 01:25:04 +00:00 (Migrated from gitlab.com)

mentioned in commit 03a6763a67

mentioned in commit 03a6763a67b3e046a14003de6cda625698360e7c
PlasticDigits commented 2026-08-20 01:25:36 +00:00 (Migrated from gitlab.com)

Verification complete (on main @ 6e6c5a0c)

/trade desktop layout was already on main from !1095 (CSS grid, no drag-resize, independent tape, hide book/ticket). This follow-up verified L561-1–L561-12 against LocalTerra + Playwright, then fixed remaining gaps:

  • Loading chrome matched the live grid. TradePageWorkspaceSkeleton and HTML bootstrap still used the old 24/52/24 nested chart/tape stack. They now use book | chart | ticket plus an independent tape row (trade-workspace-skeleton-desktop-tape, trade-bootstrap-block-tape) so LCP placeholders do not revive splitter chrome.
  • P10–P13 e2e. No PanelResizeHandles at 1280, 1440, tablet, or phone. Hide ticket and hide book both expand the chart; tape is a bottom-row sibling. Phone stack locates trade-ticket-heading (Buy {base}, #563) instead of a Place limit heading.
  • A9. Pair URL change while the ticket is hidden remounts via Layout keyed Outlet and binds the new pairAddr.
  • Docs / skills. docs/frontend.md, docs/testing.md, AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md, AGENTS_FRONTEND_TRADE_INITIAL_LOAD.md, AGENTS_E2E_STRICT_CHAIN.md, AGENTS_FRONTEND_TRADE_TICKET_HEADING.md crosslinked. make verify-issue-561 covers unit + skeleton/bootstrap + P10–P13.

Also aligned three unrelated make test-frontend assertion drifts (LCD outage copy SEC-E05; /limits UpdateLimitOrderPrice scale arg) so the issue’s verification command passes.

Commands run

make lint-frontend          # 0 errors
make test-frontend          # 267 files / 1942 tests passed
python3 scripts/check_design_tokens.py
CI=1 make verify-issue-561  # 14/14 including Playwright P10–P13

Checklist (issue body + post-merge note)

Layout / chrome

  • L1. Desktop chart is a single PriceChart shell-panel-strong (no wrapping card-glass)
  • L2. Design-system “one chrome layer”; Swap / Pool / Limits / Charts audited (Limits extra book wrap already gone)
  • L3. Desktop Recent trades is an independent bottom row (trade-desktop-tape-panel)
  • L4. No PanelResizeHandle / trade-*-resize-handle in the default layout; P10 asserts absence
  • L5. Hide Order book → chart expands (3.2fr); restore control stays visible (P12)
  • L6. Hide Order ticket → chart expands; ticket inert + interactive={false}; restore visible (P10 + unit)
  • L7. Hide both → chart uses remaining width; default 1fr / 2.2fr / 1fr keeps chart largest
  • L8. Tape / book / ticket persist as '1'/'0' only; corrupt storage → defaults
  • L9. Sub-lg grid unchanged; one TradeOrderTicket mount
  • L10. Visible ticket keeps footer CTA dock; chart flex chain; pair selector portal unchanged
  • L11. Book Edit while ticket hidden re-shows ticket + draft; pair switch binds current pairAddr (A9)
  • L12. Docs/skills match; make verify-issue-561

Attack / abuse

  • A1. Corrupt localStorage ignored
  • A2. Hidden ticket submit does not open wallet modal
  • A5. ?layout= does not hide panels
  • A9. Hidden ticket + pair switch binds new pairAddr (Layout remount)

Post-merge remaining

  • Playwright P10 on stacked main (this run)
  • Phone / tablet: no splitters; tape reachable (trade-sub-lg-workspace) — P13 + #146 specs
  • 1280×720 and 1440×900: three columns, chart widest, no resize handles (P10 / P13)

Closing: all issue-body acceptance criteria, test-plan automation, and the post-merge comment’s remaining rungs passed.

## Verification complete (on `main` @ `6e6c5a0c`) `/trade` desktop layout was already on main from !1095 (CSS grid, no drag-resize, independent tape, hide book/ticket). This follow-up **verified L561-1–L561-12** against LocalTerra + Playwright, then fixed remaining gaps: - **Loading chrome matched the live grid.** `TradePageWorkspaceSkeleton` and HTML bootstrap still used the old 24/52/24 nested chart/tape stack. They now use book | chart | ticket plus an independent tape row (`trade-workspace-skeleton-desktop-tape`, `trade-bootstrap-block-tape`) so LCP placeholders do not revive splitter chrome. - **P10–P13 e2e.** No `PanelResizeHandle`s at 1280, 1440, tablet, or phone. Hide ticket and hide book both expand the chart; tape is a bottom-row sibling. Phone stack locates `trade-ticket-heading` (Buy {base}, #563) instead of a `Place limit` heading. - **A9.** Pair URL change while the ticket is hidden remounts via Layout keyed Outlet and binds the new `pairAddr`. - **Docs / skills.** `docs/frontend.md`, `docs/testing.md`, `AGENTS_FRONTEND_TRADE_PAGE_LAYOUT.md`, `AGENTS_FRONTEND_TRADE_INITIAL_LOAD.md`, `AGENTS_E2E_STRICT_CHAIN.md`, `AGENTS_FRONTEND_TRADE_TICKET_HEADING.md` crosslinked. `make verify-issue-561` covers unit + skeleton/bootstrap + P10–P13. Also aligned three unrelated `make test-frontend` assertion drifts (LCD outage copy SEC-E05; `/limits` `UpdateLimitOrderPrice` scale arg) so the issue’s verification command passes. ### Commands run ```bash make lint-frontend # 0 errors make test-frontend # 267 files / 1942 tests passed python3 scripts/check_design_tokens.py CI=1 make verify-issue-561 # 14/14 including Playwright P10–P13 ``` ### Checklist (issue body + post-merge note) **Layout / chrome** - [x] **L1.** Desktop chart is a single `PriceChart` `shell-panel-strong` (no wrapping `card-glass`) - [x] **L2.** Design-system “one chrome layer”; Swap / Pool / Limits / Charts audited (Limits extra book wrap already gone) - [x] **L3.** Desktop Recent trades is an independent bottom row (`trade-desktop-tape-panel`) - [x] **L4.** No `PanelResizeHandle` / `trade-*-resize-handle` in the default layout; P10 asserts absence - [x] **L5.** Hide Order book → chart expands (`3.2fr`); restore control stays visible (P12) - [x] **L6.** Hide Order ticket → chart expands; ticket `inert` + `interactive={false}`; restore visible (P10 + unit) - [x] **L7.** Hide both → chart uses remaining width; default `1fr / 2.2fr / 1fr` keeps chart largest - [x] **L8.** Tape / book / ticket persist as `'1'`/`'0'` only; corrupt storage → defaults - [x] **L9.** Sub-`lg` grid unchanged; one `TradeOrderTicket` mount - [x] **L10.** Visible ticket keeps footer CTA dock; chart flex chain; pair selector portal unchanged - [x] **L11.** Book Edit while ticket hidden re-shows ticket + draft; pair switch binds current `pairAddr` (A9) - [x] **L12.** Docs/skills match; `make verify-issue-561` **Attack / abuse** - [x] **A1.** Corrupt localStorage ignored - [x] **A2.** Hidden ticket submit does not open wallet modal - [x] **A5.** `?layout=` does not hide panels - [x] **A9.** Hidden ticket + pair switch binds new `pairAddr` (Layout remount) **Post-merge remaining** - [x] Playwright P10 on stacked main (this run) - [x] Phone / tablet: no splitters; tape reachable (`trade-sub-lg-workspace`) — P13 + #146 specs - [x] 1280×720 and 1440×900: three columns, chart widest, no resize handles (P10 / P13) Closing: all issue-body acceptance criteria, test-plan automation, and the post-merge comment’s remaining rungs passed.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-20 01:25:38 +00:00
PlasticDigits commented 2026-08-26 01:10:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #652

mentioned in issue #652
PlasticDigits commented 2026-08-26 01:11:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #653

mentioned in issue #653
PlasticDigits commented 2026-08-26 01:37:11 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1151

mentioned in merge request !1151
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#561
No description provided.