From 1bda401aa1ca2a5e3345d648647a73d060f30b21 Mon Sep 17 00:00:00 2001 From: Naveen-9-9 Date: Sat, 20 Jun 2026 18:06:35 +0530 Subject: [PATCH 1/3] fix: improve search bar UI in contributor section - Replaced blurry gradient border with clean focus glow ring - Replaced plain text 'Clear' with X icon button - Added icon container with subtle background - Styled counter as a pill badge - Refined spacing and proportions - Updated tests to match new UI --- ...ContributorsSearch.empty-fallback.test.tsx | 22 +++++++++----- ...ContributorsSearch.theme-contrast.test.tsx | 2 +- app/contributors/ContributorsSearch.tsx | 30 ++++++++++++------- app/contributors/page.empty-fallback.test.tsx | 12 ++++---- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/app/contributors/ContributorsSearch.empty-fallback.test.tsx b/app/contributors/ContributorsSearch.empty-fallback.test.tsx index 7e5da6cdb..729110deb 100644 --- a/app/contributors/ContributorsSearch.empty-fallback.test.tsx +++ b/app/contributors/ContributorsSearch.empty-fallback.test.tsx @@ -4,12 +4,18 @@ import { describe, expect, it } from 'vitest'; import ContributorsSearch from './ContributorsSearch'; +function expectContributorsCount(container: HTMLElement, filtered: number, total: number) { + expect(container.textContent?.replace(/\s+/g, '').trim()).toContain( + `${filtered}/${total}contributors` + ); +} + describe('ContributorsSearch empty fallback', () => { it('renders the fallback when the contributor collection is missing', () => { - render(); + const { container } = render(); expect(screen.getByText('No architects found')).toBeTruthy(); - expect(screen.getByText('0 of 0 contributors')).toBeTruthy(); + expectContributorsCount(container, 0, 0); }); it('renders no contributor profile links for an empty collection', () => { @@ -21,15 +27,15 @@ describe('ContributorsSearch empty fallback', () => { it('keeps the empty collection stable while searching and clearing', async () => { const user = userEvent.setup(); - render(); + const { container } = render(); const input = screen.getByRole('textbox', { name: 'Search contributors by name' }); await user.type(input, 'missing contributor'); expect(screen.getByText('No architects found')).toBeTruthy(); - expect(screen.getByText('0 of 0 contributors')).toBeTruthy(); + expectContributorsCount(container, 0, 0); - await user.click(screen.getByRole('button', { name: 'Clear' })); + await user.click(screen.getByRole('button', { name: 'Clear search' })); expect(input).toHaveValue(''); expect(screen.getByText('No architects found')).toBeTruthy(); @@ -37,7 +43,7 @@ describe('ContributorsSearch empty fallback', () => { it('moves from populated results to the fallback and back', async () => { const user = userEvent.setup(); - render( + const { container } = render( { expect(screen.getByText('No architects found')).toBeTruthy(); expect(screen.queryByRole('link')).toBeNull(); - await user.click(screen.getByRole('button', { name: 'Clear' })); + await user.click(screen.getByRole('button', { name: 'Clear search' })); expect(screen.getByRole('link', { name: /alice/i })).toBeTruthy(); - expect(screen.getByText('1 of 1 contributors')).toBeTruthy(); + expectContributorsCount(container, 1, 1); }); }); diff --git a/app/contributors/ContributorsSearch.theme-contrast.test.tsx b/app/contributors/ContributorsSearch.theme-contrast.test.tsx index df1c27811..8ca552897 100644 --- a/app/contributors/ContributorsSearch.theme-contrast.test.tsx +++ b/app/contributors/ContributorsSearch.theme-contrast.test.tsx @@ -62,7 +62,7 @@ describe('ContributorsSearch Theme Contrast Tests', () => { expect(input.className).toContain('dark:text-white'); - expect(input.className).toContain('dark:placeholder:text-zinc-600'); + expect(input.className).toContain('dark:placeholder:text-zinc-500'); }); it('applies dark and light border styling on contributor cards', () => { diff --git a/app/contributors/ContributorsSearch.tsx b/app/contributors/ContributorsSearch.tsx index bb7752826..7fc9b1049 100644 --- a/app/contributors/ContributorsSearch.tsx +++ b/app/contributors/ContributorsSearch.tsx @@ -3,7 +3,7 @@ import { useState, useRef } from 'react'; import Link from 'next/link'; import Image from 'next/image'; -import { GitFork, Search } from 'lucide-react'; +import { GitFork, Search, X } from 'lucide-react'; import { motion, AnimatePresence, Variants } from 'framer-motion'; interface Contributor { @@ -89,32 +89,40 @@ export default function ContributorsSearch({ return ( <> {/* SEARCH BAR */} -
+
- {/* Animated gradient border */} -
-
- + {/* Hover/focus glow ring */} +
+
+
+ +
setSearch(e.target.value)} - className="w-full bg-transparent px-4 py-5 text-lg text-black dark:text-white placeholder:text-zinc-400 dark:placeholder:text-zinc-600 outline-none font-light" + className="w-full bg-transparent px-4 py-3.5 text-base text-black dark:text-white placeholder:text-zinc-400 dark:placeholder:text-zinc-500 outline-none font-medium tracking-wide" /> {search && ( )}
-
- {filtered.length} of {contributors.length} contributors +
+ + {filtered.length} + / + {contributors.length} + contributors +
diff --git a/app/contributors/page.empty-fallback.test.tsx b/app/contributors/page.empty-fallback.test.tsx index d1c52453d..7a8574fe0 100644 --- a/app/contributors/page.empty-fallback.test.tsx +++ b/app/contributors/page.empty-fallback.test.tsx @@ -88,10 +88,10 @@ describe('ContributorsPage empty fallback', () => { it('renders fallback UI when contributors are empty', async () => { const element = await ContributorsPage(); - render(element); + const { container } = render(element); expect(screen.getByText(/No architects found/i)).toBeTruthy(); - expect(screen.getByText(/0 of 0 contributors/i)).toBeTruthy(); + expect(container.textContent?.replace(/\s+/g, '')).toContain('0/0contributors'); expect(screen.getByRole('heading', { name: /THE COLLECTIVE/i })).toBeTruthy(); expect(screen.getByText(/READY TO BUILD\?/i)).toBeTruthy(); }); @@ -109,10 +109,10 @@ describe('ContributorsPage empty fallback', () => { it('handles fetch failures gracefully and still renders fallback state', async () => { global.fetch = vi.fn(() => Promise.reject(new Error('Network failure'))) as any; const element = await ContributorsPage(); - render(element); + const { container } = render(element); expect(screen.getByText(/No architects found/i)).toBeTruthy(); - expect(screen.getByText(/0 of 0 contributors/i)).toBeTruthy(); + expect(container.textContent?.replace(/\s+/g, '')).toContain('0/0contributors'); }); it('handles non-ok API responses without breaking the page', async () => { @@ -126,10 +126,10 @@ describe('ContributorsPage empty fallback', () => { ) as any; const element = await ContributorsPage(); - render(element); + const { container } = render(element); expect(screen.getByText(/No architects found/i)).toBeTruthy(); - expect(screen.getByText(/0 of 0 contributors/i)).toBeTruthy(); + expect(container.textContent?.replace(/\s+/g, '')).toContain('0/0contributors'); }); it('does not emit console errors when the fallback page renders', async () => { From ceb9e4036df7e95d4d4793103f4aa47a626cb095 Mon Sep 17 00:00:00 2001 From: Naveen-9-9 Date: Sat, 20 Jun 2026 20:36:24 +0530 Subject: [PATCH 2/3] fix: remove blue focus outline on search input The global :focus-visible outline was overriding the input's own focus styling (cyan border + glow ring). Added focus-visible:outline-none to suppress the mismatched blue outline. --- app/contributors/ContributorsSearch.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/contributors/ContributorsSearch.tsx b/app/contributors/ContributorsSearch.tsx index 7fc9b1049..cd2d7ef47 100644 --- a/app/contributors/ContributorsSearch.tsx +++ b/app/contributors/ContributorsSearch.tsx @@ -103,7 +103,7 @@ export default function ContributorsSearch({ aria-label="Search contributors by name" value={search} onChange={(e) => setSearch(e.target.value)} - className="w-full bg-transparent px-4 py-3.5 text-base text-black dark:text-white placeholder:text-zinc-400 dark:placeholder:text-zinc-500 outline-none font-medium tracking-wide" + className="w-full bg-transparent px-4 py-3.5 text-base text-black dark:text-white placeholder:text-zinc-400 dark:placeholder:text-zinc-500 outline-none focus-visible:outline-none font-medium tracking-wide" /> {search && (