A familiar foundation.
A working reference for interfaces that feel like Privy. Built from the components in Official Account Hermod.
One language.
Every interaction.
Warm surfaces. Clear hierarchy. Confident actions. Explore the foundations and reusable patterns behind our workspace.
Labels, status and actions explain what happens next.
Beige and burgundy bring identity to daily work.
Keyboard, small screens and errors are part of the design.
Getting started
Open the catalogue, choose a pattern, and keep your changes documented.
New to the design system?
Follow the quick guide to explore components, preview full pages, and reuse the project's visual rules.
Updating a component?
Update its specimen and guideline together, then run synchronization and the relevant checks.
Color & geometry
Primitive → semantic → component. Change a role once, and every component follows.
--pv-red#e60034--pv-burgundy#430a23--pv-beige#f2e8dd--pv-canvasvar(--pv-warm-50)--pv-surfacevar(--pv-white)--pv-mutedvar(--pv-warm-100)--pv-textvar(--pv-warm-900)--pv-secondaryvar(--pv-warm-600)Semantic states
Accepted means received for processing. It does not mean delivered.
Space & shape
Spacing: 4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 px.
View HTML Copy this specimen
<div class="swatch-grid"><div class="swatch"><div style="background:var(--pv-red)" aria-hidden="true"></div><strong>Privy Red</strong><code>--pv-red</code><small>#e60034</small></div><div class="swatch"><div style="background:var(--pv-burgundy)" aria-hidden="true"></div><strong>Heartful Burgundy</strong><code>--pv-burgundy</code><small>#430a23</small></div><div class="swatch"><div style="background:var(--pv-beige)" aria-hidden="true"></div><strong>Clear Beige</strong><code>--pv-beige</code><small>#f2e8dd</small></div><div class="swatch"><div style="background:var(--pv-canvas)" aria-hidden="true"></div><strong>Canvas</strong><code>--pv-canvas</code><small>var(--pv-warm-50)</small></div><div class="swatch"><div style="background:var(--pv-surface)" aria-hidden="true"></div><strong>Surface</strong><code>--pv-surface</code><small>var(--pv-white)</small></div><div class="swatch"><div style="background:var(--pv-muted)" aria-hidden="true"></div><strong>Muted</strong><code>--pv-muted</code><small>var(--pv-warm-100)</small></div><div class="swatch"><div style="background:var(--pv-text)" aria-hidden="true"></div><strong>Primary text</strong><code>--pv-text</code><small>var(--pv-warm-900)</small></div><div class="swatch"><div style="background:var(--pv-secondary)" aria-hidden="true"></div><strong>Secondary text</strong><code>--pv-secondary</code><small>var(--pv-warm-600)</small></div></div><div class="grid two"><article class="specimen"><h3>Semantic states</h3><div class="row wrap"><span class="badge success"><span class="dot" aria-hidden="true"></span>Confirmed</span><span class="badge info"><span class="dot" aria-hidden="true"></span>Accepted</span><span class="badge warning"><span class="dot" aria-hidden="true"></span>Unknown</span><span class="badge error"><span class="dot" aria-hidden="true"></span>Rejected</span></div><p class="muted">Accepted means received for processing. It does not mean delivered.</p></article><article class="specimen"><h3>Space & shape</h3><div class="row wrap shape-samples"><span class="shape pill">999px · control</span><span class="shape card-shape">16px · card</span><span class="shape dialog-shape">18px · dialog</span></div><p class="muted">Spacing: 4 / 8 / 12 / 16 / 20 / 24 / 32 / 48 px.</p></article></div>Typography & content
DM Sans is the implemented project font. The older brand proposal names Google Sans Flex; this kit follows the running application.
Make the next step clear.
A little structure goes a long way.
Everything in its right place.
Use familiar words, short sentences, and a useful next action.
Inline semantics
Important, emphasized, highlighted, outdated, removed, added, H2O, 102, API, request_id, ⌘ + K.
Show the outcome the service can actually confirm.Product interface principle
Saved as a draft
is clearer than a vague success message.
Macro: a reusable reply. . 25 tickets. PrivyID. ABC. 日.
Structured content
- Use one primary action.
- Keep labels visible.
- Review the content.
- Confirm the recipient.
- Owner
- Example workspace
- State
- Draft
support@example.com
status: "accepted"
request_id: "example-id"Ready to preview · n recipients · Read usage guidance
View HTML Copy this specimen
<div class="specimen type-scale"><div><span class="eyebrow">PAGE · 24 / 32 · 600</span><p class="type-page">Make the next step clear.</p></div><div><span class="eyebrow">SECTION · 20 / 28 · 600</span><p class="type-section">A little structure goes a long way.</p></div><div><span class="eyebrow">SUBHEADING · 16 / 24 · 600</span><p class="type-subheading">Everything in its right place.</p></div><div><span class="eyebrow">BODY · 14 / 20 · 400</span><p>Use familiar words, short sentences, and a useful next action.</p></div><div><span class="eyebrow">METADATA · 12 / 18 · 400</span><small>Updated 26 October 2026 at 10:30</small></div></div>
<div class="grid two"><article class="specimen prose"><h3>Inline semantics</h3><p><strong>Important</strong>, <em>emphasized</em>, <mark>highlighted</mark>, <s>outdated</s>, <del>removed</del>, <ins>added</ins>, H<sub>2</sub>O, 10<sup>2</sup>, <abbr title="Application Programming Interface">API</abbr>, <code>request_id</code>, <kbd>⌘</kbd> + <kbd>K</kbd>.</p><blockquote>Show the outcome the service can actually confirm.<cite>Product interface principle</cite></blockquote><p><q>Saved as a draft</q> is clearer than a vague success message.</p><p><dfn>Macro</dfn>: a reusable reply. <time datetime="2026-10-26">26 October 2026</time>. <data value="25">25 tickets</data>. <bdi>PrivyID</bdi>. <bdo dir="rtl">ABC</bdo>. <ruby>日<rt>day</rt></ruby>.</p></article><article class="specimen prose"><h3>Structured content</h3><ul><li>Use one primary action.</li><li>Keep labels visible.</li></ul><ol><li>Review the content.</li><li>Confirm the recipient.</li></ol><dl><dt>Owner</dt><dd>Example workspace</dd><dt>State</dt><dd>Draft</dd></dl><address>Example support team<br>support@example.com</address><pre><code>status: "accepted"
request_id: "example-id"</code></pre><p><samp>Ready to preview</samp> · <var>n</var> recipients · <a href="#guidance">Read usage guidance</a></p></article></div>Buttons & iconography
Match heights within an action group. Reserve the red primary action for the main next step.
Action hierarchy
Sizes & states
Project icon set
View HTML Copy this specimen
<div class="grid two"><article class="specimen"><h3>Action hierarchy</h3><div class="row wrap"><button type="button" class="button primary" data-open="account-dialog">Create account</button><button type="button" class="button secondary" data-toast="Demo action cancelled.">Cancel</button><button type="button" class="button outline" data-toast="Demo data is up to date."><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12h-4l-3 9-6-18-3 9H2"/></svg> Sync Data</button><button type="button" class="button ghost" data-toast="See the Guidelines section for usage rules.">Learn more <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></button><button type="button" class="button danger" data-open="confirm-dialog"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7"/></svg> Delete</button></div></article><article class="specimen"><h3>Sizes & states</h3><div class="row wrap"><button type="button" class="button secondary compact" data-toast="Compact: 36px minimum height.">Compact</button><button type="button" class="button primary" data-toast="Default: 44px minimum height.">Default · 44px</button><button type="button" class="button primary" disabled>Disabled</button><button type="button" class="button primary" disabled aria-busy="true"><span class="spinner" aria-hidden="true"></span> Saving…</button></div><div class="row wrap"><button type="button" class="button icon-button" aria-label="Add item" data-toast="Demo item added."><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14 M5 12h14"/></svg></button><button type="button" class="button icon-button" aria-label="Copy reference" data-copy="PRIVY-EXAMPLE-001"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 8h12v13H8z M16 8V3H4v13h4"/></svg></button><span class="muted">Icon-only controls need an accessible name.</span></div></article></div><article class="specimen"><h3>Project icon set</h3><div class="icon-grid"><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v12H9l-5 4z M8 9h8 M8 13h5"/></svg><small>support</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 11.5a8.4 8.4 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.4 8.4 0 0 1-3.8-.9L3 21l1.9-5.7a8.4 8.4 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.4 8.4 0 0 1 3.8-.9h.5a8.5 8.5 0 0 1 8 8z"/></svg><small>comment</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 11h14v10H5z M8 11V7a4 4 0 1 1 8 0v4 M12 15v2"/></svg><small>lock</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h18v16H3z M9 4v16 M15 9l-3 3 3 3"/></svg><small>panel-left-close</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h18v16H3z M9 4v16 M15 9l3 3-3 3"/></svg><small>panel-left-open</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3H5a2 2 0 0 0-2 2v3 M16 3h3a2 2 0 0 1 2 2v3 M21 16v3a2 2 0 0 1-2 2h-3 M3 16v3a2 2 0 0 0 2 2h3"/></svg><small>maximize</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3v5H3 M16 3v5h5 M21 16h-5v5 M3 16h5v5"/></svg><small>minimize</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18 M8 16v-5 M13 16V6 M18 16V9"/></svg><small>chart</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12h-4l-3 9-6-18-3 9H2"/></svg><small>activity</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9 M10 21h4"/></svg><small>bell</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9z"/></svg><small>zap</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0 M8 14s1.5 2 4 2 4-2 4-2 M9 9h.01 M15 9h.01"/></svg><small>smile</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0 M8 16s1.5-2 4-2 4 2 4 2 M9 9h.01 M15 9h.01"/></svg><small>frown</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20 M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2Z"/></svg><small>book</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 10 9-7 9 7 M5 9v12h14V9 M9 21v-7h6v7"/></svg><small>home</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2 M16 3a4 4 0 0 1 0 8 M22 21v-2a4 4 0 0 0-3-3.87 M13 7a4 4 0 1 1-8 0 4 4 0 0 1 8 0"/></svg><small>accounts</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 11 18-7v16L3 14z M7 15l2 6h4l-3-7 M3 11v3"/></svg><small>broadcast</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14 M5 12h14"/></svg><small>plus</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg><small>arrow</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 12H5 M11 6l-6 6 6 6"/></svg><small>back</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 21l-5-5 M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0"/></svg><small>search</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg><small>check</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg><small>chevron</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg><small>down</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 22V4h12v18 M16 10h4v12 M8 8h4 M8 12h4 M8 16h4 M2 22h20"/></svg><small>building</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7h18v14H3z M8 7V4h8v3 M3 12h18 M10 12v2h4v-2"/></svg><small>briefcase</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 16v-4 M12 8h.01 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg><small>info</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 18h.01 M9.5 9a2.5 2.5 0 1 1 4.3 1.8c-.9.9-1.8 1.2-1.8 2.7 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg><small>help</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 3 8 4v6c0 5-8 9-8 9s-8-4-8-9V7z m-4 9 3 3 5-5"/></svg><small>shield</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 3 5 5-12 12-6 1 1-6z M14 5l5 5"/></svg><small>edit</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 6v6l4 2 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg><small>clock</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3H3v18h6 M9 12h12 M17 8l4 4-4 4"/></svg><small>logout</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16 M4 12h16 M4 18h16"/></svg><small>menu</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5h.01 M12 12h.01 M12 19h.01"/></svg><small>more-vertical</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8 M12 2v2 M12 20v2 M2 12h2 M20 12h2 M5 5l1.5 1.5 M17.5 17.5 19 19 M5 19l1.5-1.5 M17.5 6.5 19 5"/></svg><small>sun</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 13a9 9 0 1 1-10-10 7 7 0 0 0 10 10Z"/></svg><small>moon</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H5v20h14V7z M14 2v5h5 M8 12h8 M8 16h6"/></svg><small>file</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 8h12v13H8z M16 8V3H4v13h4"/></svg><small>copy</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16v16H4z M4 15l5-5 4 4 3-3 4 4 M14 8h.01"/></svg><small>image</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h18v16H3z M10 8l6 4-6 4z"/></svg><small>video</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l12-2v13 M9 18a3 3 0 1 1-6 0 3 3 0 0 1 6 0 M21 16a3 3 0 1 1-6 0 3 3 0 0 1 6 0"/></svg><small>audio</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8 5 12 7-12 7z"/></svg><small>play</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg><small>close</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7"/></svg><small>trash</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h7a4 4 0 0 1 0 8H6z M6 12h8a4 4 0 0 1 0 8H6z"/></svg><small>bold</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4h10 M4 20h10 M15 4 9 20"/></svg><small>italic</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4v7a6 6 0 0 0 12 0V4 M4 21h16"/></svg><small>underline</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 5c-2-2-9-2-10 2s3 5 5 5 M7 17c2 3 10 3 10-1 M3 12h18"/></svg><small>strike</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8 7-5 5 5 5 M16 7l5 5-5 5"/></svg><small>code</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3h18v18H3z m7 5-4 4 4 4 m4-8 4 4-4 4"/></svg><small>code-block</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6h12 M9 12h12 M9 18h12 M3 6h.01 M3 12h.01 M3 18h.01"/></svg><small>bullets</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 6h11 M10 12h11 M10 18h11 M3 3h2v6 M3 9h4 M3 14c0-2 4-2 4 0s-4 3-4 5h4"/></svg><small>numbered</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m10 13 4-4 M8 16l-1 1a4 4 0 0 1-6-6l4-4a4 4 0 0 1 6 0 M13 7l1-1a4 4 0 0 1 6 6l-4 4a4 4 0 0 1-6 0"/></svg><small>link</small></div><div><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16 M12 4v12 M8 20h5 m3-4 5 5 m0-5-5 5"/></svg><small>clear-format</small></div></div></article>Inputs & forms
Single-line controls use a full pill radius. Textareas keep a readable 20px radius and can grow vertically.
Field states
Search, password & upload
View HTML Copy this specimen
<form class="specimen" id="sample-form" novalidate><div class="row spread"><h3>Contact details</h3><span class="muted">Required fields are marked *</span></div><div class="grid two"><div class="field"><label for="contact-name">Full name <span class="required" aria-hidden="true">*</span></label><input id="contact-name" name="name" autocomplete="name" required placeholder="Enter full name" aria-describedby="name-error"><small id="name-error" class="field-error" hidden>Enter a full name.</small></div><div class="field"><label for="contact-email">Email address <span class="required" aria-hidden="true">*</span></label><input id="contact-email" name="email" type="email" autocomplete="email" required placeholder="name@example.com" aria-describedby="email-error"><small id="email-error" class="field-error" hidden>Enter a valid email address.</small></div><div class="field"><label for="field-phone-number">Phone number</label><input id="field-phone-number" type="tel" autocomplete="tel" placeholder="08… or +62…"></div><div class="field"><label for="field-website">Website</label><input id="field-website" type="url" placeholder="https://example.com"></div></div><div class="field"><label for="message">Message <span class="optional">Optional</span></label><textarea id="message" name="message" rows="5" placeholder="Add context for the team…"></textarea><small>Do not include passwords or one-time passcodes.</small></div><div class="form-actions"><button type="reset" class="button secondary">Reset</button><button type="submit" class="button primary">Save details</button></div><p class="form-result" id="form-result" role="status"></p></form>
<div class="grid two"><article class="specimen"><h3>Field states</h3><div class="field"><label for="field-default">Default</label><input id="field-default" type="text" placeholder="Enter a value"></div><div class="field"><label for="field-read-only">Read only</label><input id="field-read-only" type="text" value="PRIVY001" readonly></div><div class="field"><label for="field-disabled">Disabled</label><input id="field-disabled" type="text" value="Unavailable" disabled></div><div class="field"><label for="invalid-email">Error state</label><input id="invalid-email" type="email" value="not-an-email" aria-invalid="true" aria-describedby="invalid-help"><small class="field-error" id="invalid-help">Use an address such as name@example.com.</small></div></article><article class="specimen"><h3>Search, password & upload</h3><div class="field"><label for="search-example">Search</label><div class="search"><span><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 21l-5-5 M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0"/></svg></span><input id="search-example" type="search" placeholder="Search ticket, PrivyID, or name…"></div></div><div class="field"><label for="password">Password</label><div class="password-control"><input id="password" type="password" autocomplete="new-password" value="sample-password"><button type="button" class="icon-button" id="toggle-password" aria-label="Show password" aria-pressed="false"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 11h14v10H5z M8 11V7a4 4 0 1 1 8 0v4 M12 15v2"/></svg></button></div></div><div class="field"><label for="upload">NPWP document</label><input id="upload" type="file" accept=".pdf,.jpg,.png"><small id="upload-name">PDF, JPG, or PNG. Local preview only; no upload.</small></div><label class="field">Hidden input<input type="hidden" name="example-token" value="demo-only"><small>Hidden values are not authorization.</small></label></article></div>Selection & native controls
Use native controls first, with visible labels and a consistent focus treatment.
Date, time & numeric inputs
Native picker appearance varies by browser. Always state the scheduling timezone in production.
View HTML Copy this specimen
<div class="grid two"><article class="specimen"><div class="field"><label for="business-field">Business field</label><select id="business-field"><option value="">Select business field</option>Technology</option>Financial services</option>Retail</option></select></div><div class="field"><label for="grouped-select">Assign team</label><select id="grouped-select"><optgroup label="Customer operations"><option>Customer Support</option><option>Enterprise Support</option></optgroup><optgroup label="Engineering"><option>Platform</option></optgroup></select></div><div class="field"><span class="field-label">Topics <span class="optional">Multiple selection</span></span><div id="multi-select" class="topic-choices" role="group" aria-label="Topics, multiple selection"><label class="topic-choice"><input type="checkbox" name="topics" value="Account"><span>Account</span></label><label class="topic-choice"><input type="checkbox" name="topics" value="Verification"><span>Verification</span></label><label class="topic-choice"><input type="checkbox" name="topics" value="Document signing"><span>Document signing</span></label></div></div><div class="field"><label for="city">City autocomplete</label><input id="city" list="cities" placeholder="Type a city"><datalist id="cities"><option value="Jakarta"><option value="Yogyakarta"><option value="Surabaya"></datalist></div></article><article class="specimen"><fieldset><legend>Notification preferences</legend><label class="choice"><input type="checkbox" checked> Email notifications</label><label class="choice"><input type="checkbox"> Browser notifications</label><label class="choice"><input type="checkbox" disabled> SMS notifications unavailable</label></fieldset><fieldset><legend>Audience</legend><label class="choice"><input type="radio" name="audience" checked> All enterprise members</label><label class="choice"><input type="radio" name="audience"> Selected members</label></fieldset><label class="switch"><input type="checkbox" role="switch" checked><span aria-hidden="true"></span> Enable e-seal and stamp feature</label><div class="field range-field"><label for="volume">Volume <output for="volume" id="volume-value">60%</output></label><input id="volume" type="range" min="0" max="100" value="60"></div><div class="field"><label for="accent">Accent preview</label><input id="accent" type="color" value="#e60034"><small>Preview only; brand tokens remain unchanged.</small></div></article></div><article class="specimen"><h3>Date, time & numeric inputs</h3><div class="grid three"><div class="field"><label for="field-quantity">Quantity</label><input id="field-quantity" type="number" min="1" max="500" value="25"></div><div class="field"><label for="field-date">Date</label><input id="field-date" type="date" value="2026-10-26"></div><div class="field"><label for="field-time">Time</label><input id="field-time" type="time" value="10:30"></div><div class="field"><label for="field-schedule">Schedule</label><input id="field-schedule" type="datetime-local" value="2026-10-26T10:30"></div><div class="field"><label for="field-month">Month</label><input id="field-month" type="month" value="2026-10"></div><div class="field"><label for="field-week">Week</label><input id="field-week" type="week" value="2026-W44"></div></div><p class="muted">Native picker appearance varies by browser. Always state the scheduling timezone in production.</p></article>Tables, cards & identity
Keep the object, owner, status, and next action visible. Use a scroll region for tables on small screens.
Enterprise accounts
3 accounts| Company | PrivyID | Status | Action |
|---|---|---|---|
| NSNusantara Studio | NS001 | Active | |
| PIPT Innovation | PI002 | Pending | |
| ADAwan Digital | AD003 | Inactive | |
| Sample data · no connected accounts | |||
Get started with your account
Help customers access their workspace and find the right team.
Read article
24Active
Open tickets
An example metric, not live reporting.
Avatars & chips
View HTML Copy this specimen
<article class="specimen table-specimen"><div class="row spread"><h3>Enterprise accounts</h3><span class="badge neutral"><span class="dot" aria-hidden="true"></span>3 accounts</span></div><div class="table-scroll" role="region" aria-label="Enterprise account table" tabindex="0"><table><caption class="sr-only">Example enterprise accounts and status</caption><thead><tr><th scope="col">Company</th><th scope="col">PrivyID</th><th scope="col">Status</th><th scope="col">Action</th></tr></thead><tbody><tr><th scope="row"><span class="identity"><span class="avatar small">NS</span>Nusantara Studio</span></th><td><code>NS001</code></td><td><span class="badge success"><span class="dot" aria-hidden="true"></span>Active</span></td><td><button type="button" class="button ghost compact" data-toast="Demo account: Nusantara Studio.">View <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></button></td></tr><tr><th scope="row"><span class="identity"><span class="avatar small">PI</span>PT Innovation</span></th><td><code>PI002</code></td><td><span class="badge warning"><span class="dot" aria-hidden="true"></span>Pending</span></td><td><button type="button" class="button ghost compact" data-toast="Demo account: PT Innovation.">View <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></button></td></tr><tr><th scope="row"><span class="identity"><span class="avatar small">AD</span>Awan Digital</span></th><td><code>AD003</code></td><td><span class="badge neutral"><span class="dot" aria-hidden="true"></span>Inactive</span></td><td><button type="button" class="button ghost compact" data-toast="Demo account: Awan Digital.">View <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></button></td></tr></tbody><tfoot><tr><td colspan="4">Sample data · no connected accounts</td></tr></tfoot></table></div></article><div class="grid three"><article class="specimen resource-card"><span class="tile-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20 M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2Z"/></svg></span><span class="badge neutral"><span class="dot" aria-hidden="true"></span>Account</span><h3>Get started with your account</h3><p class="muted">Help customers access their workspace and find the right team.</p><hr><a href="#overlays">Read article <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></a></article><article class="specimen"><span class="eyebrow">YOUR WORKSPACE</span><p class="metric">24<span class="badge success">Active</span></p><h3>Open tickets</h3><p class="muted">An example metric, not live reporting.</p></article><article class="specimen"><h3>Avatars & chips</h3><div class="row wrap"><span class="avatar small">AL</span><span class="avatar">NS</span><span class="avatar large">PI</span></div><div class="row wrap"><span class="chip">Account <button type="button" class="button chip-close" aria-label="Remove Account filter" data-remove-chip><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></span><span class="chip">Verification <button type="button" class="button chip-close" aria-label="Remove Verification filter" data-remove-chip><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></span></div></article></div>Status & feedback
Use words as well as color. Errors explain recovery; unknown outcomes never pretend to be success.
Inline notices
Check the message before sending.
Your workspace details are up to date.
Refresh before retrying the action.
Check your connection and try again.
Progress & loading
65% completeNo tickets in this queue
New conversations will appear here.
Access required
Ask your workspace administrator for permission.
View guidanceView HTML Copy this specimen
<div class="grid two"><article class="specimen"><h3>Inline notices</h3><div class="notice info"><span><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 16v-4 M12 8h.01 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg></span><div><strong>Ready for review</strong><p>Check the message before sending.</p></div><button type="button" class="button icon-button" aria-label="Dismiss Ready for review notice" data-dismiss><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></div><div class="notice success"><span><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 4 4L19 6"/></svg></span><div><strong>Changes saved</strong><p>Your workspace details are up to date.</p></div><button type="button" class="button icon-button" aria-label="Dismiss Changes saved notice" data-dismiss><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></div><div class="notice warning"><span><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 6v6l4 2 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg></span><div><strong>Outcome unconfirmed</strong><p>Refresh before retrying the action.</p></div><button type="button" class="button icon-button" aria-label="Dismiss Outcome unconfirmed notice" data-dismiss><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></div><div class="notice error"><span><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 16v-4 M12 8h.01 M22 12a10 10 0 1 1-20 0 10 10 0 0 1 20 0"/></svg></span><div><strong>Could not load data</strong><p>Check your connection and try again.</p></div><button type="button" class="button icon-button" aria-label="Dismiss Could not load data notice" data-dismiss><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12 M18 6 6 18"/></svg></button></div></article><article class="specimen"><h3>Progress & loading</h3><label for="upload-progress">Uploading document</label><progress id="upload-progress" value="65" max="100">65%</progress><small>65% complete</small><label for="capacity">Account capacity</label><meter id="capacity" value="75" min="0" max="100" low="30" high="85" optimum="50">75 of 100</meter><div class="skeleton" role="status" aria-label="Loading example"><span class="skeleton-circle"></span><span><i></i><i></i></span></div><button type="button" class="button secondary" data-toast="Changes saved. This is a demo notification.">Show notification</button></article></div><div class="grid two"><article class="specimen empty-state"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v12H9l-5 4z M8 9h8 M8 13h5"/></svg><h3>No tickets in this queue</h3><p>New conversations will appear here.</p><button type="button" class="button primary" data-open="account-dialog"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14 M5 12h14"/></svg> Create ticket</button></article><article class="specimen empty-state"><span class="tile-icon"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 11h14v10H5z M8 11V7a4 4 0 1 1 8 0v4 M12 15v2"/></svg></span><h3>Access required</h3><p>Ask your workspace administrator for permission.</p><a class="button secondary" href="#guidance">View guidance</a></article></div>Broadcast batch results
Partially acceptedSome recipients were accepted for processing and others were rejected. Acceptance does not confirm delivery or reading. Do not automatically resend the entire audience.
| Batch | Attempted | Status | Rejected recipients |
|---|---|---|---|
| 1 | 100 | Accepted | 0 |
| 2 | 2 | Rejected | 2 |
View rejected recipients
- DEMO001 · HTTP 404 · Actor not found
- DEMO002 · HTTP 404 · Actor not found
Broadcast delivery results
The broadcast request was accepted for processing. This does not confirm delivery or read status.
Request activity
- Submitting request…28 Oct 2026, 12:03:25 WIB (UTC+7)
- Accepted for processing28 Oct 2026, 12:03:26 WIB (UTC+7)
- Request completedDelivery request28 Oct 2026, 12:03:30 WIB (UTC+7)
The delivery request failed. Request acceptance is separate; review the sending figures before deciding what to do next.
Refresh failed. These figures are from the last successful check.
Delivery progress could not be retrieved. Refresh to check again; the saved request remains available.
- Total recipients
- 0
- Sent
- 0
- Failed to send
- 0
- Delivered
- 0
- Read
- 0
29 / 29 recipients processed.
No recipients have been reported by this request.
- Started
- 28 Oct 2026, 12:03:26 WIB
- Completed
- 28 Oct 2026, 12:03:30 WIB
- Last checked
- 28 Oct 2026, 12:10:02 WIB
Local fixture only. In the application, poll queued/running jobs while visible and online; refresh receipts manually after completion. Missing delivery never becomes zero counts and never hides the saved request.
Dialogs & disclosure
Dialogs share the same shell, action spacing, and close control. Use full width for content authoring.
Form dialog
Rounded fields, inline required marks, 24px action spacing.
Content authoring
Viewport minus 32px, with a taller content field.
Confirmation
Explain the action. Keep Cancel easy to reach.
Logout confirmation
Confirm before leaving an account. Cancel keeps the current session.
Demo only. No real session will end.
Accordion
When should I use a modal?
For a focused task that can be completed without losing the current context.
How does keyboard focus work?
Move focus into the dialog, support Escape, and return focus to its trigger.
Action disclosure
More actions
Native disclosure keeps this menu available without a framework.
View HTML Copy this specimen
<div class="grid two"><article class="specimen"><span class="tile-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 3 5 5-12 12-6 1 1-6z M14 5l5 5"/></svg></span><h3>Form dialog</h3><p class="muted">Rounded fields, inline required marks, 24px action spacing.</p><button type="button" class="button primary" data-open="account-dialog">Create account</button></article><article class="specimen"><span class="tile-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20 M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2Z"/></svg></span><h3>Content authoring</h3><p class="muted">Viewport minus 32px, with a taller content field.</p><button type="button" class="button secondary" data-open="editor-dialog">Add knowledge article</button></article><article class="specimen"><span class="tile-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7"/></svg></span><h3>Confirmation</h3><p class="muted">Explain the action. Keep Cancel easy to reach.</p><button type="button" class="button outline" data-open="confirm-dialog">Delete example</button></article><article class="specimen" id="logout-confirmation"><span class="tile-icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 4H4v16h5 M9 12h12 m-4-4 4 4-4 4"/></svg></span><h3>Logout confirmation</h3><p class="muted">Confirm before leaving an account. Cancel keeps the current session.</p><button type="button" class="button outline" data-open="logout-dialog" aria-haspopup="dialog" aria-controls="logout-dialog">Log out</button><p id="logout-demo-status" class="muted demo-note" role="status">Demo only. No real session will end.</p></article></div><div class="grid two"><article class="specimen"><h3>Accordion</h3><details class="accordion"><summary>When should I use a modal?</summary><p>For a focused task that can be completed without losing the current context.</p></details><details class="accordion"><summary>How does keyboard focus work?</summary><p>Move focus into the dialog, support Escape, and return focus to its trigger.</p></details></article><article class="specimen"><h3>Action disclosure</h3><details class="action-disclosure"><summary>More actions <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></summary><div><button type="button" class="button ghost" data-toast="Example duplicated locally."><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 8h12v13H8z M16 8V3H4v13h4"/></svg> Duplicate</button><button type="button" class="button ghost" data-open="account-dialog"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m16 3 5 5-12 12-6 1 1-6z M14 5l5 5"/></svg> Rename</button></div></details><p class="muted">Native disclosure keeps this menu available without a framework.</p></article></div><dialog class="small-dialog" id="logout-dialog" aria-labelledby="logout-title" aria-describedby="logout-description logout-demo-note"><div class="dialog-header"><h2 id="logout-title">Log out of your account?</h2><button type="button" class="icon-button" data-close aria-label="Close logout confirmation"><svg class="close-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="m5 5 14 14 M19 5 5 19"/></svg></button></div><p id="logout-description">You will need to sign in again to access your workspace.</p><p id="logout-demo-note" class="muted demo-note">This is a preview. Your actual session will stay active.</p><form method="dialog" id="logout-form" class="form-actions"><button type="submit" class="button secondary" value="cancel" autofocus>Cancel</button><button type="submit" class="button primary" value="logout">Log out</button></form></dialog>Support composer
Macros sit to the left of the reply placeholder. Press / in an empty composer to open the macro picker.
Formatted message input
Select text, then choose a format.View HTML Copy this specimen
<article class="chat-demo"><div class="chat-header"><span class="avatar small">SJ</span><div><strong>Samantha Jasmine</strong><small>Example conversation</small></div><span class="badge info"><span class="dot" aria-hidden="true"></span>Open</span></div><div class="chat-history" id="chat-history" role="log" aria-label="Demo conversation"><div class="bubble">Hello, I need help accessing my account.<small>Customer · 10:30</small></div><div class="bubble outgoing">Hi Samantha, happy to help.<small>Agent · 10:31</small></div></div><form id="composer-form" class="composer-form"><div class="composer-box"><button type="button" class="icon-button macro-trigger" id="macro-trigger" aria-label="Insert a macro" aria-expanded="false" aria-controls="macro-picker"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2 3 14h9l-1 8 10-12h-9z"/></svg></button><label class="sr-only" for="reply">Reply to Samantha Jasmine</label><textarea id="reply" rows="3" placeholder="Reply to Samantha Jasmine…"></textarea><div class="macro-picker" id="macro-picker" hidden><strong>Insert a macro · review before sending</strong><button type="button" data-macro="Hello Samantha, thank you for contacting Privy. How can I help?">Welcome<small>Greeting</small></button><button type="button" data-macro="Your report has been received. Please share the error message and time of the issue. Do not share a password or OTP.">Confirm issue received<small>Acknowledge</small></button></div></div><div class="composer-footer"><span class="muted">Demo only · messages stay on this page</span><button type="submit" class="button primary">Send reply <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14 M13 6l6 6-6 6"/></svg></button></div></form></article>
<article class="specimen"><div class="row spread"><h3>Formatted message input</h3><span class="muted">Select text, then choose a format.</span></div><div class="editor-tools" role="group" aria-label="Message formatting"><button type="button" class="button icon-button" data-format="**" aria-label="Bold"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4h7a4 4 0 0 1 0 8H6z M6 12h8a4 4 0 0 1 0 8H6z"/></svg></button><button type="button" class="button icon-button" data-format="++" aria-label="Italic"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 4h10 M4 20h10 M15 4 9 20"/></svg></button><button type="button" class="button icon-button" data-format="__" aria-label="Underline"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 4v7a6 6 0 0 0 12 0V4 M4 21h16"/></svg></button><button type="button" class="button icon-button" data-format="~" aria-label="Strikethrough"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 5c-2-2-9-2-10 2s3 5 5 5 M7 17c2 3 10 3 10-1 M3 12h18"/></svg></button><button type="button" class="button icon-button" data-format="`" aria-label="Inline code"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m8 7-5 5 5 5 M16 7l5 5-5 5"/></svg></button><button type="button" class="button icon-button" data-format="list" aria-label="Bulleted list"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 6h12 M9 12h12 M9 18h12 M3 6h.01 M3 12h.01 M3 18h.01"/></svg></button><button type="button" class="button icon-button" data-format="clear" aria-label="Clear formatting"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16 M12 4v12 M8 20h5 m3-4 5 5 m0-5-5 5"/></svg></button></div><label class="sr-only" for="formatted-message">Formatted message</label><textarea id="formatted-message" rows="6">Hello **Samantha**, your report has been received.</textarea><small>Project markers: **bold**, ++italic++, __underline__, ~strike~, `code`.</small><div class="message-preview" id="message-preview" aria-label="Message preview"></div></article>Media & embedded content
Preserve aspect ratios, provide alternatives, and let the user start playback.

Audio & video controls
Audio description: two short, quiet tones. No speech.
Video shell only; no sample video is attached. Replace it with approved media and captions in a product.Canvas with an alternative
Example queue volume: Mon 12 · Tue 20 · Wed 16. Synthetic data.
Embedded content
Provide a title and a deliberate sandbox policy.View HTML Copy this specimen
<div class="grid two"><figure class="specimen media-figure"><picture><img src="assets/logo-privy.png" alt="Privy" width="970" height="257" loading="lazy"></picture><figcaption>Original project logo. Preserve its aspect ratio and colors.</figcaption></figure><article class="specimen"><h3>Audio & video controls</h3><audio id="sample-audio" controls preload="metadata" aria-label="Example two-tone audio"><source src="assets/sample-audio.wav" type="audio/wav">Your browser does not support audio playback.</audio><p class="muted">Audio description: two short, quiet tones. No speech.</p><video id="sample-video" controls playsinline preload="none" poster="assets/media-poster.svg" aria-label="Video control specimen"><track kind="captions" label="English" srclang="en" src="assets/sample.vtt" default>Your browser does not support video.</video><small>Video shell only; no sample video is attached. Replace it with approved media and captions in a product.</small></article></div><div class="grid two"><article class="specimen"><h3>Canvas with an alternative</h3><canvas id="sample-canvas" width="640" height="200" aria-label="Example queue volume: Monday 12, Tuesday 20, Wednesday 16">Queue volume: Monday 12, Tuesday 20, Wednesday 16.</canvas><p class="muted">Example queue volume: Mon 12 · Tue 20 · Wed 16. Synthetic data.</p></article><article class="specimen"><h3>Embedded content</h3><iframe title="Example embedded document" src="assets/embed.html" loading="lazy" sandbox></iframe><small>Provide a title and a deliberate sandbox policy.</small></article></div>HTML element coverage
Native elements are grouped by purpose. Deprecated tags and unrelated platform APIs are not presented as product components.
Native action inputs
Heading level four
Heading level five
Heading level six
Preserve semantic heading order when applying compact text sizes. Attention and alternate voice retain native semantics. LongReference
This inventory is generated from the actual HTML. Document infrastructure includes html, head, meta, title, link, style, script, and template. Semantic examples include section, article, aside, header, footer, main, nav, form, and address.
Specialized tags such as object/embed, image maps, MathML, and custom elements require a use case and separate implementation review. Native file/date/color/media control internals vary by browser.
View HTML Copy this specimen
<article class="specimen"><div id="element-reference"></div><p class="muted">This inventory is generated from the actual HTML. Document infrastructure includes html, head, meta, title, link, style, script, and template. Semantic examples include section, article, aside, header, footer, main, nav, form, and address.</p><noscript>Interactive demos require JavaScript. Forms, native controls, links, and this document remain readable.</noscript><template id="toast-template"><div class="toast"><span></span><button type="button" class="icon-button" aria-label="Dismiss notification">×</button></div></template><p class="muted">Specialized tags such as object/embed, image maps, MathML, and custom elements require a use case and separate implementation review. Native file/date/color/media control internals vary by browser.</p></article>Searchable dropdowns
Filter options with the keyboard and remove selections individually.
Assign a member
Topics with removable chips
Detail drawer
Keep context visible while viewing or editing a customer. Unsaved edits require confirmation.
Right panel on desktop; bottom sheet on mobile. Escape restores focus to the opener.
Interactive data table
Search, sort, filter, paginate, select rows, and archive selected demo records.
| Account ID | Status |
|---|
Compact page-heading summaries
Keep three to five labeled counts near the page title, without turning them into large dashboard cards.
Users
Manage workspace access and assigned roles.
All synthetic workspace users · Independent of table pagination and search. A user can have more than one role; counts are not a unique-user total.
Table pagination & remote states
A page-size control inside the footer, plus a local simulation of remote filtering, sorting and pagination.
| Roles | Actions |
|---|
Changing page size, search, role or sort returns to page 1. Page-size selection stays available when results are empty or a request fails.
Vue/Persona table integration recipeFile upload
Validate type and size, preview images, and retry a failed upload.
PDF, JPEG, PNG · up to 10 MB each · maximum 5 files
Date range filter
Use a preset or an inclusive date range in Asia/Jakarta.
Tooltip & popover
Short guidance on hover or focus; richer information in a dismissible popover.
How roles work
Agents handle tickets. Support admins manage team access.
Read the guidanceMulti-step form
Validate each step, review the details, and confirm before discarding edits.
Three steps: Account → Preferences → Review. No backend request is made.
Connection & session
Keep failures actionable and never resend an unconfirmed mutation automatically.
Member lists
Rows grow with their content. Avatars, metadata, selection, and focus remain aligned.
Choose a member to continue.
Skeleton & shimmer
Match loading placeholders to the content layout and announce loading once.
Loading preview content…
Member list
Table
Card & detail
Chat
Complete page examples
Inspect the patterns together at desktop and mobile sizes.
Dashboard Overview
KPI cards, operational summaries, eight reporting charts, and local downloads.
Open example →Account directory, filters, bulk actions, creation flow.
Open example →Knowledge & Macros
Searchable resource cards with formatted previews.
Open example →Teams
Member directory with search and selection.
Open example →Support Inbox
Ticket selection, composer autofocus, macros, and local replies.
Open example →Recruitment workspace
Scoped candidates, pipeline, interviews, job forms and consent-aware talent follow-up.
Open example →Contract lifecycle workspace
Requests, documents, approvals, signing and post-signing operations.
Open example →Input groups, password & PIN
Connected controls, show/hide password, strength feedback, and a single accessible OTP field.
Input group
Use a long, unique password.
Strength feedback is illustrative; production needs a dedicated estimator.Input PIN
Inputs with units
Keep the unit visible while editing the value.
Dashboard input patterns
Useful field combinations for budgets, contacts, settings and filters. All checks run locally.
Formatted amount
Use for budgets and spending limits, with a plain numeric form value.
Phone with country code
Use for contact details and notification recipients.
URL with format feedback
Use for website addresses and webhook settings.
Minimum–maximum range
Use for amount, volume and count filters.
Freeform tags
Use for labels, categories and saved-filter keywords.
Search with clear action
Use for short directories and in-page lists.
Search with leading filters
Place a filter on the left of search, with one shared border and results that combine both choices.
Filter by status
Narrow the list by status, then search within the matching accounts.
Try another filter or search term.
Search by field
Choose which account field to search using the selector on the left.
Try another filter or search term.
Advanced form patterns
Build a notification rule with repeatable fields, dependent choices and a review before saving. Nothing is sent or stored.
Try error states
For the rule ID, try taken (unavailable) or offline (check failed).
HTML & JSON editor
Compose HTML, define template variables and inspect a local preview with sample data.
Message template
Dark code surfaces, clear labels and a focused preview. No data is uploaded.
Fix the errors below, then update the preview.
Use {{variable_name}} in text. Preview supports static content and inline styles; scripts, links, forms and remote assets are omitted.
Local preview only. Tab moves to the next control.
Calendar & time picker
An inline calendar with keyboard navigation and a time control with an explicit timezone.
Choose a date.
Schedule a time
09:00 · UTC+7
Carousel, chart & progress
Manual slide navigation, accessible data charts, circular progress, and expandable text.
Tickets resolved · demo data
View chart data
| Day | Tickets |
|---|---|
| Monday | 12 |
| Tuesday | 18 |
| Wednesday | 9 |
Truncate with expansion
This enterprise account has a long description that remains available to everyone. Use an explicit expansion control when important content does not fit in a compact card. Never hide the full value from keyboard or touch users.
Aspect ratio
Camera & cropper
Capture only after permission, or choose a local image. Preview and export a square crop.
Camera
Camera is off. HTTPS or localhost may be required by your browser.
Image cropper
Choose an image to begin. Images stay on this device.
Signature & QR code
Draw or type a signature preview, and scan a real QR code linking to the public documentation.
Draw signature
Visual preview only. No signing or identity verification takes place.
QR code
Open the same documentation link
No login token or private data is encoded.
PDF viewer & placement
A local sample PDF plus a placement surface for text and signature fields.
Native PDF viewer
Use Open sample PDF if your browser does not support embedded PDFs.
Placement controls
Click to add, drag to place, or move the selected field with arrow keys. Shift + arrow moves in larger steps.
No fields placed.
Sample agreement
Company: PT Innovation (Demo)
This is a synthetic one-page document for UI previews.
Rich text editor
Editable formatted content with a keyboard-accessible toolbar and plain-text paste.
Write a helpful answer for your customer.
Applied filters
Combine filters, remove individual criteria, and reset the result set without losing context.
View HTML
<article class="specimen"><form id="filter-form"><div class="grid three"><label class="field">Search tickets<input type="search" id="filter-query" placeholder="Subject or ticket ID…"></label><label class="field">Status<select id="filter-status"><option value="all">All statuses</option><option>Open</option><option>Resolved</option></select></label><label class="field">Priority<select id="filter-priority"><option value="all">All priorities</option><option>High</option><option>Normal</option></select></label></div><div class="row wrap"><button class="button primary" type="submit">Apply filters</button><button class="button secondary" type="reset">Reset</button></div></form><div id="applied-filter-chips" class="chip-list demo-note" aria-label="Applied filters"></div><p id="filter-count" class="demo-note muted" role="status"></p><ul id="filtered-tickets" class="pattern-list"></ul></article>Activity timeline
Show who changed what and when, with a filter and explicit timestamps.
Ticket activity
View HTML
<article class="specimen"><div class="row wrap spread"><h3>Ticket activity</h3><label class="field">Activity type<select id="activity-filter"><option value="all">All activity</option><option value="message">Messages</option><option value="assignment">Assignments</option><option value="status">Status changes</option></select></label></div><ol id="activity-list" class="activity-list"></ol><button class="button secondary demo-note" id="activity-more">Load earlier activity</button><p id="activity-summary" class="demo-note muted" role="status"></p></article>Notification center
Review read and unread notifications, mark individual items, and recover from loading failures.
Notifications 2 unread
View HTML
<article class="specimen"><div class="row wrap spread"><h3>Notifications <span class="badge info" id="notification-count">2 unread</span></h3><button class="button secondary compact" id="notifications-read-all">Mark all as read</button></div><div class="row wrap"><label class="field grow">Show<select id="notification-filter"><option value="all">All notifications</option><option value="unread">Unread only</option></select></label><label class="field grow">Preview state<select id="notification-state"><option value="ready">Ready</option><option value="loading">Loading</option><option value="empty">Empty</option><option value="error">Error</option></select></label></div><div id="notification-content" aria-busy="false"><ul id="notification-items" class="pattern-list"></ul><p id="notification-feedback" role="status"></p></div><button class="button secondary demo-note" id="notification-retry" hidden>Retry</button></article>Reorderable list
Reorder macro steps by dragging or using Move up/Move down. Changes are announced and can be reset.
Macro steps
Drag a row, use its arrow buttons, or press Alt + Up/Down on the row handle.
Three steps. No changes saved to a server.
View HTML
<article class="specimen"><div class="row wrap spread"><h3>Macro steps</h3><button class="button secondary compact" id="sort-reset">Reset order</button></div><p class="muted">Drag a row, use its arrow buttons, or press Alt + Up/Down on the row handle.</p><ol id="sortable-items" class="sortable-items demo-note"></ol><p id="sort-status" class="demo-note muted" role="status">Three steps. No changes saved to a server.</p></article>Customer satisfaction rating
Collect a clear one-to-five rating with optional feedback and an explicit submit action.
View HTML
<form id="rating-form" class="specimen" novalidate><fieldset class="rating-fieldset" aria-describedby="rating-description rating-error"><legend>How was your support experience?</legend><p id="rating-description" class="muted">Choose one rating. Your response is only stored in this local preview.</p><div class="rating-options"><label class="rating-choice"><input type="radio" name="satisfaction" value="1" required><span class="rating-star" aria-hidden="true">★</span><span class="sr-only">1 — Very dissatisfied</span><small aria-hidden="true">1</small></label><label class="rating-choice"><input type="radio" name="satisfaction" value="2" required><span class="rating-star" aria-hidden="true">★</span><span class="sr-only">2 — Dissatisfied</span><small aria-hidden="true">2</small></label><label class="rating-choice"><input type="radio" name="satisfaction" value="3" required><span class="rating-star" aria-hidden="true">★</span><span class="sr-only">3 — Neutral</span><small aria-hidden="true">3</small></label><label class="rating-choice"><input type="radio" name="satisfaction" value="4" required><span class="rating-star" aria-hidden="true">★</span><span class="sr-only">4 — Satisfied</span><small aria-hidden="true">4</small></label><label class="rating-choice"><input type="radio" name="satisfaction" value="5" required><span class="rating-star" aria-hidden="true">★</span><span class="sr-only">5 — Very satisfied</span><small aria-hidden="true">5</small></label></div><p id="rating-value" role="status">No rating selected.</p></fieldset><label class="field demo-note">Tell us more <span class="optional">Optional</span><textarea id="rating-comment" rows="3" maxlength="300" placeholder="What could we improve?" aria-describedby="rating-counter"></textarea><small id="rating-counter">0 / 300 characters</small></label><p id="rating-error" class="field-error" role="alert"></p><div class="row wrap"><button type="submit" class="button primary" id="rating-submit">Submit feedback</button><button type="reset" class="button secondary">Reset</button></div><p id="rating-result" class="demo-note" role="status"></p></form>Resizable split pane
Resize a desktop detail panel with the pointer or keyboard. On small screens, panels stack vertically.
Ticket workspace
Drag the divider or focus it and use Left/Right. Home/End selects the minimum/maximum width.
Inbox
Signing issue
Select a ticket from the inbox to view its details.
Local previewInbox width: 35%.
View HTML
<article class="specimen"><div class="row wrap spread"><h3>Ticket workspace</h3><button class="button secondary compact" id="split-reset">Reset width</button></div><p id="split-help" class="muted">Drag the divider or focus it and use Left/Right. Home/End selects the minimum/maximum width.</p><div class="split-layout demo-note" id="split-layout"><section class="split-panel" id="split-queue" aria-labelledby="split-queue-title"><h4 id="split-queue-title">Inbox</h4><button class="member-row" data-split-ticket="Signing issue" aria-pressed="true"><span class="member-copy"><strong>Signing issue</strong><small>Samantha Jasmine · Open</small></span></button><button class="member-row" data-split-ticket="Verification request" aria-pressed="false"><span class="member-copy"><strong>Verification request</strong><small>Bani El Rehab · Pending</small></span></button></section><div id="split-divider" class="split-divider" role="separator" tabindex="0" aria-label="Inbox width" aria-orientation="vertical" aria-controls="split-queue" aria-valuemin="25" aria-valuemax="60" aria-valuenow="35" aria-valuetext="35 percent" aria-describedby="split-help"></div><section class="split-panel" aria-labelledby="split-detail-title"><h4 id="split-detail-title">Signing issue</h4><p class="demo-note">Select a ticket from the inbox to view its details.</p><span class="badge info demo-note">Local preview</span></section></div><p id="split-status" class="muted demo-note" role="status">Inbox width: 35%.</p></article>Page states & recovery
Error pages for missing content, access restrictions and service interruptions, plus empty and loading states.
Error pages
A clear explanation. A useful next step.
WorkspaceWe can’t find that page
The link may be out of date, or the page may have moved. Return to your workspace to find what you need.
404 preview selected.
View HTML
<article class="specimen error-pages" id="error-pages"><div class="error-pages-heading"><div><h3>Error pages</h3><p class="muted">A clear explanation. A useful next step.</p></div><span class="badge info">Companion examples</span></div><div class="error-page-options" role="group" aria-label="Choose an error page preview"><button type="button" class="error-page-option" data-error-page="400" aria-pressed="false" aria-controls="error-page-preview"><strong>400</strong><span>Bad request</span></button><button type="button" class="error-page-option" data-error-page="401" aria-pressed="false" aria-controls="error-page-preview"><strong>401</strong><span>Sign-in required</span></button><button type="button" class="error-page-option" data-error-page="403" aria-pressed="false" aria-controls="error-page-preview"><strong>403</strong><span>Access denied</span></button><button type="button" class="error-page-option" data-error-page="404" aria-pressed="true" aria-controls="error-page-preview"><strong>404</strong><span>Not found</span></button><button type="button" class="error-page-option" data-error-page="408" aria-pressed="false" aria-controls="error-page-preview"><strong>408</strong><span>Request timeout</span></button><button type="button" class="error-page-option" data-error-page="429" aria-pressed="false" aria-controls="error-page-preview"><strong>429</strong><span>Too many requests</span></button><button type="button" class="error-page-option" data-error-page="500" aria-pressed="false" aria-controls="error-page-preview"><strong>500</strong><span>Server error</span></button><button type="button" class="error-page-option" data-error-page="502" aria-pressed="false" aria-controls="error-page-preview"><strong>502</strong><span>Bad gateway</span></button><button type="button" class="error-page-option" data-error-page="503" aria-pressed="false" aria-controls="error-page-preview"><strong>503</strong><span>Unavailable</span></button><button type="button" class="error-page-option" data-error-page="504" aria-pressed="false" aria-controls="error-page-preview"><strong>504</strong><span>Gateway timeout</span></button><button type="button" class="error-page-option" data-error-page="offline" aria-pressed="false" aria-controls="error-page-preview"><strong>OFFLINE</strong><span>Offline</span></button></div><div id="error-page-preview" class="error-page-preview" role="region" aria-labelledby="error-page-title"><div class="error-page-brand"><img src="assets/logo-privy.png" alt="Privy" width="970" height="257"><span class="muted">Workspace</span></div><div class="error-page-body"><div class="error-page-art" aria-hidden="true"><svg id="error-page-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h5 M14 2v6h6v3 M14 2l6 6 M8 7h2 M8 11h4 M19 16a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z M18 19l4 3"/></svg><span id="error-page-code">404</span><span id="error-page-kind">Page not found</span></div><div class="error-page-copy"><span class="eyebrow" id="error-page-category">MISSING CONTENT</span><h4 id="error-page-title">We can’t find that page</h4><p id="error-page-description">The link may be out of date, or the page may have moved. Return to your workspace to find what you need.</p><div class="error-page-actions"><button type="button" class="button primary" id="error-page-action" hidden>Try again</button><a href="examples/dashboard.html" class="button primary" id="error-page-home">Back to workspace</a></div></div></div><div class="error-page-footer"><span>Preview only · No real requests or session changes.</span><span id="error-page-reference">HTTP 404</span></div></div><p id="error-page-feedback" class="demo-note" role="status" aria-atomic="true">404 preview selected.</p></article><article class="specimen"><label class="field">Preview page state<select id="page-state"><option value="empty">First use</option><option value="filtered">No matching results</option><option value="loading">Loading</option><option value="error">Service error</option><option value="forbidden">Access required</option><option value="missing">Not found</option><option value="ready">Ready</option></select></label><div id="state-preview" class="state-preview" aria-busy="false"><div id="state-symbol" class="state-symbol" aria-hidden="true"></div><h3 id="state-title"></h3><p id="state-description" class="muted"></p><div id="state-loading" class="state-loading" hidden aria-hidden="true"><span class="pv-skeleton line"></span><span class="pv-skeleton line short"></span></div><button class="button primary" id="state-action"></button></div><p id="state-result" class="demo-note" role="status"></p></article>Dashboard cards & overview
Reusable KPI, trend, chart, progress, status, activity, team, quick-action, and service-health cards.
Synthetic data for layout and interaction previews.
Ticket volume
Created tickets by day or week.
View chart data
| Period | Created |
|---|
Resolution target
Progress towards the team target.
Review unresolved tickets before the end of the period.
Queue status
Recent activity
- Ticket assignedMaya Chen · Signing issue
- Article updatedAlex Morgan · Account verification
- Team member invitedGuritno Adi · Customer Support
Team overview
Resolved tickets in this demo period.
Quick actions
Actions open local page examples.
Service health
Attention needed
Requires reviewOpen tickets approaching their SLA deadline.
Review ticketsReporting, charts & downloads
Explore the same synthetic delivery data through eight chart patterns. Every chart has an accessible data table.
Recent downloads
Files generated in this tab, up to 10 entries. A browser download request does not confirm that a file was saved. No scheduled jobs or emails are created.
No files prepared yet. Download a report or chart to get started.
Advanced data table
Choose and pin columns, expand records, and edit names with validation.
Choose columns
Saved views & advanced filters
Apply AND/OR criteria, save a named view, and restore its filters.
CSV import wizard
Map columns, review invalid or duplicate rows, and import only valid records.
- 1. File
- 2. Mapping
- 3. Review
- 4. Result
Local preview · UTF-8 CSV, at most 100 KB and 500 data rows. Import never uploads a file.
| Row | Name | Result |
|---|
Background job center
Track queued and running jobs, cancel or retry failures, and download completed exports.
Simulated jobs run only while this page is open. Retry creates a new attempt; expired files require a new export.
Roles & permission matrix
Preview changes to feature permissions and data scope before applying them.
| Capability | Workspace admin | Supervisor | Agent |
|---|
Review permission changes
Approval workflow
Review a request, approve or reject with a reason, and retain the decision history.
- Requested by
- Maya Chen
- Audience
- 250 verified contacts
- Message
- Scheduled service update for your workspace.
Decision history
Recurring scheduler
Preview explicit timezones and upcoming runs, then pause or resume a schedule.
Upcoming runs
Report drill-down & saved reports
Select a metric and channel to inspect matching rows; save and restore the report definition.
Select a metric, then a bar to inspect its records. Counts are synthetic messages.
| Campaign | Channel | Sent | Delivered | Failed |
|---|
Drafts, publishing & version history
Autosave a local draft, publish snapshots, compare versions, and restore a version into a draft.
Kanban work queue
Assign work and move cards between columns with capacity limits and keyboard alternatives.
Drag a card or use its Move to control. In progress allows at most two cards.
Integration operations
Inspect webhook delivery attempts, explicitly retry failures, and rotate a demo API key.
| Event | Result | Attempts | Actions |
|---|
Delivery detail
API key
Demo key · ••••a1b2
Replace this demo key? Existing demo integrations would need the new key.
Copy this sample once. It is not a usable credential.
Profile, preferences & sessions
Save profile and notification preferences, choose a timezone, and revoke other demo sessions.
Active sessions
Conflicts, partial results, undo & stale data
Preserve user work and distinguish confirmed failures from unconfirmed outcomes.
Concurrent editing conflict
Server version: Original verification steps
Another user changed this record. Compare both versions before resolving.
Bulk action: partial outcome
Time-limited undo
Draft: October newsletter
Stale data after refresh failure
132 open tickets
Last successful snapshot: 10:00 Asia/Jakarta
Recruitment workspace
Team-scoped candidates, hiring stages, profile details, pipeline and interview review.
Hiring workspace
Synthetic records · October 2026 · WIB (UTC+07:00)
Role switching is a demo. Real access must be enforced by the server.
Metrics use the same team scope and applications received 1–31 October 2026. Filters below affect the list and board only.
| Role / team | Stage | Applied (WIB) | Actions |
|---|
No candidates match these filters.
Application pipeline
Use Move stage or drag a card to review a transition. Rejected and Withdrawn are alternative outcomes.
Interview schedule & reviews
All times show WIB. HR and interviewer reviews stay separate.
Job openings & forms
Draft, publish or close a scoped opening using a reusable job form.
Job templates and openings use the team scope selected in Hiring workspace. Only Published openings accept applications.
Create or edit an opening
Talent pool & saved pipeline
Keep future-opportunity consent, outreach and archives separate from applications.
Talent pool membership is separate from active applications. Future-opportunity consent is versioned separately from application consent.
Contract repository & workspace
Find, filter, inspect and report on the permitted contract portfolio.
Contract workspace
Synthetic portfolio · Snapshot 27 October 2026 · WIB
All access, approval, signature and extraction events are local fixtures. No contract is sent, signed or published.
Saved views live in this tab. The JSON download bundles selected document text and metadata; it is not a PDF or signature certificate.
Metrics and exports use the same permitted, filtered contracts and fixed snapshot date. Currency totals are kept separate.
| Counterparty / owner | Value | Workflow / signature | Term / action |
|---|
Workflow distribution and cycle times
Contract detail workspace
Activity
Contract requests, templates & metadata
Create a structured request, reuse approved wording and review extracted values.
Request a contract
Create a local draft with conditional fields and supporting file metadata.
Templates & clause library
Approved snippets are illustrative; they are not legal advice or a production playbook.
Metadata extraction review
Synthetic suggestions with source evidence. Review values and explicitly confirm selected corrections.
Contract review & approval
Compare versions, discuss clauses and approve the exact document version.
Document workspace
Plain-text contract specimen. Signed documents and in-flight envelopes are locked.
Compare document versions
Clause comments & negotiation
Version-bound approval route
Finance is required at IDR 100 million or USD 10,000 and above. This is a demo routing rule.
Signature preparation & tracking
Prepare signer fields and inspect envelope states without sending real documents.
Prepare signature fields
Select a signer, field and page; set its position using keyboard-accessible coordinates.
Obligations, renewals & amendments
Track commitments, notice deadlines and relationships after signing.
Obligations & evidence
Renewal & notice calendar
Related agreements & amendments
The signed original remains immutable. A draft replacement does not supersede the original until signed and effective.
Bot messages & request recovery
Separate bot availability, message outcomes and secret handling. These fixtures do not send messages or call an API.
Bot availability & conversation
Sending is available. History remains readable.
One-time webhook secret
Rotation invalidates the previous secret. Reveal it only after a successful response; closing removes it from the preview.
Use a confirmation before rotation. A lost response requires reconciliation, not automatic replay.
Rate-limit feedback
Use the server’s Retry-After duration. Keep drafts intact and show the remaining wait without announcing every second.
Recover an unconfirmed request
Preserve the original review ID and exact schedule intent. Checking an unknown result reuses the same identifier; it does not create another logical send.
No request yet.
Build with consistency
Keep the implementation and its written contract together. A design change is not complete until both are updated.
When changing a component
- Read GUIDELINES.md and reuse semantic tokens.
- Update the example, states, and behavior in the HTML/CSS/JS.
- Update the relevant written guidance and changelog.
- Run
python3 scripts/sync.py --note "Describe the change". - Run
python3 scripts/sync.py --checkand test light/dark, keyboard, and mobile.
Project alignment
Derived from Official Account Hermod: main.css, AppIcon, SupportResources, SupportConfirmDialog, StatusBadge, and the message composer.
Full-rounded fields follow the current Support forms. Native controls not yet present in the product are companion examples using the same tokens.
View HTML Copy this specimen
<div class="grid two"><article class="specimen"><h3>When changing a component</h3><ol class="steps-list"><li>Read <a href="GUIDELINES.md">GUIDELINES.md</a> and reuse semantic tokens.</li><li>Update the example, states, and behavior in the HTML/CSS/JS.</li><li>Update the relevant written guidance and changelog.</li><li>Run <code>python3 scripts/sync.py --note "Describe the change"</code>.</li><li>Run <code>python3 scripts/sync.py --check</code> and test light/dark, keyboard, and mobile.</li></ol></article><article class="specimen"><h3>Project alignment</h3><p>Derived from Official Account Hermod: main.css, AppIcon, SupportResources, SupportConfirmDialog, StatusBadge, and the message composer.</p><p>Full-rounded fields follow the current Support forms. Native controls not yet present in the product are companion examples using the same tokens.</p><div class="row wrap"><a class="button secondary" href="GUIDELINES.md">Guidelines</a><a class="button outline" href="CHANGELOG.md">Changelog</a><a class="button ghost" href="README.md">Getting started</a></div></article></div>