Employment Agreement
Northwind Freight Co.
State of California
Fixed-term employment · Form HR-104
A printed document on screen: a contract, an incident report, a ticket or a certificate. A white sheet in a serif with typed-in values, laid out at one design width and scaled as a whole to the room it gets, so it reads as the paper it stands for rather than as one more card of the app around it.
Northwind Freight Co.
State of California
Fixed-term employment · Form HR-104
# One-time setup (tokens + lib helpers)
npx koalaui-cli@latest init
# Add this component (its dependencies come along)
npx koalaui-cli@latest add paperimport {
Paper,
PaperField,
PaperHeader,
PaperHeading,
PaperRow,
PaperSection,
PaperTitle,
} from "@/components/ui/paper"
export function Example() {
return (
<Paper>
<PaperHeader>
<PaperHeading>
<PaperTitle>Delivery Note</PaperTitle>
</PaperHeading>
</PaperHeader>
<PaperSection>Consignee</PaperSection>
<PaperRow>
<PaperField label="Name" grow={2}>Priya Raman</PaperField>
<PaperField label="Packages">3</PaperField>
</PaperRow>
</Paper>
)
}Every part is optional but the sheet. A ticket can be a heading and three rows; a contract uses the lot. Parts stack in the order you write them, each ruled off the next.
import {
Paper,
PaperBox,
PaperCaption,
PaperCheckItem,
PaperChecklist,
PaperField,
PaperFooter,
PaperHeader,
PaperHeaderAside,
PaperHeading,
PaperNote,
PaperRow,
PaperSeal,
PaperSection,
PaperSignature,
PaperSignatures,
PaperSubtitle,
PaperTable,
PaperTableCell,
PaperTableHead,
PaperText,
PaperTitle,
} from "@/components/ui/paper"
<Paper>
{/* The letterhead: a seal, the centered heading, an aside of boxes */}
<PaperHeader>
<PaperSeal>{/* an img or an svg */}</PaperSeal>
<PaperHeading>
<PaperTitle />
<PaperSubtitle />
<PaperCaption />
</PaperHeading>
<PaperHeaderAside>
<PaperBox label="Number" />
</PaperHeaderAside>
</PaperHeader>
{/* The body: bands, rows of fields, copy, tables, checklists */}
<PaperSection />
<PaperRow>
<PaperField label="Label" grow={2} />
</PaperRow>
<PaperText />
<PaperTable>
<PaperTableHead />
<PaperTableCell />
</PaperTable>
<PaperNote />
<PaperChecklist>
<PaperCheckItem checked />
</PaperChecklist>
{/* Signature lines, side by side, and the small print */}
<PaperSignatures>
<PaperSignature label="Signature" name="Name" date="Date" />
</PaperSignatures>
<PaperFooter />
</Paper>A document is not responsive. The sheet is laid out at one design width, width in em (52 by default, 832px at a 16px root), and with fit it scales as a whole to the width it is given: the title stays on one line, a label never breaks in two and the copy keeps its line breaks, at any size. The scale is the sheet's own font size, and every length inside is in em, so the page is laid out again for real rather than zoomed. It never grows past its design size: in a wider column it sits centered.
City of Bayview
City of Bayview
fit={false} keeps the design width whatever the room, and the frame decides what happens to the overflow: here it scrolls sideways. frameClassName styles that frame, the div the sheet measures; padding on it is room the sheet keeps clear, for its shadow.
City of Bayview
PaperHeader lays out a PaperSeal, the PaperHeading and a PaperHeaderAside, in that order. The seal and the aside are boxes of the same width, so the heading between them is centered on the sheet, not on the room they leave. The title wraps rather than run under the boxes; captions stack under the subtitle. The seal takes any img or svg: the Seal on these pages is a small inline SVG of the docs' own, drawn in var(--paper-accent) so it follows the sheet's accent.
Bayview County Sheriff's Office
Patrol Division · Form IR-2
Confidential when completed
Drop the seal or the aside and keep the other side's box empty (<PaperSeal /> or <PaperHeaderAside />) to hold the heading on the sheet's axis, or drop both and give the heading the whole row.
A PaperRow is a ruled line of PaperFields: a pre-printed label over a typed-in value. grow is a field's share of the row, a flex factor. When a label wraps onto two lines the values in the row still sit on one line, and a value too long for its cell ends in an ellipsis. PaperBox is the same pair boxed on its own, for the aside: a number, a status, a date.
Department of Motor Vehicles · Form REG-343
PaperText is copy someone reads: terms, a statement, a narrative. It is justified in the serif and keeps the line breaks it was typed with, so text straight from a textarea or a database lands as it was written, paragraphs and numbered clauses included.
PaperTable is a plain table: write the thead and tbody yourself with PaperTableHead, printed like a field label, and PaperTableCell, typed in. Widths are yours too; percentages hold at every scale. When a section has nothing to list, PaperNote says so in place of an empty table.
| Item | Description | Qty | Logged by |
|---|---|---|---|
| E-01 | Padlock, shackle cut, from the east loading door | 1 | Dep. A. Moreno |
| E-02 | Photographs of the door, the lock and bay 14 | 12 | Dep. A. Moreno |
| E-03 | Security camera footage, east yard, 22:00 to 23:00 | 1 | Sgt. R. Okafor |
No witnesses recorded.
PaperChecklist is a list of printed boxes and PaperCheckItem one line of it, ticked with checked. It records what was done; nothing on the sheet can be toggled, so the box is drawn, not a control.
Documents not ticked are due before the start date.
PaperSignatures sets PaperSignature blocks side by side: a label, the mark over a line, and the name and date under it. The mark is the children: a stored signature as an img (here the SVG a SignaturePad submits), a Signature, or a live SignaturePad given absolute inset-0 so it takes the whole box. Sign the right-hand line: the pad maps the pointer with getBoundingClientRect(), which the sheet's scale never skews, and the date fills in when the first stroke lands. The app's own controls, like Clear here, sit outside the sheet. Left empty, a line waits to be signed.
Northwind Freight Co.
The sheet's inks are its own roles, derived from the light theme's tokens: --paper-ink, --paper-muted, --paper-rule, --paper-label (the label tint) and --paper-accent (the title, the section bands and anything you paint with it, like the seal here). The accent defaults to a letterhead navy; set any of them per sheet with a utility.
Bayview Community College
Continuing Education · 48 contact hours
Paper is built to hold in old embedded Chromium too, the kind a desktop or game shell ships (Chromium 10x): no container queries, no :has() and no standalone translate or scale anywhere. The fit is one ResizeObserver and one font size; without the observer the sheet is measured once, on mount. Because nothing is zoomed or transformed, a control set into the paper reads its pointer and its box as it would anywhere else, and the type is rasterized at the size it is shown, crisp.
The sheet, an <article> scoped .light, inside the frame it scales into (a <div>). Every article prop goes to the sheet. Every part forwards its element's props and merges className last; paperVariants is the recipe.
| Prop | Type | Default | Description |
|---|---|---|---|
| width | number | 52 | The design width in em of the root type size (832px at 16px). Also set as --paper-width. |
| fit | boolean | true | Scales the sheet down to the frame's width, never up. The scale is set as --paper-scale; off, it stays 1 and the container decides what happens to the overflow. |
| frameClassName | string | - | Classes for the frame (the sheet itself takes className). |
| className | string | - | Merged last on the sheet, where the --paper-* roles live. |
| Data attribute | Description |
|---|---|
| data-slot | "paper" on the sheet, "paper-frame" on the frame. |
| CSS variable | Description |
|---|---|
| --paper-width | The design width, from width. |
| --paper-scale | The fit's scale, from 0 to 1: the sheet's font size, which every length inside is measured in. |
| --paper-ink | The type. The light theme's --foreground. |
| --paper-muted | Labels, captions and notes. The light theme's --body. |
| --paper-rule | Every rule and box edge. |
| --paper-label | The ground of a pre-printed label and the footer. |
| --paper-accent | The title and the section bands: a deep navy, ink with a little --info in it. |
| --paper-accent-foreground | The type on a section band. |
The letterhead row, a <header>: a seal, the heading and an aside of boxes, in that order.
| Data attribute | Description |
|---|---|
| data-slot | "paper-header". |
A seal or logo (an img or an svg child), in a <div> boxed as wide as PaperHeaderAside, so the heading is centered on the sheet.
| Data attribute | Description |
|---|---|
| data-slot | "paper-seal". |
The centered column, a <div>, for the title, the subtitle and the captions.
| Data attribute | Description |
|---|---|
| data-slot | "paper-heading". |
The document's title, an <h2>, in small caps and the accent ink. A very long word wraps anywhere.
| Data attribute | Description |
|---|---|
| data-slot | "paper-title". |
The issuing body, a <p> in italics under the title.
| Data attribute | Description |
|---|---|
| data-slot | "paper-subtitle". |
A line of small print under the title (a jurisdiction, a form number), a <p>. Several stack tight.
| Data attribute | Description |
|---|---|
| data-slot | "paper-caption". |
The column on the right of the letterhead, a <div>, for PaperBoxes (a number, a status).
| Data attribute | Description |
|---|---|
| data-slot | "paper-header-aside". |
A boxed field with its label on top, a <div>: a reference number, a status, a date.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | React.ReactNode | - | The pre-printed label. |
| children | React.ReactNode | - | The typed-in value. |
| Data attribute | Description |
|---|---|
| data-slot | "paper-box", with "paper-box-label" and "paper-box-value" inside. |
The band that heads a part of the form ("Parties", "Terms"), an <h3>.
| Data attribute | Description |
|---|---|
| data-slot | "paper-section". |
A row of PaperFields, a <div> ruled underneath.
| Data attribute | Description |
|---|---|
| data-slot | "paper-row". |
One labelled cell of a PaperRow, a <div>. The value is pinned to the bottom, so every value in a row sits on one line when a label wraps.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | React.ReactNode | - | The pre-printed label. |
| children | React.ReactNode | - | The typed-in value, on one line, ending in an ellipsis when it runs long. |
| grow | number | 1 | The field's share of the row, a flex grow factor. |
| Data attribute | Description |
|---|---|
| data-slot | "paper-field", with "paper-field-label" and "paper-field-value" inside. |
Running copy (terms, a narrative), a <div>, justified, keeping the line breaks it was typed with.
| Data attribute | Description |
|---|---|
| data-slot | "paper-text". |
A ruled <table>. Compose a plain thead/tbody with PaperTableHead and PaperTableCell.
| Data attribute | Description |
|---|---|
| data-slot | "paper-table". |
A column heading, a <th>, printed like a field label.
| Data attribute | Description |
|---|---|
| data-slot | "paper-table-head". |
A typed-in cell, a <td>.
| Data attribute | Description |
|---|---|
| data-slot | "paper-table-cell". |
What a section says when it has nothing to show ("No evidence logged."), a <p>.
| Data attribute | Description |
|---|---|
| data-slot | "paper-note". |
A list of printed check boxes, a <ul>.
| Data attribute | Description |
|---|---|
| data-slot | "paper-checklist". |
One line of a PaperChecklist, an <li>: the box, ticked or not, and its text. The box is decorative; the item's text carries the meaning.
| Prop | Type | Default | Description |
|---|---|---|---|
| checked | boolean | false | Ticks the printed box. |
| Data attribute | Description |
|---|---|
| data-slot | "paper-check-item", with "paper-check-box" on the box. |
| data-checked | Present when checked. |
The row of signature blocks, side by side, a <div>.
| Data attribute | Description |
|---|---|
| data-slot | "paper-signatures". |
One signature block, a <div>: its label, the mark over a line, and the name and date under it.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | React.ReactNode | - | What the line is for ("Signature of the issuer"), above it. |
| name | React.ReactNode | - | Who signs, printed under the line. |
| date | React.ReactNode | - | The date, printed under the line at the right. |
| children | React.ReactNode | - | The mark above the line: an img, a Signature, a typed name, or a SignaturePad with absolute inset-0. Empty, the line waits to be signed. |
| Data attribute | Description |
|---|---|
| data-slot | "paper-signature", with "paper-signature-label", "paper-signature-mark" and "paper-signature-meta" inside. |
The strip at the foot of the sheet, a <footer>: the form's small print, spread edge to edge.
| Data attribute | Description |
|---|---|
| data-slot | "paper-footer". |