Nasaq

LegalPage

A legal document page with a switcher between documents, the updated date, a draft notice, numbered sections with copyable anchors and a section rail.

PreviewOpen ↗

Code

import { LegalPage } from "@fadymondy/nasaq/web";export function Terms() {  return (    <LegalPage      document={{        id: "terms",        title: "Terms of service",        updated: "2026-09-01",        sections: [          { title: "Using the service", body: "You agree to use it lawfully." },          { title: "Payments", body: "Fees are billed monthly." },        ],      }}    />  );}

Website · beta

Live examples and controls: LegalPage in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/legal-page.json

Terms of service, privacy policy, cookie policy: long documents people search for one clause in. The page has a switcher between the documents, the date it was last updated, an optional draft notice, numbered sections whose headings are #anchors with a copy-link button, and an "On this page" rail. The text sits in one readable column of about 68 characters.

When to use

  • Public legal pages of a product.
  • Any long policy or agreement where clause links get shared.

When not to use

Import

import { LegalPage } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"

Quick start

import { LegalPage } from "@fadymondy/nasaq/web";

export function Terms() {
  return (
    <LegalPage
      document={{
        id: "terms",
        title: "Terms of service",
        updated: "2026-09-01",
        sections: [
          { title: "Using the service", body: "You agree to use it lawfully." },
          { title: "Payments", body: "Fees are billed monthly." },
        ],
      }}
    />
  );
}

Anatomy

LegalPage             data-slot="legal-page"
├─ switcher           <nav> of documents (two or more)
├─ header             title, summary, updated / effective / version, draft Alert
├─ sections           data-slot="legal-section"  number, h2 with id, copy-link button, Markdown body
├─ footer             your contact details (optional)
└─ rail               TableOfContents (from the lg breakpoint)

API

PropTypeDefaultDescription
documentLegalDocumentrequired{ id, title, summary?, updated, effective?, version?, draft?, sections }.
documents{ id, title }[]noneThe site's documents. The switcher shows for two or more.
onSelectDocument(id) => voidnoneA document was picked. Navigate, then pass the new document.
onCopyLink(url) => voidnoneAfter a section link was copied.
footerReactNodenoneContact details under the last section.
scrollOffsetnumber96Space headings keep from the top when scrolled to.
labelsLegalPageLabelsEnglish or ArabicString overrides.

LegalSection is { id?, title, body } where body is Markdown. Give sections an explicit id once a document is published so shared links keep working when titles change. Without one the id is a slug of the title (Arabic letters are kept); repeats get -2, -3.

Helpers

resolveLegalSections, legalHashTarget, legalSectionUrl.

Examples

<LegalPage document={terms} documents={[{ id: "terms", title: "Terms" }, { id: "privacy", title: "Privacy" }]} onSelectDocument={goTo} />
<LegalPage document={{ ...privacy, draft: true, version: "2.0-rc1" }} footer={<a href="mailto:[email protected]">[email protected]</a>} />

A link such as /terms#payments opens scrolled to that section.

Accessibility

KeyAction
TabSwitcher, copy-link buttons, links in the text, the rail.
Enter, SpacePick a document, copy a section link.
  • Every section is a section labelled by its heading. The copy-link button names the section, and the result is announced in a polite status.
  • The draft notice is an alert with text and an icon, not colour alone.
  • The current document in the switcher has aria-current="page".

RTL & i18n

  • The page mirrors: rail on the end side, numbers before titles on the reading start.
  • Titles and text use dir="auto"; dates use the locale with Latin digits by default.
  • Built-in strings are English and Arabic. Provide the document text in the reader's language.

Styling & tokens

Uses the typography roles and --nq-* border, selected and focus tokens. Target data-slot="legal-page" and legal-section.

Do / Don't

  • Do show the date the document changed and, when it matters, the date it takes effect.
  • Do keep section ids stable.
  • Don't put the whole document in one section. Anchors are only useful when sections are small.
  • Don't present a draft as final. Set draft.

Lab

https://docs.nasaqui.com/?path=/docs/components-website-legal-page--docs

On this page