fix(frontend): Sticky header + environment ribbon overlap and text bleed on desktop Chrome #482

Closed
opened 2026-07-12 12:11:05 +00:00 by PlasticDigits · 12 comments
PlasticDigits commented 2026-07-12 12:11:05 +00:00 (Migrated from gitlab.com)

Summary

MacBook Chrome report: the sticky environment ribbon (second bar) visually overlaps the primary header card, and page copy (especially Trade’s subtitle) collides with or bleeds through the ribbon so network status text becomes unreadable.

Reporter screenshots (mainnet build):

  1. Ribbon card sits into the header’s bottom edge; ribbon label/detail sit cramped against the ribbon’s top border.
  2. On /trade, longer page subtitle (Order book, chart, tape…) appears mashed into the same horizontal band as MAINNET + Terra Classic · columbus-5 · real assets.

Current codebase

App chrome is a two-tier sticky stack in one wrapper (not fixed positioning):

  • frontend-dapp/src/components/common/Layout.tsx — .app-top-sticky wraps header.app-header-shell + <EnvironmentRibbon />.
  • frontend-dapp/src/components/legal/EnvironmentRibbon.tsx — static build-time network strip (MAINNET / TESTNET / LOCAL + detail). No page-specific content is injected into the ribbon.
  • frontend-dapp/src/index.css:
    • .app-top-sticky — position: sticky; top: 0; z-index: 40
    • .app-header — heavy downward box-shadow, backdrop-filter, ~margin: 12px auto 0
    • .app-env-ribbon — margin: 6px auto 0, padding: 8px 14px, semi-transparent mainnet/testnet/local backgrounds (rgba(…, 0.08–0.1))
    • .app-main-shell — fixed padding-top (~44–54px), not derived from measured sticky height
  • Trade page subtitle lives in frontend-dapp/src/pages/TradePage.tsx (H1 + long text-sm line with --ink-dim), i.e. main content that scrolls under the sticky stack (z-index: 1 vs sticky 40).

Prior related work (closed): #136 (header density / fold), #138 (environment strip), #170 (theme in sticky header). Existing E2E in frontend-dapp/e2e/navigation.spec.ts covers nav-item overlap and sticky presence, but not header↔ribbon seam gap, ribbon opacity under scroll, or mainnet ribbon copy length (playwright.config.ts defaults to VITE_NETWORK=local).

Likely causes (ranked):

  1. Translucent sticky ribbon + page content scrolling underneath → text “collision” reading as one broken second bar.
  2. Tight 6px ribbon margin-top vs large header shadow → seam looks overlapped.
  3. Insufficient ribbon vertical padding / line-height for uppercase label + detail at macOS Chrome font metrics / zoom.
  4. macOS Chrome amplifiers: -webkit-font-smoothing, backdrop-filter, scrollbar-gutter: stable, subpixel rounding (worsen 1–3; not a separate root cause).

Why a new implementation is needed

Production mainnet builds show a longer ribbon detail string and reddish translucent panel. The current spacing and opacity make the shell look broken on real MacBook Chrome viewports (~1280–1440, 100–110% zoom), undermine the #138 goal (clear network risk context), and can make users misread Trade chrome as part of the legal/environment strip. Local-only E2E did not catch this.

Constraints / guardrails

  • Keep persistent environment context under the header (#138). Do not remove the ribbon or bury network risk copy behind a menu.
  • Do not regress #136 tablet compact nav fold or #170 theme placement in the sticky header.
  • Prefer CSS/layout fixes in the shell; avoid page-by-page special cases unless Trade subtitle length is also clarified for hierarchy.
  • Ribbon copy remains build-time from DEFAULT_NETWORK / NETWORKS — no user-controlled HTML in the strip.
  • Preserve dark/light theme tokens; do not introduce opaque panels that break light theme contrast.
  • Match existing shell visual language (rounded cards, tokens in index.css / theme CSS) — no unrelated redesign.

Relevant files

Path Role
frontend-dapp/src/components/common/Layout.tsx Sticky stack composition
frontend-dapp/src/components/legal/EnvironmentRibbon.tsx Second bar markup + mainnet/testnet/local copy
frontend-dapp/src/index.css .app-top-sticky, .app-header, .app-env-ribbon*, .app-main-shell
frontend-dapp/src/pages/TradePage.tsx Long page subtitle that bleeds under sticky chrome
frontend-dapp/src/utils/networkDisplay.ts / constants.ts Network labels / chainId
frontend-dapp/e2e/navigation.spec.ts Existing shell E2E (extend)
skills/AGENTS_FRONTEND_RISK_DISCLAIMERS.md Environment strip playbook
docs/frontend.md (responsive shell section) Docs drift risk
  1. Increase header↔ribbon vertical separation (margin-top / header bottom margin) and/or reduce/clip header bottom shadow so cards do not visually eat each other.
  2. Solidify ribbon background (higher alpha or var(--panel-bg) + tint) so scrolled page text cannot show through.
  3. Increase ribbon vertical padding / line-height so label+detail are not flush to the top edge (single- and multi-line wrap).
  4. Optionally introduce --app-sticky-stack-height (or scroll-padding-top) so main content clearance tracks real sticky height; verify Trade H1 has clear separation at scrollY=0.
  5. Optionally shorten or wrap Trade subtitle for hierarchy (distinct from ribbon), without removing operational copy users need.
  6. Add Playwright bounding-box assertions for seam gap and scroll-underlap; run at least one path with mainnet network env (or fixture CSS class) so long ribbon copy is covered.

Acceptance criteria

  • Clear visual gap between header card bottom border and ribbon top at 100% and 110% zoom (desktop Chrome; dark + light).
  • Ribbon label/detail have comfortable inner top/bottom padding (≥ ~8px perceived) when wrapped and unwrapped.
  • At scrollY=0 on /trade, ≥ ~16px visual separation between ribbon bottom and Trade H1; after small scroll, Trade subtitle is not legible through/over ribbon text.
  • Mainnet ribbon (Mainnet + Terra Classic · columbus-5 · real assets) has no internal label/detail overlap at 1280px width.
  • Local and testnet ribbon variants remain readable and visually distinct.
  • #136 / #170 / #138 behaviors still hold (nav fold, theme in header, ribbon visible).

Test plan (all paths)

Path How
Mainnet dark, scroll 0 Preview/VITE_NETWORK=mainnet build; Mac or CI Chrome; /, /trade, /limits
Mainnet dark, scroll ~40–80px Confirm no text bleed through ribbon
Mainnet light theme Same seam + opacity checks
Testnet + local builds Ribbon tone/padding still correct
Zoom 90% / 100% / 110% Seam and padding hold
Tablet compact (768–1119) Ribbon still under header; no new overflow
Mobile bottom nav Sticky top stack unchanged; no double-padding regressions
Regression frontend-dapp/e2e/navigation.spec.ts + new sticky-shell describe

Test plan (attack / hack / abuse)

Vector Expectation
XSS / HTML injection via ribbon strings N/A for user input today — copy is build-time. Guardrail: keep React text nodes; no dangerouslySetInnerHTML; if copy ever becomes remote, sanitize/escape.
CSS injection via network config at build Treat malicious VITE_* as supply-chain; CI should not render untrusted CSS from env into style= attributes.
Theme localStorage tampering (cl8y-dex-theme) Only toggles data-theme; must not inject markup or break sticky opacity fix.
Extremely long forced detail string (dev fixture) Ribbon wraps without overlapping header; layout does not escape shell / cover wallet menus incorrectly.
Overlay clickjacking via translucent sticky Opaque enough ribbon must not create a click-through hole onto page controls under the strip.

Verification criteria

  • Reproduced on MacBook Chrome (or equivalent viewport + mainnet build) before/after.
  • Distinguishes scroll-0 spacing bug vs scroll-underlap (both fixed if both present).
  • New E2E asserts header bottom vs ribbon top gap and ribbon vs Trade H1; at least one mainnet (or long-copy) case.
  • Manual sign-off: dark + light, 100% + 110% zoom, /trade and a shorter page (e.g. Swap).
  • Docs/skills updated only if sticky-height or ribbon opacity rules become conventions agents should follow.
## Summary MacBook Chrome report: the sticky **environment ribbon** (second bar) visually overlaps the primary header card, and page copy (especially Trade’s subtitle) collides with or bleeds through the ribbon so network status text becomes unreadable. Reporter screenshots (mainnet build): 1. Ribbon card sits into the header’s bottom edge; ribbon label/detail sit cramped against the ribbon’s top border. 2. On `/trade`, longer page subtitle (`Order book, chart, tape…`) appears mashed into the same horizontal band as `MAINNET` + `Terra Classic · columbus-5 · real assets`. ## Current codebase App chrome is a **two-tier sticky stack** in one wrapper (not fixed positioning): - `frontend-dapp/src/components/common/Layout.tsx` — `.app-top-sticky` wraps `header.app-header-shell` + `<EnvironmentRibbon />`. - `frontend-dapp/src/components/legal/EnvironmentRibbon.tsx` — static build-time network strip (`MAINNET` / `TESTNET` / `LOCAL` + detail). **No page-specific content is injected into the ribbon.** - `frontend-dapp/src/index.css`: - `.app-top-sticky` — `position: sticky; top: 0; z-index: 40` - `.app-header` — heavy downward `box-shadow`, `backdrop-filter`, ~`margin: 12px auto 0` - `.app-env-ribbon` — `margin: 6px auto 0`, `padding: 8px 14px`, **semi-transparent** mainnet/testnet/local backgrounds (`rgba(…, 0.08–0.1)`) - `.app-main-shell` — fixed `padding-top` (~44–54px), not derived from measured sticky height - Trade page subtitle lives in `frontend-dapp/src/pages/TradePage.tsx` (H1 + long `text-sm` line with `--ink-dim`), i.e. **main content that scrolls under** the sticky stack (`z-index: 1` vs sticky `40`). Prior related work (closed): #136 (header density / fold), #138 (environment strip), #170 (theme in sticky header). Existing E2E in `frontend-dapp/e2e/navigation.spec.ts` covers nav-item overlap and sticky presence, but **not** header↔ribbon seam gap, ribbon opacity under scroll, or mainnet ribbon copy length (`playwright.config.ts` defaults to `VITE_NETWORK=local`). Likely causes (ranked): 1. Translucent sticky ribbon + page content scrolling underneath → text “collision” reading as one broken second bar. 2. Tight `6px` ribbon `margin-top` vs large header shadow → seam looks overlapped. 3. Insufficient ribbon vertical padding / line-height for uppercase label + detail at macOS Chrome font metrics / zoom. 4. macOS Chrome amplifiers: `-webkit-font-smoothing`, `backdrop-filter`, `scrollbar-gutter: stable`, subpixel rounding (worsen 1–3; not a separate root cause). ## Why a new implementation is needed Production mainnet builds show a longer ribbon detail string and reddish translucent panel. The current spacing and opacity make the shell look broken on real MacBook Chrome viewports (~1280–1440, 100–110% zoom), undermine the #138 goal (clear network risk context), and can make users misread Trade chrome as part of the legal/environment strip. Local-only E2E did not catch this. ## Constraints / guardrails - Keep persistent environment context under the header (#138). Do not remove the ribbon or bury network risk copy behind a menu. - Do not regress #136 tablet compact nav fold or #170 theme placement in the sticky header. - Prefer CSS/layout fixes in the shell; avoid page-by-page special cases unless Trade subtitle length is also clarified for hierarchy. - Ribbon copy remains **build-time** from `DEFAULT_NETWORK` / `NETWORKS` — no user-controlled HTML in the strip. - Preserve dark/light theme tokens; do not introduce opaque panels that break light theme contrast. - Match existing shell visual language (rounded cards, tokens in `index.css` / theme CSS) — no unrelated redesign. ## Relevant files | Path | Role | |------|------| | `frontend-dapp/src/components/common/Layout.tsx` | Sticky stack composition | | `frontend-dapp/src/components/legal/EnvironmentRibbon.tsx` | Second bar markup + mainnet/testnet/local copy | | `frontend-dapp/src/index.css` | `.app-top-sticky`, `.app-header`, `.app-env-ribbon*`, `.app-main-shell` | | `frontend-dapp/src/pages/TradePage.tsx` | Long page subtitle that bleeds under sticky chrome | | `frontend-dapp/src/utils/networkDisplay.ts` / `constants.ts` | Network labels / chainId | | `frontend-dapp/e2e/navigation.spec.ts` | Existing shell E2E (extend) | | `skills/AGENTS_FRONTEND_RISK_DISCLAIMERS.md` | Environment strip playbook | | `docs/frontend.md` (responsive shell section) | Docs drift risk | ## Recommended direction 1. Increase header↔ribbon vertical separation (`margin-top` / header bottom margin) and/or reduce/clip header bottom shadow so cards do not visually eat each other. 2. Solidify ribbon background (higher alpha or `var(--panel-bg)` + tint) so scrolled page text cannot show through. 3. Increase ribbon vertical padding / line-height so label+detail are not flush to the top edge (single- and multi-line wrap). 4. Optionally introduce `--app-sticky-stack-height` (or `scroll-padding-top`) so main content clearance tracks real sticky height; verify Trade H1 has clear separation at `scrollY=0`. 5. Optionally shorten or wrap Trade subtitle for hierarchy (distinct from ribbon), without removing operational copy users need. 6. Add Playwright bounding-box assertions for seam gap and scroll-underlap; run at least one path with **mainnet** network env (or fixture CSS class) so long ribbon copy is covered. ## Acceptance criteria - [ ] Clear visual gap between header card bottom border and ribbon top at 100% and 110% zoom (desktop Chrome; dark + light). - [ ] Ribbon label/detail have comfortable inner top/bottom padding (≥ ~8px perceived) when wrapped and unwrapped. - [ ] At `scrollY=0` on `/trade`, ≥ ~16px visual separation between ribbon bottom and Trade H1; after small scroll, Trade subtitle is **not** legible through/over ribbon text. - [ ] Mainnet ribbon (`Mainnet` + `Terra Classic · columbus-5 · real assets`) has no internal label/detail overlap at 1280px width. - [ ] Local and testnet ribbon variants remain readable and visually distinct. - [ ] #136 / #170 / #138 behaviors still hold (nav fold, theme in header, ribbon visible). ## Test plan (all paths) | Path | How | |------|-----| | Mainnet dark, scroll 0 | Preview/`VITE_NETWORK=mainnet` build; Mac or CI Chrome; `/`, `/trade`, `/limits` | | Mainnet dark, scroll ~40–80px | Confirm no text bleed through ribbon | | Mainnet light theme | Same seam + opacity checks | | Testnet + local builds | Ribbon tone/padding still correct | | Zoom 90% / 100% / 110% | Seam and padding hold | | Tablet compact (768–1119) | Ribbon still under header; no new overflow | | Mobile bottom nav | Sticky top stack unchanged; no double-padding regressions | | Regression | `frontend-dapp/e2e/navigation.spec.ts` + new sticky-shell describe | ## Test plan (attack / hack / abuse) | Vector | Expectation | |--------|-------------| | XSS / HTML injection via ribbon strings | N/A for user input today — copy is build-time. Guardrail: keep React text nodes; no `dangerouslySetInnerHTML`; if copy ever becomes remote, sanitize/escape. | | CSS injection via network config at build | Treat malicious `VITE_*` as supply-chain; CI should not render untrusted CSS from env into `style=` attributes. | | Theme `localStorage` tampering (`cl8y-dex-theme`) | Only toggles `data-theme`; must not inject markup or break sticky opacity fix. | | Extremely long forced detail string (dev fixture) | Ribbon wraps without overlapping header; layout does not escape shell / cover wallet menus incorrectly. | | Overlay clickjacking via translucent sticky | Opaque enough ribbon must not create a click-through hole onto page controls under the strip. | ## Verification criteria - [ ] Reproduced on MacBook Chrome (or equivalent viewport + mainnet build) before/after. - [ ] Distinguishes scroll-0 spacing bug vs scroll-underlap (both fixed if both present). - [ ] New E2E asserts header bottom vs ribbon top gap and ribbon vs Trade H1; at least one mainnet (or long-copy) case. - [ ] Manual sign-off: dark + light, 100% + 110% zoom, `/trade` and a shorter page (e.g. Swap). - [ ] Docs/skills updated only if sticky-height or ribbon opacity rules become conventions agents should follow.
PlasticDigits commented 2026-07-12 12:11:07 +00:00 (Migrated from gitlab.com)

marked as related to #136

marked as related to #136
PlasticDigits commented 2026-07-12 12:11:08 +00:00 (Migrated from gitlab.com)

marked as related to #138

marked as related to #138
PlasticDigits commented 2026-07-12 12:11:08 +00:00 (Migrated from gitlab.com)

marked as related to #170

marked as related to #170
PlasticDigits commented 2026-07-12 12:11:16 +00:00 (Migrated from gitlab.com)

marked as related to #483

marked as related to #483
PlasticDigits commented 2026-07-13 01:20:31 +00:00 (Migrated from gitlab.com)

mentioned in commit b598fb59a8

mentioned in commit b598fb59a8be79d47638054f7a720434263be4de
PlasticDigits commented 2026-07-13 01:20:32 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1019

mentioned in merge request !1019
PlasticDigits commented 2026-07-13 01:55:41 +00:00 (Migrated from gitlab.com)

mentioned in commit d62f7c78e9

mentioned in commit d62f7c78e9c921bb2beed1ef26e51c2c146e35df
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-07-13 01:55:41 +00:00
PlasticDigits commented 2026-07-13 11:54:32 +00:00 (Migrated from gitlab.com)

mentioned in issue #486

mentioned in issue #486
PlasticDigits commented 2026-07-13 11:54:32 +00:00 (Migrated from gitlab.com)

marked as related to #486

marked as related to #486
PlasticDigits commented 2026-07-13 12:07:56 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1022

mentioned in merge request !1022
leonardocolucci commented 2026-07-18 15:32:30 +00:00 (Migrated from gitlab.com)

mentioned in issue #500

mentioned in issue #500
PlasticDigits commented 2026-08-15 13:18:35 +00:00 (Migrated from gitlab.com)

mentioned in issue #527

mentioned in issue #527
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#482
No description provided.