Skip to content

Labs switches, sidebar cleanup: Switch visibility in dark mode, clickable contact email, disable Team sync toggle, remove database switcher, hide Favorites/Conflicts #2813

Closed

Description

@malibio

Three related fixes to the Labs settings switches: make the unchecked Switch visible in dark mode, make the Team synchronization contact email clickable, and disable the Team synchronization toggle until the capability is ready (visible but not interactive).


1. Unchecked Switch is invisible in dark mode

In dark mode an unchecked Switch is effectively invisible: a near-black thumb on a 15%-opacity white track, sitting on a near-black card. The Labs page reads as two description cards with no toggles at all. Reported directly as "is there an actual toggle? I didn't see it" while looking straight at the page.

Cause

packages/desktop-app/src/lib/components/ui/switch/switch.svelte:

  • track: data-[state=unchecked]:bg-input
  • thumb: bg-background
  • border: border-2 border-transparent

Dark-mode tokens (app.css):

--input:      0 0% 100% / 0.15   /* white @ 15% opacity */
--background: 0 0% 9%            /* near-black */

A barely-visible track and a near-black thumb against a near-black card. With border-transparent and a deliberate no-focus-ring exception (documented in the component), nothing remains to make the control perceivable.

Light mode is unaffected (--input: 240 6% 90%, white thumb, white card). The checked state is fine in both themes — it uses bg-primary.

Scope

bg-input is also used by input.svelte and textarea.svelte, but there it is a fill behind text, where the text and field border carry visibility. switch.svelte is the only component using it as the sole indicator of state, which is why only the switch disappears. Every Switch instance in the app is affected in dark mode, not just Labs.


2. Make the Team synchronization contact email clickable

The Team synchronization card ends with "contact us at developer@nodespace.ai" as plain text. It should be an actionable mailto: link, styled consistently with other links in settings.

Note the project standard prohibits {@html}, so the link must be real markup in the template, not an interpolated HTML string.


3. Disable the Team synchronization toggle

Team synchronization is not ready to be switched on. The toggle should remain visible (so the capability is discoverable and its status legible) but be non-interactive.

switch.svelte already supports disabled:cursor-not-allowed disabled:opacity-50, so this is passing disabled through from the Labs card.

Ordering constraint: disabled:opacity-50 applied to an already-invisible dark-mode switch would make it less visible still. Fix (1) must land before or with (3), and the disabled state must be verified in dark mode specifically.

The AI Chat toggle stays fully interactive — this applies only to Team synchronization.


Acceptance criteria

Switch visibility

  • An unchecked switch is clearly visible in dark mode: the track reads as a distinct control against the card, and the thumb is distinguishable from the track
  • The checked state remains visually distinct from unchecked in both themes
  • Light mode appearance is unchanged (or deliberately improved, not regressed)
  • Unchecked track meets the non-text contrast requirement (WCAG 1.4.11, 3:1) against its typical surface
  • Fixed at the component or token level so every Switch benefits, not patched per usage site
  • If --input's dark value is changed rather than the component, input.svelte and textarea.svelte are re-checked for regressions

Team sync email

  • developer@nodespace.ai renders as a working mailto: link
  • Implemented as real markup, not {@html}
  • Link styling is consistent with other settings links and legible in both themes

Team sync toggle disabled

  • The Team synchronization switch is visible but cannot be toggled by click or keyboard
  • Its disabled state is visually evident in BOTH themes — explicitly re-checked in dark mode given the opacity-50 interaction with fix (1)
  • The control is not focus-reachable as an actionable element, or is marked so assistive tech reports it as disabled
  • labsFlags.syncEnabled cannot be flipped from this UI while disabled
  • The AI Chat toggle remains fully interactive

Verification

  • All three verified visually in the running app in BOTH light and dark themes

Notes

Found by running the real app in dark mode. This is the exact failure class the run-app guidance calls out — verifying only one theme misses a token that resolves to near-invisible in the other. The component's own comment justifies its WCAG 2.4.7 focus-indicator exception on the grounds that "the track's fill is already how on/off is shown"; that reasoning depends on the track fill being visible, which in dark mode it is not.


4. Remove the sidebar database switcher

packages/desktop-app/src/lib/components/layout/database-switcher.svelte, rendered at navigation-sidebar.svelte:313, shows the active database name with a gear icon. It is redundant: its own comment states that switching and management "now live in Settings → Database", so the control is a read-only indicator whose only action is openSettings('database') — which the sidebar's own Settings nav item at the bottom already does.

Remove the component and its usage.

The one behavior to preserve: the switcher renders a red TriangleAlert glyph when activeDatabase.status === 'missing'. That is a genuine error signal and the only thing the control does that Settings does not. Rather than dropping it, propagate it to the Settings nav item — see item 6.

5. Hide the "Favorites" and "Conflicts" sidebar nav items

Both are not ready and should not be offered yet. Defined in packages/desktop-app/src/lib/stores/layout.svelte.ts (id: 'favorites' and id: 'conflicts').

navigation-sidebar.svelte:265 already acknowledges "Favorites is not implemented yet" while still rendering the item, so it currently opens a non-functional view.

Follow the precedent already set in that same file for the AI Chat nav item, which was removed with a comment explaining why and when it returns — "omitted rather than left inert". Omit both items the same way rather than leaving them present and inert, and leave a comment recording that they are hidden pending readiness.

Conflicts is backed by real functionality (openConflicts(), the ADR-068 conflict journal); hiding the nav entry should not delete that machinery, only the sidebar entry point.

Acceptance criteria (items 4 and 5)

  • The database switcher no longer renders in the sidebar, and database-switcher.svelte is deleted rather than left unused
  • The missing database state is surfaced on the Settings nav item instead (item 6)
  • "Favorites" and "Conflicts" no longer appear in the sidebar
  • Each omission carries a comment explaining why it is hidden and what would bring it back, matching the existing AI Chat precedent
  • openConflicts() and the underlying conflict-journal functionality are left intact
  • Settings remains reachable from the sidebar
  • Any tests asserting the presence of the removed items are updated

6. Propagate the missing-database alert to the Settings nav item

With the sidebar database switcher removed (item 4), the red TriangleAlert it showed for activeDatabase.status === 'missing' needs a new home. Surface it as an alert badge on the sidebar's Settings nav item, which is where the user must go to resolve the problem (Settings → Database).

databaseStore.activeDatabase.status is "closed" | "open" | "missing" | "unknown"; the badge shows for missing.

Design constraints

  • Nav items currently render from one generic {#each navItems.slice(1)} loop in navigation-sidebar.svelte (icon + optional label), with no badge affordance today — this introduces the first one. Prefer a reusable badge concept over a Settings-only special case, since Conflicts and others are likely to want the same treatment later.
  • The sidebar collapses (isCollapsed hides .nav-label), so the badge must remain visible and comprehensible in the collapsed rail where there is no text.
  • The badge is an error signal: it needs an accessible name, not colour alone (WCAG 1.4.1). A screen-reader user must learn that a database is missing, not merely that "Settings" has a decoration.

Acceptance criteria

  • When the active database's status is missing, the Settings nav item shows an alert badge
  • The badge clears when the status is no longer missing
  • The badge is visible and legible in both the expanded and collapsed sidebar
  • The badge is legible in both light and dark themes
  • The alert is conveyed accessibly (accessible name / aria-label or equivalent), not by colour alone
  • The badge mechanism is reusable by other nav items rather than hardcoded for Settings
  • Clicking Settings with the badge active still opens Settings as normal

Activity

  1. changed the title [-]Unchecked Switch is invisible in dark mode: near-black thumb on a 15%-opacity track over a near-black card[/-] [+]Labs switches: unchecked Switch invisible in dark mode, clickable contact email, and disable the Team synchronization toggle[/+] on Sep 22, 2026
  2. changed the title [-]Labs switches: unchecked Switch invisible in dark mode, clickable contact email, and disable the Team synchronization toggle[/-] [+]Labs switches, sidebar cleanup: Switch visibility in dark mode, clickable contact email, disable Team sync toggle, remove database switcher, hide Favorites/Conflicts[/+] on Sep 22, 2026
  3. malibio commented on Sep 25, 2026

    @malibio
    CollaboratorAuthor

    Frontend baseline: 262 files / 5269 tests passed

  4. self-assigned this
    on Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions