PII Post Guard

Before a public post goes out, finds phone numbers, emails, Aadhaar, PAN, card numbers or OTPs in it and masks them in one tap.

StopsHelps stop an Aadhaar number being posted in public

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/pii-post-guard.json

The files land in your project. They're yours to edit.

Usage

pii-post-guard-usage.tsx
import { PiiPostGuard } from "@/components/ui/pii-post-guard"

<PiiPostGuard
  label="Your review of QuickKart"
  defaultValue={REVIEW}
  delay={0}
  rows={5}
  aria-describedby="review-tip"
  onPost={() => setPosted(true)}
  audience={{ label: "Public review", who: "anyone on the internet", note: "Reviews are indexed by search engines." }}
/>

What it catches

  • Aadhaar numbers (Verhoeff check)
  • Card numbers (Luhn check)
  • PAN numbers, including holder type
  • Phone numbers, emails and street addresses
  • OTPs near words like code or OTP

Props

PiiPostGuard props
PropTypeDefault
label

Visible label.

string—
value

Controlled text.

string—
defaultValue

Initial text when uncontrolled.

string""
onValueChange

Called with the text on every change, including masking.

(text: string) => void—
audience

Who will see the post. Defaults to the public internet.

PostAudience—
kinds

Which details to look for. Defaults to all of them.

PiiKind[]PII_KINDS
mask

Your own masking, e.g. to match a house style. Defaults to `maskPii`.

(match: PiiMatch) => string—
delay

Pause in ms before findings refresh while typing, so nothing is announced per keystroke.

number600
locale

Locale for joining the list of kinds ("phone number and PAN").

string—
onPost

Called on Post. With details left, the first click jumps to them and the second posts. May return a Promise.

(text: string) => void | Promise<void>—
messages

Any visible string; `kinds` and `why` may be partial.

Partial<Omit<PiiPostMessages, "kinds" | "why">> & { kinds?: Partial<PiiPostMessages["kinds"]>; why?: Partial<PiiPostMessages["why"]> }—
className

Class for the outer wrapper.

string—

* required. Also accepts the native props of <textarea> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible label; textarea is described by the audience line and findings
  • Announcements go to a polite live region
  • Focus moves to the next mask button, then back to the field

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Scan and redact on the server before publishing, and never log the raw text.

  • India-focused identifiers; other countries' IDs aren't detected
  • Addresses are detected heuristically
  • The post still goes out if the user chooses to; it masks, it doesn't block

What it stops

Helps stop an Aadhaar number being posted in public

Also known as

pii · aadhaar · pan · phone number · comment box · review · mask · luhn

Dependencies

Pairs well with