feat(frontend): Make UI sound effects optional (mute toggle + persist) #487

Closed
opened 2026-07-13 11:56:49 +00:00 by PlasticDigits · 9 comments
PlasticDigits commented 2026-07-13 11:56:49 +00:00 (Migrated from gitlab.com)

Summary

User feedback: click / UI sounds provide useful action registration for some users, but others find them annoying. Sounds are currently always on with no mute preference. Add an opt-out (default ON) so users can disable all UI SFX without removing the feature for everyone else.

Current codebase

UI audio is centralized in frontend-dapp/src/lib/sounds.ts:

  • Four WAV assets under frontend-dapp/public/sounds/ (button-press, hover, success, error)
  • Lazy singleton HTMLAudioElements; play() resets currentTime, sets volume (buttonPress 0.2, others 0.4), and swallows play errors
  • Public API: sounds.playButtonPress(), playHover(), playSuccess(), playError()

Playback is opt-in per call site (no global click listener / shared Button wrapper). Roughly:

Kind Typical usage
playButtonPress Nav, theme, modals, copy, chart/trade controls, amount presets (~70+ calls)
playHover Wallet connect option cards only (WalletModal.tsx)
playSuccess / playError Mutation / wallet-connect onSuccess / onError callbacks

There is no mute flag, localStorage key, env gate, React context, or Settings page for sounds. Closest global UX preference pattern is theme (cl8y-dex-theme) in Layout.tsx via ThemeSegmentedControl. Boolean prefs elsewhere use '1'/'0' helpers (e.g. utils/swapSettingsAdvanced.ts).

QA docs (QA_TEMPLATE.md §10.3) expect sounds on; recent passes sometimes SKIP manual sound checks. Component tests mock @/lib/sounds; there is no unit coverage of sounds.ts itself and no E2E for audio.

Why a new implementation is needed

  • Positive feedback: click sound confirms the action registered.
  • Negative feedback: at least one user finds repeated clicking annoying.
  • Without a toggle, the product cannot satisfy both groups; removing sounds entirely would regress the useful feedback path.
  • Centralizing the gate in play() avoids touching ~33 production import sites and keeps mute consistent for press, hover, success, and error.

Constraints / guardrails

  1. Default ON — preserve current behavior and existing QA expectations until the user opts out.
  2. Single gate — mute must be enforced inside sounds.ts play() (or equivalent), not duplicated at each call site.
  3. All SFX kinds — mute disables button press, hover, success, and error (one preference, not four).
  4. Persist across reloads — use localStorage with a cl8y-dex-* key and the existing boolean '1'/'0' helper pattern; SSR/window-guard safe.
  5. No secrets / no on-chain state — preference is client-local only.
  6. Do not introduce a global click-sound interceptor — keep explicit sounds.play*() call sites; silent buttons by omission stay silent.
  7. Toggle UX — place control near theme in Layout (desktop header + mobile More sheet); must be keyboard-accessible with clear aria state.
  8. Mute UX — when turning sounds off, do not play a press sound on that click; when turning on, optionally play one confirmation press (product choice — document in MR).
  9. Tests that mock sounds — keep working; do not force every component test to assert mute.
  10. Optional a11y stretch (same issue, not required for Done): also treat prefers-reduced-motion: reduce as muted unless the user explicitly re-enables sounds — if implemented, document precedence in the util.

Relevant files

Implement / extend

Path Role
frontend-dapp/src/lib/sounds.ts Central playback; add enabled gate
frontend-dapp/src/utils/soundPreferences.ts New — SOUNDS_ENABLED_STORAGE_KEY, readSoundsEnabled, writeSoundsEnabled
frontend-dapp/src/components/common/Layout.tsx Global toggle UI next to theme
frontend-dapp/src/components/common/ThemeSegmentedControl.tsx Visual/placement reference for adjacent control
QA_TEMPLATE.md §10.3 Add mute / unmute cases

Patterns to mirror

Path Role
frontend-dapp/src/utils/swapSettingsAdvanced.ts Boolean localStorage '1'/'0'
frontend-dapp/src/utils/expertMode.ts / stores/dex.ts Only if live cross-component React state is needed

Assets (unchanged unless replacing clips)

  • frontend-dapp/public/sounds/button-press.wav
  • frontend-dapp/public/sounds/hover.wav
  • frontend-dapp/public/sounds/success.wav
  • frontend-dapp/public/sounds/error.wav

Regression surface (call sites import @/lib/sounds)

Shell/common: AppShellNavLink.tsx, Layout.tsx, Modal.tsx, CopyButton.tsx, AmountBalanceActions.tsx, TradeOnboardingStrip.tsx
Wallet: WalletButton.tsx, WalletModal.tsx, WalletDropdownMenuItems.tsx
Trade/swap: TradeOrderTicket.tsx, TradeMarketOrderPanel.tsx, TradeWorkspaceDisclosure.tsx, SwapAdvancedSettings.tsx, ExpertModeModal.tsx, TraderPositionsTable.tsx
Charts/portfolio/legal + pages: chart overlay/menu, portfolio sections, RiskAcknowledgementModal.tsx, SwapPage, PoolPage, TradePage, LimitOrdersPage, ChartsPage, TraderPage, PortfolioPage, TiersPage, MintPage, CreatePairPage
Hooks: limit-order cancel / update price / expired claim mutations

  1. Add soundPreferences.ts with key cl8y-dex-sounds-enabled, default true when missing/invalid.
  2. Early-return in play() when !readSoundsEnabled().
  3. Add a compact Sound on/off control in Layout beside theme (desktop + mobile More).
  4. Unit-test preference read/write + play() no-op when muted (mock HTMLAudioElement or spy Audio.prototype.play).
  5. Update QA_TEMPLATE.md §10.3 with mute/unmute paths.
  6. Skip building a full Settings page or per-sound volume mixer in this issue.

Acceptance criteria

  • With default / no stored preference, all existing SFX behave as today (press, hover, success, error).
  • User can disable sound effects from the shell UI (desktop and mobile More).
  • Preference persists across full page reload.
  • While muted, no WAV playback occurs from any sounds.play*() path (nav, modals, wallet hover, mutation success/error).
  • User can re-enable sounds; subsequent actions play again without requiring a hard refresh (if UI is React state–backed) or after reload at minimum.
  • Toggle is reachable by keyboard and exposes pressed/checked state to assistive tech.
  • Unit tests cover preference util + mute gate in sounds.ts.
  • QA_TEMPLATE.md includes mute and unmute verification steps.
  • Existing unit tests that mock @/lib/sounds still pass.

Test plan (all paths)

Unit

  1. Missing key → readSoundsEnabled() === true.
  2. Stored '0' → false; '1' → true; garbage → treat as default true (or documented fallback).
  3. writeSoundsEnabled(false/true) round-trips via localStorage.
  4. With enabled=true, playButtonPress attempts audio.play (spy).
  5. With enabled=false, playButtonPress / playHover / playSuccess / playError do not call audio.play.
  6. Toggle write then immediate play* reflects new value.

Manual / QA (browser)

  1. Fresh profile / cleared localStorage: click nav, theme, modal close → press sounds play; open wallet modal and hover options → hover sound; complete a dry success path if available or use Simulated Wallet swap → success sound; force a failed tx if feasible → error sound.
  2. Turn Sound off → repeat the same interactions → silence.
  3. Reload page → remains off; interactions still silent.
  4. Turn Sound on → interactions audible again; reload → still on.
  5. Desktop header control and mobile More sheet control both work and stay in sync after reload (same storage key).
  6. Rapid clicks while unmuted still reset/currentTime=0 behavior without throwing.
  7. Confirm theme toggle still works when sounds are muted (no sound, theme still changes).

Automated regression

  • make test-frontend (or scoped vitest for new util + sounds tests).
  • Spot-check that mocked-sound component tests unchanged.

Test plan — attack, hack, and abuse vectors

Audio prefs are client-only, but still verify:

Vector Risk Expected handling
localStorage pollution / XSS writing cl8y-dex-sounds-enabled Attacker forces mute or unmute Preference is UX-only; no auth/funds impact. Invalid values must not throw or break the app; fall back to default ON.
QuotaExceeded / private-mode localStorage throws Write fails Prefer try/catch like other utils; UI should not crash; in-memory toggle for session is acceptable if write fails.
Rapid toggle spam Event loop / audio thrash Gate must remain correct; no unbounded Audio instance creation beyond the existing four singletons.
Forcing play via DevTools calling sounds.play* while muted Bypass UI Acceptable for client code; gate should still apply if callers use exported API. Do not expose a second ungated play path.
Autoplay policy / blocked audio.play() Promise rejection Continue swallowing errors; mute must not surface console-breaking unhandled rejections.
Malicious replacement of /sounds/*.wav via compromised static host Phishing audio Out of scope for this issue (deploy/CDN integrity); do not load remote URLs outside public/sounds/.
Cross-tab desync Tab A mutes, Tab B still plays until refresh Document acceptable MVP; optional storage event sync is nice-to-have, not required.
Social-engineering “disable sounds to hide error feedback” User misses audio error cue Toasts/UI errors remain primary; sounds are supplemental only — do not remove visual error feedback.

Verification criteria

Done when:

  1. MR implements default-ON mute with persistence and shell toggle as above.
  2. Unit tests for preference + gate are green in CI / make test-frontend.
  3. Manual checklist: muted silence for press, hover, success, and error; unmuted restores all four kinds; preference survives reload.
  4. QA template updated; no regression in theme control or wallet connect flows.
  5. Code review confirms no per-call-site mute forks and no new remote audio URLs.

Out of scope

  • Redesigning or replacing WAV assets / volumes
  • Per-sound volume sliders or separate mute for success vs click
  • Global automatic click sounding for every <button>
  • Backend / indexer / contract changes
## Summary User feedback: click / UI sounds provide useful action registration for some users, but others find them annoying. Sounds are currently always on with no mute preference. Add an opt-out (default ON) so users can disable all UI SFX without removing the feature for everyone else. ## Current codebase UI audio is centralized in `frontend-dapp/src/lib/sounds.ts`: - Four WAV assets under `frontend-dapp/public/sounds/` (`button-press`, `hover`, `success`, `error`) - Lazy singleton `HTMLAudioElement`s; `play()` resets `currentTime`, sets volume (`buttonPress` 0.2, others 0.4), and swallows play errors - Public API: `sounds.playButtonPress()`, `playHover()`, `playSuccess()`, `playError()` Playback is **opt-in per call site** (no global click listener / shared Button wrapper). Roughly: | Kind | Typical usage | |------|----------------| | `playButtonPress` | Nav, theme, modals, copy, chart/trade controls, amount presets (~70+ calls) | | `playHover` | Wallet connect option cards only (`WalletModal.tsx`) | | `playSuccess` / `playError` | Mutation / wallet-connect `onSuccess` / `onError` callbacks | There is **no** mute flag, localStorage key, env gate, React context, or Settings page for sounds. Closest global UX preference pattern is theme (`cl8y-dex-theme`) in `Layout.tsx` via `ThemeSegmentedControl`. Boolean prefs elsewhere use `'1'`/`'0'` helpers (e.g. `utils/swapSettingsAdvanced.ts`). QA docs (`QA_TEMPLATE.md` §10.3) expect sounds **on**; recent passes sometimes SKIP manual sound checks. Component tests mock `@/lib/sounds`; there is no unit coverage of `sounds.ts` itself and no E2E for audio. ## Why a new implementation is needed - Positive feedback: click sound confirms the action registered. - Negative feedback: at least one user finds repeated clicking annoying. - Without a toggle, the product cannot satisfy both groups; removing sounds entirely would regress the useful feedback path. - Centralizing the gate in `play()` avoids touching ~33 production import sites and keeps mute consistent for press, hover, success, and error. ## Constraints / guardrails 1. **Default ON** — preserve current behavior and existing QA expectations until the user opts out. 2. **Single gate** — mute must be enforced inside `sounds.ts` `play()` (or equivalent), not duplicated at each call site. 3. **All SFX kinds** — mute disables button press, hover, success, and error (one preference, not four). 4. **Persist across reloads** — use localStorage with a `cl8y-dex-*` key and the existing boolean `'1'`/`'0'` helper pattern; SSR/window-guard safe. 5. **No secrets / no on-chain state** — preference is client-local only. 6. **Do not introduce a global click-sound interceptor** — keep explicit `sounds.play*()` call sites; silent buttons by omission stay silent. 7. **Toggle UX** — place control near theme in `Layout` (desktop header + mobile More sheet); must be keyboard-accessible with clear `aria` state. 8. **Mute UX** — when turning sounds **off**, do not play a press sound on that click; when turning **on**, optionally play one confirmation press (product choice — document in MR). 9. **Tests that mock sounds** — keep working; do not force every component test to assert mute. 10. **Optional a11y stretch (same issue, not required for Done):** also treat `prefers-reduced-motion: reduce` as muted unless the user explicitly re-enables sounds — if implemented, document precedence in the util. ## Relevant files ### Implement / extend | Path | Role | |------|------| | `frontend-dapp/src/lib/sounds.ts` | Central playback; add enabled gate | | `frontend-dapp/src/utils/soundPreferences.ts` | **New** — `SOUNDS_ENABLED_STORAGE_KEY`, `readSoundsEnabled`, `writeSoundsEnabled` | | `frontend-dapp/src/components/common/Layout.tsx` | Global toggle UI next to theme | | `frontend-dapp/src/components/common/ThemeSegmentedControl.tsx` | Visual/placement reference for adjacent control | | `QA_TEMPLATE.md` §10.3 | Add mute / unmute cases | ### Patterns to mirror | Path | Role | |------|------| | `frontend-dapp/src/utils/swapSettingsAdvanced.ts` | Boolean localStorage `'1'`/`'0'` | | `frontend-dapp/src/utils/expertMode.ts` / `stores/dex.ts` | Only if live cross-component React state is needed | ### Assets (unchanged unless replacing clips) - `frontend-dapp/public/sounds/button-press.wav` - `frontend-dapp/public/sounds/hover.wav` - `frontend-dapp/public/sounds/success.wav` - `frontend-dapp/public/sounds/error.wav` ### Regression surface (call sites import `@/lib/sounds`) Shell/common: `AppShellNavLink.tsx`, `Layout.tsx`, `Modal.tsx`, `CopyButton.tsx`, `AmountBalanceActions.tsx`, `TradeOnboardingStrip.tsx` Wallet: `WalletButton.tsx`, `WalletModal.tsx`, `WalletDropdownMenuItems.tsx` Trade/swap: `TradeOrderTicket.tsx`, `TradeMarketOrderPanel.tsx`, `TradeWorkspaceDisclosure.tsx`, `SwapAdvancedSettings.tsx`, `ExpertModeModal.tsx`, `TraderPositionsTable.tsx` Charts/portfolio/legal + pages: chart overlay/menu, portfolio sections, `RiskAcknowledgementModal.tsx`, `SwapPage`, `PoolPage`, `TradePage`, `LimitOrdersPage`, `ChartsPage`, `TraderPage`, `PortfolioPage`, `TiersPage`, `MintPage`, `CreatePairPage` Hooks: limit-order cancel / update price / expired claim mutations ## Recommended direction 1. Add `soundPreferences.ts` with key `cl8y-dex-sounds-enabled`, default `true` when missing/invalid. 2. Early-return in `play()` when `!readSoundsEnabled()`. 3. Add a compact Sound on/off control in `Layout` beside theme (desktop + mobile More). 4. Unit-test preference read/write + `play()` no-op when muted (mock `HTMLAudioElement` or spy `Audio.prototype.play`). 5. Update `QA_TEMPLATE.md` §10.3 with mute/unmute paths. 6. Skip building a full Settings page or per-sound volume mixer in this issue. ## Acceptance criteria - [ ] With default / no stored preference, all existing SFX behave as today (press, hover, success, error). - [ ] User can disable sound effects from the shell UI (desktop and mobile More). - [ ] Preference persists across full page reload. - [ ] While muted, **no** WAV playback occurs from any `sounds.play*()` path (nav, modals, wallet hover, mutation success/error). - [ ] User can re-enable sounds; subsequent actions play again without requiring a hard refresh (if UI is React state–backed) or after reload at minimum. - [ ] Toggle is reachable by keyboard and exposes pressed/checked state to assistive tech. - [ ] Unit tests cover preference util + mute gate in `sounds.ts`. - [ ] `QA_TEMPLATE.md` includes mute and unmute verification steps. - [ ] Existing unit tests that mock `@/lib/sounds` still pass. ## Test plan (all paths) ### Unit 1. Missing key → `readSoundsEnabled() === true`. 2. Stored `'0'` → false; `'1'` → true; garbage → treat as default true (or documented fallback). 3. `writeSoundsEnabled(false/true)` round-trips via localStorage. 4. With enabled=true, `playButtonPress` attempts `audio.play` (spy). 5. With enabled=false, `playButtonPress` / `playHover` / `playSuccess` / `playError` do not call `audio.play`. 6. Toggle write then immediate `play*` reflects new value. ### Manual / QA (browser) 1. Fresh profile / cleared `localStorage`: click nav, theme, modal close → press sounds play; open wallet modal and hover options → hover sound; complete a dry success path if available or use Simulated Wallet swap → success sound; force a failed tx if feasible → error sound. 2. Turn Sound **off** → repeat the same interactions → **silence**. 3. Reload page → remains off; interactions still silent. 4. Turn Sound **on** → interactions audible again; reload → still on. 5. Desktop header control and mobile More sheet control both work and stay in sync after reload (same storage key). 6. Rapid clicks while unmuted still reset/`currentTime=0` behavior without throwing. 7. Confirm theme toggle still works when sounds are muted (no sound, theme still changes). ### Automated regression - `make test-frontend` (or scoped vitest for new util + sounds tests). - Spot-check that mocked-sound component tests unchanged. ## Test plan — attack, hack, and abuse vectors Audio prefs are client-only, but still verify: | Vector | Risk | Expected handling | |--------|------|-------------------| | localStorage pollution / XSS writing `cl8y-dex-sounds-enabled` | Attacker forces mute or unmute | Preference is UX-only; no auth/funds impact. Invalid values must not throw or break the app; fall back to default ON. | | QuotaExceeded / private-mode localStorage throws | Write fails | Prefer try/catch like other utils; UI should not crash; in-memory toggle for session is acceptable if write fails. | | Rapid toggle spam | Event loop / audio thrash | Gate must remain correct; no unbounded `Audio` instance creation beyond the existing four singletons. | | Forcing play via DevTools calling `sounds.play*` while muted | Bypass UI | Acceptable for client code; gate should still apply if callers use exported API. Do not expose a second ungated play path. | | Autoplay policy / blocked `audio.play()` | Promise rejection | Continue swallowing errors; mute must not surface console-breaking unhandled rejections. | | Malicious replacement of `/sounds/*.wav` via compromised static host | Phishing audio | Out of scope for this issue (deploy/CDN integrity); do not load remote URLs outside `public/sounds/`. | | Cross-tab desync | Tab A mutes, Tab B still plays until refresh | Document acceptable MVP; optional `storage` event sync is nice-to-have, not required. | | Social-engineering “disable sounds to hide error feedback” | User misses audio error cue | Toasts/UI errors remain primary; sounds are supplemental only — do not remove visual error feedback. | ## Verification criteria Done when: 1. MR implements default-ON mute with persistence and shell toggle as above. 2. Unit tests for preference + gate are green in CI / `make test-frontend`. 3. Manual checklist: muted silence for press, hover, success, and error; unmuted restores all four kinds; preference survives reload. 4. QA template updated; no regression in theme control or wallet connect flows. 5. Code review confirms no per-call-site mute forks and no new remote audio URLs. ## Out of scope - Redesigning or replacing WAV assets / volumes - Per-sound volume sliders or separate mute for success vs click - Global automatic click sounding for every `<button>` - Backend / indexer / contract changes
PlasticDigits commented 2026-07-14 10:54:10 +00:00 (Migrated from gitlab.com)

Manual UI verification (#487)

Verified locally with make dev (Vite @ 127.0.0.1:5173). Screenshots of the altered shell controls:

487-desktop-light-sound-on.png

487-desktop-light-sound-on

487-desktop-sound-muted.png

487-desktop-sound-muted

487-desktop-sound-on.png

487-desktop-sound-on

487-header-pref-group.png

487-header-pref-group

487-mobile-more-sound-off.png

487-mobile-more-sound-off

487-mobile-more-sound-on.png

487-mobile-more-sound-on

Checks observed:

  • Desktop header shows Sound next to theme; mute flips label to Muted and sets localStorage cl8y-dex-sounds-enabled=0.
  • Mobile More sheet shows Sound off / Sound on and stays in sync with the same storage key after reload/viewport change.
  • Light theme still works with the sound control present.
  • Unit tests: soundPreferences, sounds mute gate, SoundEffectsToggle — all green.
## Manual UI verification (#487) Verified locally with `make dev` (Vite @ 127.0.0.1:5173). Screenshots of the altered shell controls: ### `487-desktop-light-sound-on.png` ![487-desktop-light-sound-on](/uploads/ba65e028fde575c6c99c50fd4e7239f0/487-desktop-light-sound-on.png) ### `487-desktop-sound-muted.png` ![487-desktop-sound-muted](/uploads/55ede5385c06d79774c48949823b06ff/487-desktop-sound-muted.png) ### `487-desktop-sound-on.png` ![487-desktop-sound-on](/uploads/56b1e1baf1d5e3063886c78f2d327b54/487-desktop-sound-on.png) ### `487-header-pref-group.png` ![487-header-pref-group](/uploads/df877cc00f59e7e1f41e17cbad9ba113/487-header-pref-group.png) ### `487-mobile-more-sound-off.png` ![487-mobile-more-sound-off](/uploads/2a4eb13ba43a2723192cbfcc7a6c6083/487-mobile-more-sound-off.png) ### `487-mobile-more-sound-on.png` ![487-mobile-more-sound-on](/uploads/7047491fdd8b23c7f349411070376bd2/487-mobile-more-sound-on.png) Checks observed: - Desktop header shows **Sound** next to theme; mute flips label to **Muted** and sets `localStorage cl8y-dex-sounds-enabled=0`. - Mobile **More** sheet shows **Sound off** / **Sound on** and stays in sync with the same storage key after reload/viewport change. - Light theme still works with the sound control present. - Unit tests: `soundPreferences`, `sounds` mute gate, `SoundEffectsToggle` — all green.
PlasticDigits commented 2026-07-14 10:55:47 +00:00 (Migrated from gitlab.com)

mentioned in commit 6097243b51

mentioned in commit 6097243b51a06e8e4995f09a78084fb893275014
PlasticDigits commented 2026-07-14 10:55:58 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1027

mentioned in merge request !1027
PlasticDigits commented 2026-07-14 10:56:05 +00:00 (Migrated from gitlab.com)

Implementation status (MR !1027)

Branch feat/487-sound-mute-toggle — https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/309

Acceptance criteria

  • Default / no stored preference → SFX behave as today
  • Disable from shell UI (desktop header + mobile More)
  • Preference persists across full page reload (cl8y-dex-sounds-enabled)
  • While muted, no WAV from any sounds.play*() path (central gate in play())
  • Re-enable without hard refresh (React state + session cache); confirmation press on unmute
  • Keyboard-reachable; aria-pressed = sounds enabled
  • Unit tests for preference util + mute gate + toggle component
  • QA_TEMPLATE.md §10.3 mute/unmute cases
  • Existing mocked-@/lib/sounds tests unaffected (no call-site changes)

Not done / out of scope (per issue)

  • Optional prefers-reduced-motion: reduce auto-mute stretch
  • Cross-tab storage event sync (documented as MVP acceptable)
  • Per-sound volume mixer / asset redesign / global click interceptor
  • Full success/error audio QA on LocalTerra swap paths in this session (LCD/mainnet env; mute gate covered by unit tests + shell UI verification)

Screenshots of altered shell controls are in the prior note on this issue.

## Implementation status (MR !1027) Branch `feat/487-sound-mute-toggle` — https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/309 ### Acceptance criteria - [x] Default / no stored preference → SFX behave as today - [x] Disable from shell UI (desktop header + mobile More) - [x] Preference persists across full page reload (`cl8y-dex-sounds-enabled`) - [x] While muted, no WAV from any `sounds.play*()` path (central gate in `play()`) - [x] Re-enable without hard refresh (React state + session cache); confirmation press on unmute - [x] Keyboard-reachable; `aria-pressed` = sounds enabled - [x] Unit tests for preference util + mute gate + toggle component - [x] `QA_TEMPLATE.md` §10.3 mute/unmute cases - [x] Existing mocked-`@/lib/sounds` tests unaffected (no call-site changes) ### Not done / out of scope (per issue) - [ ] Optional `prefers-reduced-motion: reduce` auto-mute stretch - [ ] Cross-tab `storage` event sync (documented as MVP acceptable) - [ ] Per-sound volume mixer / asset redesign / global click interceptor - [ ] Full success/error audio QA on LocalTerra swap paths in this session (LCD/mainnet env; mute gate covered by unit tests + shell UI verification) Screenshots of altered shell controls are in the prior note on this issue.
PlasticDigits commented 2026-07-14 10:58:49 +00:00 (Migrated from gitlab.com)

mentioned in commit 8491a798a9

mentioned in commit 8491a798a9dcdae72e0383b1fa44441a50cd81c3
PlasticDigits commented 2026-07-14 10:58:56 +00:00 (Migrated from gitlab.com)

Icon chrome update (MR !1027)

Replaced Dark / Light / Sound text buttons with compact moon / sun / speaker flat icons.

487-icons-header-light.png

487-icons-header-light

487-icons-header-dark.png

487-icons-header-dark

487-icons-header-muted.png

487-icons-header-muted

487-icons-mobile-more.png

487-icons-mobile-more

## Icon chrome update (MR !1027) Replaced Dark / Light / Sound text buttons with compact moon / sun / speaker flat icons. ### `487-icons-header-light.png` ![487-icons-header-light](/uploads/c9cefee1b36dfb06dabde2648c1c60d4/487-icons-header-light.png) ### `487-icons-header-dark.png` ![487-icons-header-dark](/uploads/44de0f58baf6568294a136159dc3907d/487-icons-header-dark.png) ### `487-icons-header-muted.png` ![487-icons-header-muted](/uploads/7732bdecfe5a6708650cc11020360830/487-icons-header-muted.png) ### `487-icons-mobile-more.png` ![487-icons-mobile-more](/uploads/4ff1d746ee478f89212fb862d9aa2808/487-icons-mobile-more.png)
PlasticDigits commented 2026-07-14 11:04:04 +00:00 (Migrated from gitlab.com)

mentioned in commit 481355d500

mentioned in commit 481355d5008f29de6dc8c94d858097ba55a439d6
PlasticDigits commented 2026-07-14 11:04:20 +00:00 (Migrated from gitlab.com)

Moved EnvironmentRibbon into the footer on all breakpoints and tightened sticky header vertical spacing.

487-footer-ribbon-desktop

487-footer-ribbon-mobile

## Footer ribbon + denser header (MR !1027) Moved `EnvironmentRibbon` into the footer on all breakpoints and tightened sticky header vertical spacing. ### `487-footer-ribbon-desktop.png` ![487-footer-ribbon-desktop](/uploads/b9dd7755ca994b55d52327dc0e2a5293/487-footer-ribbon-desktop.png) ### `487-footer-ribbon-mobile.png` ![487-footer-ribbon-mobile](/uploads/22bb0ff3e4e419dbbd698a1e8b75a5e3/487-footer-ribbon-mobile.png)
PlasticDigits commented 2026-07-14 11:08:39 +00:00 (Migrated from gitlab.com)

mentioned in commit d226b04bee

mentioned in commit d226b04beeac76783c8cf3ef0892d6497aab3e84
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-07-14 11:08:39 +00:00
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#487
No description provided.