PixelDosa UI

AI Disclosure

Say what the AI did and what it used — or ask first, with both answers equally available.

Written by AI, from your project

A draft reply, prepared from this thread and your saved tone. Check it before sending.

How this works

Record and summarise this call?

Everyone on the call is told. You can stop it at any point.

Who can see it
You and the three people on this call.
How long it's kept
30 days, then deleted.

Installation

bash
npx shadcn@latest add @pixeldosa/ai-disclosure

Requires `cn` at `@/lib/utils`. Write the `facts` as specifics about this feature, not policy boilerplate.

Usage

"use client";

import { AIDisclosure } from "@/components/ui/ai-disclosure";

<AIDisclosure
  title="Written by AI, from your project"
  summary="A draft reply, prepared from this thread and your saved tone."
  facts={[
    { id: "used", label: "What it used", value: "The last 20 messages in this thread." },
    { id: "training", label: "Training", value: "Your content isn't used to train the model." },
  ]}
  learnMoreHref="/privacy"
/>

Why it exists

People are entitled to know when AI wrote something, what it read to do that, and what happens to their data — and to be asked before a feature records, remembers or shares. Most products bury this in a policy page or a tooltip.

Features

  • Two shapes, one voice. notice states the fact; consent asks first.
  • Specifics, not boilerplate. facts are label-and-value pairs about this feature: what it used, how long it's kept, who can see it.
  • A notice stays one line with the details one click away.
  • Consent shows the details up front. A decision taken without them isn't consent.
  • Both answers look the same. Same size, same prominence, nothing preselected, and no dismiss — a question you can close is a question that gets closed.

Variants

A notice on AI-written content

This reply was drafted by AI

Prepared from this thread and your saved tone. Check it before sending.

Asking first

Record and summarise this meeting?

Everyone in the meeting is told. You can stop it at any point.

Who can see it
You and the three people on this call.
How long it's kept
30 days, then deleted.

A short notice, no details

This summary was written by AI

Check anything you plan to act on.

Props

PropTypeNotes
titlestringRequired. The fact, or the question.
variant"notice" | "consent"Defaults to "notice".
summarystringOne sentence in the user's words.
facts{ id, label, value }[]The specifics. Shown up front for consent.
learnMoreHref / learnMoreLabelstringYour policy page.
onAllow / onDecline() => voidConsent only.
allowLabel / declineLabelstringSay what happens: "Record and summarise".
onDismiss() => voidNotice only.
headingLevel2 | 3 | 4 | 5 | 6Defaults to 3.

Accessibility

  • A labelled <section> with a real heading.
  • The details toggle is a proper expander (aria-expanded, aria-controls).
  • Consent buttons are ordinary buttons of equal weight, in reading order, with labels that say what will happen rather than "OK" and "Cancel".

When not to use

  • For one consequential action awaiting a decision, use AI Approval Gate.
  • As a cookie banner. This is about what a feature does with a person's content.
  • To explain one output's sources, use AI Context Surface or Inline Citations.

why it's built this way

Engineering Notes

Notice and consent are one component because they share a sentence, a set of facts and a visual home; splitting them produced two surfaces that drifted apart in wording, which is how products end up disclosing one thing and asking about another. What differs is only what the variant does with the facts: a notice hides them behind a disclosure button so the common case stays a single line, while consent renders them open, because a decision taken without the specifics is not consent and an expander is an easy thing to skip. Both consent buttons carry identical styling by construction rather than by convention — there is no primary variant available to the caller — so a product cannot make "Allow" the obvious answer. The consent variant intentionally offers no dismiss: a question you can close is a question that gets closed.

how it moves, and when it doesn't

Motion Notes

Nothing animates in either variant. Consent is the user's turn at its most consequential, and a notice is a statement of fact, so neither has anything to express through motion; the only transitions are the standard instant colour changes on buttons, disabled under `prefers-reduced-motion`.