Skip to content
txbrendan/ui

No Access

beta

What someone sees when they open something they cannot: what it is, whose it is, and Request access with an 8-second Undo.

Preview

default

Budget review, before Saturday 3 October

Sam Carter owns this

Ask for access and they get an email. You hear back when they answer.

asked before

Pay notes, Design team

Priya Shah owns this

Ask for access and they get an email. You hear back when they answer.

no owner, own hint

Board minutes

Only board members can read these. Ask, and the secretary decides.

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/no-access

Use it for

  • Put it where the content would be, when someone opens a page, file, or team they cannot see.
  • Name what it is in title, so they know they reached the right thing.
  • Pass owner, so they know who will get the request. Put their avatar in owner.avatar.
  • Send the request in onRequest. It runs once the 8-second undo window ends, or when the part leaves the page.
  • Pass asked when they asked before, so the button shows the record instead of asking twice.

Avoid it for

  • Hiding that something exists when its name is private too. Show a plain not-found page instead.
  • Sending the request on press. onRequest waits for the undo window, so Undo never takes back a sent email.
  • Content that is only loading or empty. Use an empty state or a skeleton.

Props

  • title

    What it is, like "Budget review, before Saturday 3 October".

    string

  • owner

    Whose it is. avatar is an element, like an Avatar with their photo.

    { person: string; avatar?: ReactNode }

  • hint

    What happens when they ask.

    string · default "Ask for access and they get an email. You hear back when they answer."

  • asked

    Asked before. The button shows "Asked", with the day and time when at is given.

    { at?: string | Date } | null

  • onRequest

    Sends the request, once the 8-second undo window ends.

    () => void

  • headingLevel

    The title's heading level, so it fits the page's outline.

    1 | 2 | 3 | 4 | 5 | 6 · default 2

  • className

    Extra classes on the root. Later classes win.

    string

Accessibility

  • The title is a real heading, at the level you give.
  • Request access is a native button. After a press it reads "Asked. Press to undo." and focus stays on it.
  • Undo arms when the pointer leaves, or after 1.2 seconds for the keys and touch, so a double press never undoes.
  • A polite status line says the request was made, and says when it was undone.
  • Once sent, the button is a record: it stays in the tab order with aria-disabled and full contrast.
  • The lock spot is hidden from screen readers, since the title says what it is.

Examples

A page they cannot see

"use client";

export function LockedPage() {
  return (
    <NoAccess
      title="Budget review, before Saturday 3 October"
      owner={{ person: "Sam Carter" }}
      onRequest={() => fetch("/api/access-requests", { method: "POST" })}
    />
  );
}

Asked before

<NoAccess
  title="Pay notes, Design team"
  owner={{ person: "Priya Shah" }}
  asked={{ at: "2026-10-02T09:20:00" }}
/>