Public role lookup - evidence workspace
public-role-evidence-workspace.tsx
componentskill: emil-design-eng + design-taste-frontendsearchpublic-recordscomplianceevidenceforms
Variants of public-role-lookup-2026-q3: public-role-compact-ledger, public-role-guided-rail
Brief
The prompt that produced this. Re-run it to generate more in the same taste.
- Aesthetic
- institutional utility - sober, exact, trust-first, compact, evidence-led, accessible
- Reference
- FLAIR public-person role lookup, verified desktop and 390px screenshots
- Intent
- make public-role research efficient and auditable without turning the interface into an identity profile
- Guardrails
- always keep source and retrieval context beside every record, use native labels and controls, make public-record scope explicit, collapse cleanly at 375pxnever resolve identity automatically, surface private contact data, use ornamental motion, imply that a public record is a current fact
Install
pnpm dlx shadcn@latest add http://localhost:3040/r/public-role-evidence-workspace.json
Source
Meta block and library type import stripped - this is what pastes cleanly elsewhere.
'use client'
import { AlertTriangle, ExternalLink, Search, ShieldCheck } from 'lucide-react'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
type DemoState = 'empty' | 'loading' | 'result' | 'unavailable'
const stateOrder: DemoState[] = ['empty', 'loading', 'result', 'unavailable']
export default function PublicRoleEvidenceWorkspace() {
const [demoState, setDemoState] = useState<DemoState>('result')
function showNextState() {
const next = (stateOrder.indexOf(demoState) + 1) % stateOrder.length
setDemoState(stateOrder[next])
}
return (
<main className="min-h-[100dvh] bg-[#f7f7f7] text-zinc-950 dark:bg-zinc-950 dark:text-zinc-100">
<header className="border-zinc-300 border-b bg-white dark:border-zinc-800 dark:bg-zinc-950">
<div className="mx-auto flex min-h-16 max-w-7xl items-center justify-between gap-4 px-4 sm:px-6">
<div>
<p className="font-semibold text-lg tracking-[-0.02em]">Public role lookup</p>
<p className="text-xs text-zinc-600 dark:text-zinc-400">Public records only</p>
</div>
<Button
type="button"
variant="outline"
size="sm"
onClick={showNextState}
className="rounded-none border-zinc-400 bg-transparent transition-[transform,background-color,border-color] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.97] motion-reduce:transform-none motion-reduce:transition-none dark:border-zinc-700"
>
State: {demoState}
</Button>
</div>
</header>
<div className="mx-auto grid max-w-7xl gap-0 px-4 py-8 sm:px-6 lg:grid-cols-[minmax(0,1fr)_21rem] lg:py-12">
<section className="min-w-0 border-zinc-300 border bg-white p-5 dark:border-zinc-800 dark:bg-zinc-900 sm:p-8">
<div className="max-w-2xl">
<h1 className="font-semibold text-2xl tracking-[-0.025em] sm:text-3xl">
Find an officially published role
</h1>
<p className="mt-2 max-w-[62ch] text-sm leading-6 text-zinc-600 dark:text-zinc-300">
Search for a public mandate, appointment, or company office. Confirm the person
manually before using the record.
</p>
</div>
<form className="mt-8 grid gap-5" onSubmit={(event) => event.preventDefault()}>
<fieldset>
<legend className="font-medium text-sm">Record type</legend>
<div className="mt-2 grid grid-cols-1 border-zinc-400 border sm:grid-cols-3 dark:border-zinc-700">
{['Public mandate', 'Company office', 'Public appointment'].map((label, index) => (
<label
key={label}
className="flex min-h-11 cursor-pointer items-center gap-2 border-zinc-300 border-b px-3 text-sm last:border-b-0 sm:border-r sm:border-b-0 sm:last:border-r-0 dark:border-zinc-700"
>
<input
type="radio"
name="record-type"
defaultChecked={index === 0}
className="size-4 accent-[#000091]"
/>
{label}
</label>
))}
</div>
</fieldset>
<div className="grid gap-5 sm:grid-cols-2">
<label htmlFor="evidence-family-name" className="grid gap-2 font-medium text-sm">
Family name
<Input
id="evidence-family-name"
defaultValue="Valcour"
autoComplete="family-name"
className="h-11 rounded-none border-zinc-400 bg-white shadow-none focus-visible:border-[#000091] focus-visible:ring-[#000091]/20 dark:border-zinc-700 dark:bg-zinc-950"
/>
<span className="font-normal text-xs text-zinc-600 dark:text-zinc-400">
Required. Match spelling from the source.
</span>
</label>
<label htmlFor="evidence-given-name" className="grid gap-2 font-medium text-sm">
Given name
<Input
id="evidence-given-name"
defaultValue="NoƩmie"
autoComplete="given-name"
className="h-11 rounded-none border-zinc-400 bg-white shadow-none focus-visible:border-[#000091] focus-visible:ring-[#000091]/20 dark:border-zinc-700 dark:bg-zinc-950"
/>
<span className="font-normal text-xs text-zinc-600 dark:text-zinc-400">
Optional, but useful for common names.
</span>
</label>
</div>
<div className="flex flex-col gap-3 border-zinc-300 border-t pt-5 sm:flex-row sm:items-center dark:border-zinc-700">
<Button
type="submit"
className="h-11 rounded-none bg-[#000091] px-5 text-white transition-[transform,background-color] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] hover:bg-[#1212a8] active:scale-[0.97] motion-reduce:transform-none motion-reduce:transition-none"
>
<Search aria-hidden className="size-4" />
Search public records
</Button>
<p className="text-xs leading-5 text-zinc-600 dark:text-zinc-400">
Queries and opened sources are written to the audit log.
</p>
</div>
</form>
</section>
<aside className="border-zinc-300 border-x border-b bg-zinc-100 p-5 dark:border-zinc-800 dark:bg-zinc-950 lg:border-t lg:border-l-0 sm:p-6">
<ShieldCheck aria-hidden className="size-6 text-[#000091] dark:text-blue-400" />
<h2 className="mt-4 font-semibold text-base">Research boundary</h2>
<ul className="mt-4 grid gap-4 text-sm leading-5 text-zinc-700 dark:text-zinc-300">
<li>Only officially published roles are returned.</li>
<li>Private addresses and contact details are excluded.</li>
<li>A name match is evidence to review, not identity resolution.</li>
</ul>
</aside>
<section
aria-live="polite"
className="min-w-0 border-zinc-300 border-x border-b bg-white p-5 dark:border-zinc-800 dark:bg-zinc-900 sm:p-8 lg:col-span-2"
>
{demoState === 'empty' && (
<div className="py-8">
<h2 className="font-semibold text-lg">No search run</h2>
<p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
Enter a name and record type to begin.
</p>
</div>
)}
{demoState === 'loading' && (
<div className="grid gap-4 py-6" role="status" aria-label="Searching public sources">
<div className="h-5 w-40 animate-pulse bg-zinc-200 motion-reduce:animate-none dark:bg-zinc-700" />
<div className="h-20 animate-pulse bg-zinc-100 motion-reduce:animate-none dark:bg-zinc-800" />
</div>
)}
{demoState === 'unavailable' && (
<div className="flex gap-3 border-amber-700 border-l-4 bg-amber-50 p-4 text-amber-950 dark:bg-amber-950 dark:text-amber-100">
<AlertTriangle aria-hidden className="mt-0.5 size-5 shrink-0" />
<div>
<h2 className="font-semibold text-sm">Source temporarily unavailable</h2>
<p className="mt-1 text-sm leading-5">
No conclusion was recorded. Retry later or open the official register directly.
</p>
</div>
</div>
)}
{demoState === 'result' && (
<div>
<div className="flex flex-col gap-2 border-zinc-300 border-b pb-5 sm:flex-row sm:items-end sm:justify-between dark:border-zinc-700">
<div>
<h2 className="font-semibold text-lg">1 record to review</h2>
<p className="mt-1 text-xs text-zinc-600 dark:text-zinc-400">
Sample dataset. Retrieved 24 July 2026.
</p>
</div>
<span className="font-medium text-xs text-zinc-700 dark:text-zinc-300">
Official source
</span>
</div>
<article className="grid gap-5 py-6 lg:grid-cols-[minmax(0,1fr)_17rem]">
<div className="min-w-0">
<p className="font-semibold text-xl tracking-[-0.02em]">NoƩmie Valcour</p>
<p className="mt-2 text-sm leading-6 text-zinc-700 dark:text-zinc-300">
Member of a municipal advisory commission
</p>
<dl className="mt-5 grid gap-4 text-sm sm:grid-cols-2">
<div>
<dt className="text-zinc-600 dark:text-zinc-400">Jurisdiction</dt>
<dd className="mt-1 font-medium">Sample municipality</dd>
</div>
<div>
<dt className="text-zinc-600 dark:text-zinc-400">Published term</dt>
<dd className="mt-1 font-medium">2025-2027</dd>
</div>
</dl>
</div>
<div className="border-zinc-300 border-l-0 pt-0 lg:border-l lg:pl-5 dark:border-zinc-700">
<p className="text-xs leading-5 text-zinc-600 dark:text-zinc-400">
The source states the role. It does not prove this is the person in your case.
</p>
<Button
type="button"
variant="outline"
className="mt-4 w-full rounded-none border-[#000091] text-[#000091] transition-[transform,background-color] duration-[160ms] ease-[cubic-bezier(0.23,1,0.32,1)] hover:bg-blue-50 active:scale-[0.97] motion-reduce:transform-none motion-reduce:transition-none dark:border-blue-400 dark:text-blue-300 dark:hover:bg-blue-950"
>
Open official source
<ExternalLink aria-hidden className="size-4" />
</Button>
</div>
</article>
</div>
)}
</section>
</div>
</main>
)
}