diff --git a/apps/differ/src/app.css b/apps/differ/src/app.css index edb09ea33..47dedda79 100644 --- a/apps/differ/src/app.css +++ b/apps/differ/src/app.css @@ -30,8 +30,8 @@ --diff-range-border: #524d58; --diff-comment-highlight: rgba(88, 166, 255, 0.5); - --search-match-bg: rgba(250, 200, 50, 0.35); - --search-current-match-bg: rgba(255, 150, 50, 0.5); + --search-match-accent: #f2c744; + --search-current-match-bg: rgba(242, 199, 68, 0.3); --annotation-overlay-bg: rgba(0, 0, 0, 0.5); --annotation-border: rgba(255, 255, 255, 0.1); diff --git a/apps/staged/src/app.css b/apps/staged/src/app.css index 9a69fb83d..15de5de59 100644 --- a/apps/staged/src/app.css +++ b/apps/staged/src/app.css @@ -92,8 +92,8 @@ --diff-comment-border: rgba(167, 139, 250, 0.56); --diff-comment-accent: #a78bfa; - --search-match-bg: rgba(250, 200, 50, 0.35); - --search-current-match-bg: rgba(255, 150, 50, 0.5); + --search-match-accent: #f2c744; + --search-current-match-bg: rgba(242, 199, 68, 0.3); --annotation-overlay-bg: rgba(0, 0, 0, 0.5); --annotation-border: rgba(255, 255, 255, 0.1); @@ -269,15 +269,41 @@ svg.lucide:not(.animate-spin), transform: translateZ(0); } -/* ── In-content search highlights ── */ +/* ── Search match highlights ── */ +/* Longhands on purpose: WebKit before Safari 26.2 rejects a thickness inside + the `text-decoration` shorthand, and a rejected `var()` declaration drops + the whole underline, not just its thickness. */ +mark.search-match, +mark.label-match { + background-color: transparent; + text-decoration-line: underline; + text-decoration-color: var(--search-match-accent); + text-decoration-skip-ink: none; +} + +/* In-content find: notes and session chat. */ mark.search-match { - background-color: rgba(255, 235, 59, 0.4); - border-radius: 2px; color: inherit; + text-decoration-thickness: 2px; + text-underline-offset: 3px; } +/* A match inside a one-line label: project search rows and the command + palette. No fill, the match in the primary text colour over a warm line, + which lifts it out of muted text. The offset keeps the line inside the + label's line box, which clips. */ +mark.label-match { + color: var(--text-primary); + text-decoration-thickness: 1.5px; + text-underline-offset: 1.5px; +} + +/* The wash alone is faint in the light theme; the ring keeps the current + match obvious on every surface. */ mark.search-match-current { - background-color: rgba(255, 152, 0, 0.6); + background-color: var(--search-current-match-bg); + border-radius: 2px; + box-shadow: 0 0 0 1px var(--search-match-accent); } /* ── Hashtag badges (shared across all badge contexts) ── */ diff --git a/apps/staged/src/lib/features/commandPalette/CommandPalette.component.test.ts b/apps/staged/src/lib/features/commandPalette/CommandPalette.component.test.ts index f72886532..eefb2c00a 100644 --- a/apps/staged/src/lib/features/commandPalette/CommandPalette.component.test.ts +++ b/apps/staged/src/lib/features/commandPalette/CommandPalette.component.test.ts @@ -872,7 +872,7 @@ describe('CommandPalette filtering', () => { await type('beta'); expect(optionNames()).toEqual(['Beta tools']); - expect(options()[0].querySelector('.row-name mark')?.textContent).toBe('Beta'); + expect(options()[0].querySelector('.row-name mark.label-match')?.textContent).toBe('Beta'); }); it('returns the highlight to the first result when the query changes', async () => { @@ -934,11 +934,15 @@ describe('CommandPalette filtering', () => { await type('payments'); expect(optionIds()).toEqual(['beta']); expect(optionFor('beta').querySelector('.row-repo')?.textContent).toBe('org/payments'); - expect(optionFor('beta').querySelector('.row-repo mark')?.textContent).toBe('payments'); + expect(optionFor('beta').querySelector('.row-repo mark.label-match')?.textContent).toBe( + 'payments' + ); await type('storefront'); expect(optionIds()).toEqual(['gamma']); - expect(optionFor('gamma').querySelector('.row-alias mark')?.textContent).toBe('storefront'); + expect(optionFor('gamma').querySelector('.row-alias mark.label-match')?.textContent).toBe( + 'storefront' + ); expect(optionFor('gamma').querySelector('.row-repo')?.textContent).toBe('org/web/apps/site'); }); }); diff --git a/apps/staged/src/lib/features/commandPalette/CommandPaletteRow.svelte b/apps/staged/src/lib/features/commandPalette/CommandPaletteRow.svelte index 5618bae0c..7592177ce 100644 --- a/apps/staged/src/lib/features/commandPalette/CommandPaletteRow.svelte +++ b/apps/staged/src/lib/features/commandPalette/CommandPaletteRow.svelte @@ -62,7 +62,7 @@ {#snippet highlighted(segments: readonly HighlightSegment[])} - {#each segments as segment}{#if segment.match}{segment.text}{segment.text}{:else}{segment.text}{/if}{/each} {/snippet} @@ -240,12 +240,11 @@ min-width: 0; } - .row-name mark, - .row-alias mark, - .row-repo mark { - background-color: var(--search-match-bg); - border-radius: 2px; - color: inherit; + /* Matches use the global `label-match` rule, shared with ProjectMatchReason, + which draws the reason beside these. The pill's border sits right under + the text; pull the line up off it. */ + .row-alias mark { + text-underline-offset: 0.5px; } .row-badges { diff --git a/apps/staged/src/lib/features/projects/ProjectMatchReason.svelte b/apps/staged/src/lib/features/projects/ProjectMatchReason.svelte index b68900a87..4eb5a2c4a 100644 --- a/apps/staged/src/lib/features/projects/ProjectMatchReason.svelte +++ b/apps/staged/src/lib/features/projects/ProjectMatchReason.svelte @@ -57,7 +57,7 @@ {KIND_NAMES[reason.kind]}: {#each segments as segment}{#if segment.match}{segment.text}{#each segments as segment}{#if segment.match}{segment.text}{:else}{segment.text}{/if}{/each} {#if reason.more > 0} @@ -84,12 +84,6 @@ text-overflow: ellipsis; } - .match-label mark { - background-color: var(--search-match-bg); - border-radius: 2px; - color: inherit; - } - .match-more { flex-shrink: 0; color: var(--text-faint); diff --git a/apps/staged/src/lib/theme.ts b/apps/staged/src/lib/theme.ts index 34ca8f3bc..51c97a8da 100644 --- a/apps/staged/src/lib/theme.ts +++ b/apps/staged/src/lib/theme.ts @@ -70,7 +70,7 @@ export interface Theme { // Search highlight colors search: { - matchBg: string; // Background for search matches + matchAccent: string; // Line under a search match currentMatchBg: string; // Background for current/focused match }; @@ -510,13 +510,11 @@ export function createAdaptiveTheme( }, search: { - // Search yellow derived from foreground warmth — mix fg toward warm tone - matchBg: overlay(mix(syntaxFg, isDark ? '#fac832' : '#e8b000', 0.7), isDark ? 0.35 : 0.5), - // Current match: warmer/more saturated variant of above - currentMatchBg: overlay( - mix(syntaxFg, isDark ? '#ff9632' : '#d07000', 0.7), - isDark ? 0.5 : 0.6 - ), + // A match keeps its surface and gains a warm line beneath it. Solid, so + // it reads the same on plain, hovered and selected rows. + matchAccent: isDark ? '#f2c744' : '#a87400', + // Only the current match is filled, in a wash of the same amber. + currentMatchBg: overlay(isDark ? '#f2c744' : '#f2b600', isDark ? 0.3 : 0.35), }, annotation: { @@ -632,7 +630,7 @@ export function themeToVarMap(t: Theme): Record { '--diff-comment-border': t.diff.commentBorder, '--diff-comment-accent': t.diff.commentAccent, - '--search-match-bg': t.search.matchBg, + '--search-match-accent': t.search.matchAccent, '--search-current-match-bg': t.search.currentMatchBg, '--annotation-overlay-bg': t.annotation.overlayBg, diff --git a/packages/diff-viewer/src/lib/components/DiffViewer.svelte b/packages/diff-viewer/src/lib/components/DiffViewer.svelte index a8c5928fa..4b61efb59 100644 --- a/packages/diff-viewer/src/lib/components/DiffViewer.svelte +++ b/packages/diff-viewer/src/lib/components/DiffViewer.svelte @@ -3352,23 +3352,24 @@ } /* Search result highlighting */ - .search-match { - background-color: rgba(250, 200, 50, 0.35); - border-radius: 2px; + /* Longhands on purpose: WebKit before Safari 26.2 rejects a thickness inside + the shorthand and would drop the whole underline. The `em` offset keeps + the line inside the row at any `--code-font-size`. */ + .search-match, + .search-current { + text-decoration-line: underline; + text-decoration-thickness: 2px; + text-decoration-color: var(--search-match-accent, #f2c744); + text-underline-offset: 0.2em; + text-decoration-skip-ink: none; } + /* On a modified line the wash is nearly the same colour as the + changed-character tint it replaces; the ring is what keeps the current + match visible there. */ .search-current { - background-color: rgba(255, 150, 50, 0.5); + background-color: var(--search-current-match-bg, rgba(242, 199, 68, 0.3)); border-radius: 2px; - } - - @media (prefers-color-scheme: light) { - .search-match { - background-color: rgba(250, 200, 50, 0.5); - } - - .search-current { - background-color: rgba(255, 150, 50, 0.6); - } + box-shadow: 0 0 0 1px var(--search-match-accent, #f2c744); }