Repository navigation
Description
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
Switchbenefits, not patched per usage site - If
--input's dark value is changed rather than the component,input.svelteandtextarea.svelteare re-checked for regressions
Team sync email
-
developer@nodespace.airenders as a workingmailto: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-50interaction with fix (1) - The control is not focus-reachable as an actionable element, or is marked so assistive tech reports it as disabled
-
labsFlags.syncEnabledcannot 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.svelteis deleted rather than left unused - The
missingdatabase 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 innavigation-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 (
isCollapsedhides.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-labelor 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
[-]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[/+][-]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[/+]malibio commentedon Sep 25, 2026
Frontend baseline: 262 files / 5269 tests passed