Design Library

Public role lookup - guided rail

public-role-guided-rail.tsx

componentskill: emil-design-eng + design-taste-frontendsearchpublic-recordscomplianceguided-flow

Variants of public-role-lookup-2026-q3: public-role-compact-ledger, public-role-evidence-workspace

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 375px
never 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-guided-rail.json

Source

Meta block and library type import stripped - this is what pastes cleanly elsewhere.

'use client'

import { Check, ChevronRight, Search, ShieldCheck } from 'lucide-react'
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'

const steps = ['Choose scope', 'Enter subject', 'Review evidence']

export default function PublicRoleGuidedRail() {
  const [scope, setScope] = useState('mandate')

  return (
    <main className="min-h-[100dvh] bg-zinc-100 text-zinc-950 dark:bg-zinc-950 dark:text-zinc-100">
      <div className="mx-auto grid min-h-[100dvh] max-w-7xl lg:grid-cols-[18rem_minmax(0,1fr)]">
        <aside className="border-zinc-300 border-b bg-zinc-950 p-6 text-zinc-100 lg:border-r lg:border-b-0 dark:border-zinc-800">
          <div className="flex items-center gap-3">
            <ShieldCheck aria-hidden className="size-6 text-blue-400" />
            <p className="font-semibold tracking-[-0.02em]">Public role lookup</p>
          </div>
          <p className="mt-3 max-w-[28rem] text-sm leading-6 text-zinc-400">
            A constrained search for roles already disclosed by an official publisher.
          </p>

          <ol className="mt-8 grid grid-cols-3 gap-2 lg:grid-cols-1 lg:gap-0">
            {steps.map((step, index) => (
              <li
                key={step}
                className="flex min-w-0 items-center gap-3 border-zinc-800 border-t py-3 text-sm first:border-t-0 lg:first:border-t"
              >
                <span
                  className={`flex size-6 shrink-0 items-center justify-center border text-xs ${
                    index < 2
                      ? 'border-blue-400 bg-blue-400 text-zinc-950'
                      : 'border-zinc-600 text-zinc-400'
                  }`}
                >
                  {index < 2 ? <Check aria-hidden className="size-3.5" /> : index + 1}
                </span>
                <span className="hidden truncate sm:inline">{step}</span>
              </li>
            ))}
          </ol>
        </aside>

        <div className="min-w-0 px-4 py-8 sm:px-8 lg:px-12 lg:py-12">
          <header className="max-w-2xl">
            <p className="text-sm text-[#000091] dark:text-blue-400">Subject and scope</p>
            <h1 className="mt-2 font-semibold text-3xl tracking-[-0.03em]">
              Define what may be searched
            </h1>
            <p className="mt-3 text-sm leading-6 text-zinc-600 dark:text-zinc-300">
              A narrow scope produces evidence that is easier to verify and less likely to be
              misapplied.
            </p>
          </header>

          <form
            className="mt-8 max-w-3xl border-zinc-300 border bg-white dark:border-zinc-800 dark:bg-zinc-900"
            onSubmit={(event) => event.preventDefault()}
          >
            <fieldset className="p-5 sm:p-7">
              <legend className="px-1 font-semibold text-base">Published role type</legend>
              <div className="mt-3 grid gap-0 border-zinc-300 border dark:border-zinc-700">
                {[
                  ['mandate', 'Public mandate', 'Elected or appointed public responsibilities'],
                  ['company', 'Company office', 'Director or officer disclosed in a register'],
                  [
                    'appointment',
                    'Public appointment',
                    'Membership of a public body or commission',
                  ],
                ].map(([value, label, description]) => (
                  <label
                    key={value}
                    className={`flex cursor-pointer gap-3 border-zinc-300 border-b p-4 last:border-b-0 dark:border-zinc-700 ${
                      scope === value
                        ? 'bg-blue-50 dark:bg-blue-950/60'
                        : 'hover:bg-zinc-50 dark:hover:bg-zinc-800'
                    }`}
                  >
                    <input
                      type="radio"
                      name="scope"
                      value={value}
                      checked={scope === value}
                      onChange={() => setScope(value)}
                      className="mt-0.5 size-4 accent-[#000091]"
                    />
                    <span>
                      <span className="block font-medium text-sm">{label}</span>
                      <span className="mt-1 block text-xs leading-5 text-zinc-600 dark:text-zinc-400">
                        {description}
                      </span>
                    </span>
                  </label>
                ))}
              </div>
            </fieldset>

            <div className="grid gap-5 border-zinc-300 border-t p-5 sm:grid-cols-2 sm:p-7 dark:border-zinc-700">
              <label htmlFor="guided-family-name" className="grid gap-2 font-medium text-sm">
                Family name
                <Input
                  id="guided-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"
                />
              </label>
              <label htmlFor="guided-given-name" className="grid gap-2 font-medium text-sm">
                Given name
                <Input
                  id="guided-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"
                />
              </label>
            </div>

            <div className="flex flex-col gap-4 border-zinc-300 border-t bg-zinc-50 p-5 sm:flex-row sm:items-center sm:justify-between sm:p-7 dark:border-zinc-700 dark:bg-zinc-950">
              <p className="max-w-md text-xs leading-5 text-zinc-600 dark:text-zinc-400">
                Results remain unlinked until a reviewer confirms the subject.
              </p>
              <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"
              >
                Continue to evidence
                <ChevronRight aria-hidden className="size-4" />
              </Button>
            </div>
          </form>

          <section className="mt-8 max-w-3xl border-[#000091] border-l-4 bg-white p-5 dark:bg-zinc-900">
            <div className="flex gap-3">
              <Search
                aria-hidden
                className="mt-0.5 size-5 shrink-0 text-[#000091] dark:text-blue-400"
              />
              <div>
                <h2 className="font-semibold text-sm">What happens next</h2>
                <p className="mt-1 text-sm leading-6 text-zinc-600 dark:text-zinc-300">
                  The tool checks official publishers, records the query, and returns source links
                  for manual review.
                </p>
              </div>
            </div>
          </section>
        </div>
      </div>
    </main>
  )
}