From 70e74406e59a167f2f2e3476962118a6f80cb756 Mon Sep 17 00:00:00 2001 From: Chaitanya Chandurkar Date: Thu, 10 Sep 2026 11:59:43 -0400 Subject: [PATCH 1/5] style(ui): sidebar/analysis-panel spacing tweak, tooltip icons for array matching hints - Reduce sidebar/analysis-panel section top margin (32px -> 20px) - Replace inline array-matching toggle-hint text with ? tooltip icons, matching the existing sidebar Comparison section pattern --- .../analysis-panel.component.css | 2 +- .../array-matching.component.css | 34 +++++++++--- .../array-matching.component.html | 54 ++++++++++++++++--- .../array-matching.component.ts | 3 +- .../components/sidebar/sidebar.component.css | 2 +- 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/packages/ui/src/app/components/analysis-panel/analysis-panel.component.css b/packages/ui/src/app/components/analysis-panel/analysis-panel.component.css index 9d6a2db..da754bb 100644 --- a/packages/ui/src/app/components/analysis-panel/analysis-panel.component.css +++ b/packages/ui/src/app/components/analysis-panel/analysis-panel.component.css @@ -75,7 +75,7 @@ display: flex; flex-direction: column; gap: 2px; - margin-top: 32px; + margin-top: 20px; } .panel-section:first-of-type { diff --git a/packages/ui/src/app/components/array-matching/array-matching.component.css b/packages/ui/src/app/components/array-matching/array-matching.component.css index c484bdd..80e1a08 100644 --- a/packages/ui/src/app/components/array-matching/array-matching.component.css +++ b/packages/ui/src/app/components/array-matching/array-matching.component.css @@ -20,7 +20,7 @@ app-array-picker { .toggle-row { display: flex; - align-items: flex-start; + align-items: center; gap: 9px; border-radius: var(--radius); padding: 6px 8px; @@ -36,7 +36,7 @@ app-array-picker { } .toggle-row input { - margin: 1px 0 0; + margin: 0; accent-color: var(--primary); cursor: pointer; } @@ -47,6 +47,7 @@ app-array-picker { } .toggle-text { + flex: 1; display: flex; flex-direction: column; gap: 2px; @@ -58,9 +59,29 @@ app-array-picker { font: 500 12px var(--sans); } -.toggle-hint { - color: var(--muted-foreground); - font: 11px/1.4 var(--sans); +.hint-icon { + flex: none; + width: 16px; + height: 16px; + display: grid; + place-items: center; + border: 1px solid var(--border); + border-radius: 999px; + background: transparent; + color: var(--subtle-foreground); + font: 600 10px var(--sans); + line-height: 1; + cursor: help; +} + +.hint-icon:hover { + color: var(--foreground); + border-color: var(--border-strong); +} + +.hint-icon:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 2px; } .field-list { @@ -171,7 +192,8 @@ app-array-picker { } .matching-fieldset:disabled .toggle-row, -.matching-fieldset:disabled .toggle-row input { +.matching-fieldset:disabled .toggle-row input, +.matching-fieldset:disabled .hint-icon { cursor: not-allowed; } diff --git a/packages/ui/src/app/components/array-matching/array-matching.component.html b/packages/ui/src/app/components/array-matching/array-matching.component.html index 46d8112..6aa2e1e 100644 --- a/packages/ui/src/app/components/array-matching/array-matching.component.html +++ b/packages/ui/src/app/components/array-matching/array-matching.component.html @@ -6,24 +6,45 @@ Auto - JSON Semantic Diff chooses the best identity. +
@@ -38,16 +59,30 @@ Auto - JSON Semantic Diff chooses the best identity. + @if (strategy() === 'key') { @@ -84,8 +119,15 @@ Position - Compare elements by array position. + @if (context.analysis.duplicateKeyCount) { diff --git a/packages/ui/src/app/components/array-matching/array-matching.component.ts b/packages/ui/src/app/components/array-matching/array-matching.component.ts index 758d65b..3dfcd77 100644 --- a/packages/ui/src/app/components/array-matching/array-matching.component.ts +++ b/packages/ui/src/app/components/array-matching/array-matching.component.ts @@ -10,6 +10,7 @@ import { } from 'json-semantic-diff'; import { percent } from '../../shared/format'; import { MatchingOverrideChange, overridePatternFor } from '../../shared/node-actions'; +import { TooltipDirective } from '../../shared/tooltip/tooltip.directive'; import { ArrayPickerComponent } from '../array-picker/array-picker.component'; /** One configurable array: its core analysis plus the node holding the rows. */ @@ -30,7 +31,7 @@ type Strategy = 'auto' | 'key' | 'position'; @Component({ selector: 'app-array-matching', standalone: true, - imports: [ArrayPickerComponent], + imports: [ArrayPickerComponent, TooltipDirective], changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './array-matching.component.html', styleUrl: './array-matching.component.css' diff --git a/packages/ui/src/app/components/sidebar/sidebar.component.css b/packages/ui/src/app/components/sidebar/sidebar.component.css index 1203698..c8fcca6 100644 --- a/packages/ui/src/app/components/sidebar/sidebar.component.css +++ b/packages/ui/src/app/components/sidebar/sidebar.component.css @@ -62,7 +62,7 @@ display: flex; flex-direction: column; gap: 2px; - margin-top: 32px; + margin-top: 20px; } .sidebar-section:first-of-type { From 8b02285c620beb41d0f6b79d1c389dac7db363c2 Mon Sep 17 00:00:00 2001 From: Chaitanya Chandurkar Date: Thu, 10 Sep 2026 12:45:58 -0400 Subject: [PATCH 2/5] style(ui): switch-style toggles for sidebar width, Firefox pseudo-element fix, and toolbar 'Changes only' checkbox - Reduce left sidebar width 300px -> 270px - Add -moz-appearance: none fallback so the switch thumb renders in Firefox - Apply the same pill-switch re-skin to the toolbar's 'Changes only' checkbox --- .../components/sidebar/sidebar.component.css | 54 +++++++++++++++++-- packages/ui/src/app/home/home.component.css | 48 ++++++++++++++++- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/app/components/sidebar/sidebar.component.css b/packages/ui/src/app/components/sidebar/sidebar.component.css index c8fcca6..a6c5049 100644 --- a/packages/ui/src/app/components/sidebar/sidebar.component.css +++ b/packages/ui/src/app/components/sidebar/sidebar.component.css @@ -81,16 +81,37 @@ .toggle-row { display: flex; align-items: center; - gap: 9px; + gap: 6px; border-radius: var(--radius); padding: 6px 8px; cursor: pointer; } +/* + * Re-skinned native checkbox: keeps for free + * keyboard support/screen-reader semantics, appearance:none just strips the + * default box so a pill track + sliding thumb can be drawn instead. + */ .toggle-row input { + flex: none; margin: 0; - accent-color: var(--primary); + appearance: none; + + /* Firefox (Gecko) doesn't render ::before/::after on at all unless + * -moz-appearance is explicitly reset - Chrome/Safari/Edge already allow it + * without this. Without this line the switch's thumb is invisible in Firefox. */ + /* stylelint-disable-next-line property-no-vendor-prefix -- required, not accidental prefix creep; see comment above */ + -moz-appearance: none; + width: 24px; + height: 14px; + border: 1px solid var(--border-strong); + border-radius: 999px; + background: var(--muted); cursor: pointer; + position: relative; + transition: + background-color 0.15s ease, + border-color 0.15s ease; } .toggle-row:has(input:focus-visible) { @@ -113,8 +134,8 @@ .hint-icon { flex: none; - width: 16px; - height: 16px; + width: 14px; + height: 14px; display: grid; place-items: center; border: 1px solid var(--border); @@ -232,6 +253,29 @@ outline-color: var(--danger); } +/* Toggle-switch thumb/checked state; kept below .ignore-row's `input` rules to satisfy stylelint's no-descending-specificity ordering. */ +.toggle-row input::after { + content: ''; + position: absolute; + top: 1px; + left: 1px; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--card); + box-shadow: 0 1px 2px rgb(0 0 0 / 25%); + transition: transform 0.15s ease; +} + +.toggle-row input:checked { + background: var(--primary); + border-color: var(--primary); +} + +.toggle-row input:checked::after { + transform: translateX(10px); +} + .ignore-error { margin-top: 4px; font: 11px / 1.3 var(--sans); @@ -367,7 +411,7 @@ display: flex; align-items: center; gap: 6px; - font: 500 11px var(--mono); + font: 400 11px var(--mono); color: var(--subtle-foreground); letter-spacing: 0.02em; } diff --git a/packages/ui/src/app/home/home.component.css b/packages/ui/src/app/home/home.component.css index 3b750d8..16f97cd 100644 --- a/packages/ui/src/app/home/home.component.css +++ b/packages/ui/src/app/home/home.component.css @@ -9,7 +9,7 @@ } app-sidebar { - flex: 0 0 300px; + flex: 0 0 270px; min-width: 0; border-right: 1px solid var(--border); } @@ -310,9 +310,53 @@ h1 span { border-radius: var(--radius); } +/* + * Re-skinned native checkbox: keeps for free + * keyboard support/screen-reader semantics, appearance:none just strips the + * default box so a pill track + sliding thumb can be drawn instead. + */ .toolbar-toggle input { - accent-color: var(--primary); + flex: none; + margin: 0; + appearance: none; + + /* Firefox (Gecko) doesn't render ::before/::after on at all unless + * -moz-appearance is explicitly reset - Chrome/Safari/Edge already allow it + * without this. Without this line the switch's thumb is invisible in Firefox. */ + /* stylelint-disable-next-line property-no-vendor-prefix -- required, not accidental prefix creep; see comment above */ + -moz-appearance: none; + width: 24px; + height: 14px; + border: 1px solid var(--border-strong); + border-radius: 999px; + background: var(--muted); cursor: pointer; + position: relative; + transition: + background-color 0.15s ease, + border-color 0.15s ease; +} + +.toolbar-toggle input::after { + content: ''; + position: absolute; + top: 1px; + left: 1px; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--card); + box-shadow: 0 1px 2px rgb(0 0 0 / 25%); + transition: transform 0.15s ease; +} + +.toolbar-toggle input:checked { + background: var(--primary); + border-color: var(--primary); +} + +.toolbar-toggle input:checked::after { + transform: translateX(10px); } .nav-group { From e4b2665fe941bba74840c0ca7feb8af252fe8059 Mon Sep 17 00:00:00 2001 From: Chaitanya Chandurkar Date: Thu, 10 Sep 2026 13:53:47 -0400 Subject: [PATCH 3/5] style(ui): fix invisible switch thumb in dark theme Thumb used var(--card), which is identical to the track's var(--muted) background in dark theme. Switch to var(--muted-foreground) for contrast in both themes and both checked/unchecked states. --- .../ui/src/app/components/sidebar/sidebar.component.css | 8 +++++++- packages/ui/src/app/home/home.component.css | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/app/components/sidebar/sidebar.component.css b/packages/ui/src/app/components/sidebar/sidebar.component.css index a6c5049..c5d3ddf 100644 --- a/packages/ui/src/app/components/sidebar/sidebar.component.css +++ b/packages/ui/src/app/components/sidebar/sidebar.component.css @@ -262,7 +262,12 @@ width: 10px; height: 10px; border-radius: 50%; - background: var(--card); + + /* var(--card) is a no-go here: it's identical to var(--muted) (the track's + * unchecked background) in dark theme, making the thumb invisible. + * var(--muted-foreground) contrasts against the track in both themes and + * both checked/unchecked states. */ + background: var(--muted-foreground); box-shadow: 0 1px 2px rgb(0 0 0 / 25%); transition: transform 0.15s ease; } @@ -274,6 +279,7 @@ .toggle-row input:checked::after { transform: translateX(10px); + background: #fff; } .ignore-error { diff --git a/packages/ui/src/app/home/home.component.css b/packages/ui/src/app/home/home.component.css index 16f97cd..20088c6 100644 --- a/packages/ui/src/app/home/home.component.css +++ b/packages/ui/src/app/home/home.component.css @@ -345,7 +345,12 @@ h1 span { width: 10px; height: 10px; border-radius: 50%; - background: var(--card); + + /* var(--card) is a no-go here: it's identical to var(--muted) (the track's + * unchecked background) in dark theme, making the thumb invisible. + * var(--muted-foreground) contrasts against the track in both themes and + * both checked/unchecked states. */ + background: var(--muted-foreground); box-shadow: 0 1px 2px rgb(0 0 0 / 25%); transition: transform 0.15s ease; } @@ -357,6 +362,7 @@ h1 span { .toolbar-toggle input:checked::after { transform: translateX(10px); + background: #fff; } .nav-group { From 060f4464a5c45a3e2d128a19cad0cb8109e740aa Mon Sep 17 00:00:00 2001 From: Chaitanya Chandurkar Date: Thu, 10 Sep 2026 16:02:34 -0400 Subject: [PATCH 4/5] feat(ui): make analysis panel collapsible with mobile trigger bar - Add collapse/expand toggle to the analysis panel resize handle, with localStorage persistence separate from the dragged width. - Move the two mobile drawer trigger buttons out of SidebarComponent/ AnalysisPanelComponent into a shared HomeComponent .mobile-panel-bar styled as a continuation of the app header. - Re-skin the Comparison section's checkboxes as pill switches (CSS-only, native semantics preserved), with a -moz-appearance fix for Firefox ::after support. - Fix reload flashes: analysis panel collapsed state and width both start from Angular defaults (avoiding a hydration mismatch) and are corrected via afterNextRender; a pre-hydration index.html script applies both as pure visual overrides ahead of first paint. The collapsed override lives in the global styles.css (component-scoped :root selectors are dead CSS under Angular's view encapsulation, which scopes every compound selector including :root). The width override is an injected global