indira-workspace-ui 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,8 +10,9 @@ This repo also holds a second, deliberately separate composition — **Vault UI*
10
10
  custody/treasury/compliance-grade screens where a light, friendly surface
11
11
  would undersell the data. See it live at the "Vault UI" nav item in the demo
12
12
  workspace. The `/indira-ui` skill now asks which composition to use before
13
- building a screen whenever a repo has more than one. Vault UI is not part of
14
- the published npm package yet it's source-only in this repo for now.
13
+ building a screen whenever a repo has more than one. Vault UI ships from its
14
+ own subpath `indira-workspace-ui/vault`so installing the main package
15
+ never pulls its dark theme in by accident.
15
16
 
16
17
  ## Install in a consuming project
17
18
 
@@ -44,6 +45,24 @@ Add a `@source` pointing at the installed package next to your own `@import "tai
44
45
  - One filled button per view, top-right (law 1). Status is always a glyph and a colour (law 5).
45
46
  - Theme: `useTheme()` sets `data-theme` on the root; the default follows `prefers-color-scheme`.
46
47
 
48
+ ### Vault UI
49
+
50
+ ```jsx
51
+ import { VaultShell, VaultButton, VaultTable, VaultStatusPill } from "indira-workspace-ui/vault";
52
+ import "indira-workspace-ui/vault/style.css";
53
+ ```
54
+
55
+ Same `@source` requirement as the main system, pointed at the same `dist-lib`:
56
+
57
+ ```css
58
+ @import "indira-workspace-ui/vault/style.css";
59
+ @source "../node_modules/indira-workspace-ui/dist-lib";
60
+ ```
61
+
62
+ Never import both `style.css` and `vault/style.css` into the same screen's markup — see
63
+ [docs/design-system-vault.md](docs/design-system-vault.md) §6. A page is one composition or the
64
+ other.
65
+
47
66
  ## Scales
48
67
 
49
68
  The only values that compile: text `xs sm base lg xl 2xl`, weights `normal medium semibold bold`,
@@ -64,7 +83,7 @@ npm run build:lib # builds the publishable package into dist-lib/
64
83
  | `src/components/` | One file per component; `index.js` is the barrel and the library's entry point. |
65
84
  | `src/screens/` | The demo workspace: Dashboard, BRD list, BRD detail, a library gallery per component group, and the dev-skills docs screen. |
66
85
  | `src/data/` | Demo directory and BRD records (demo app only — not part of the published package). |
67
- | `src/vault/` | Vault UI — the second design composition. Its own tokens/components, scoped under `.vault`, never mixed with `src/components/`. |
86
+ | `src/vault/` | Vault UI — the second design composition. Its own tokens/components, scoped under `.vault`, never mixed with `src/components/`. Ships from its own `dist-lib/vault.{mjs,cjs}` + `dist-lib/vault.css`, exported as the package's `./vault` subpath. |
68
87
  | `.claude/skills/indira-ui`, `.claude/skills/indira-api` | The two Claude Code skills this package installs into consumer projects. |
69
88
  | `bin/cli.js` | The `indira-workspace-ui add-skills` CLI. |
70
89
  | `vite.lib.config.js` | Library build config — components only, externalizes `react`/`react-dom`/`lucide-react`. |
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("react/jsx-runtime"),t=require("lucide-react");function n({name:t=``,photo:n,size:r=32,className:i=``}){let a=t.trim().split(/\s+/).slice(0,2).map(e=>e[0]).join(``).toUpperCase();return(0,e.jsx)(`span`,{className:`vault-avatar ${i}`,style:{width:r,height:r,fontSize:Math.round(r*.4)},children:n?(0,e.jsx)(`img`,{src:n,alt:``}):a})}function r({brand:r,nav:a=[],activeId:o,onNavigate:s,footNav:c=[],user:l,className:u=``,children:d}){return(0,e.jsxs)(`div`,{className:`vault-shell ${u}`,children:[(0,e.jsxs)(`aside`,{className:`vault-shell__sidebar`,children:[(0,e.jsxs)(`div`,{className:`vault-shell__brand`,children:[(0,e.jsx)(`span`,{className:`vault-shell__brand-mark`,children:r?.mark??`V`}),(0,e.jsx)(`span`,{className:`vault-shell__brand-name`,children:r?.name})]}),(0,e.jsx)(`nav`,{className:`vault-shell__nav`,children:a.map(t=>(0,e.jsx)(i,{item:t,active:t.id===o,onClick:()=>s?.(t.id)},t.id))}),c.length>0&&(0,e.jsxs)(`div`,{className:`vault-shell__foot`,children:[(0,e.jsx)(`div`,{className:`vault-shell__foot-label`,children:`Support`}),c.map(t=>(0,e.jsx)(i,{item:t,active:!1,onClick:()=>s?.(t.id)},t.id))]})]}),(0,e.jsxs)(`div`,{className:`vault-shell__main`,children:[(0,e.jsxs)(`header`,{className:`vault-shell__topbar`,children:[(0,e.jsx)(`button`,{type:`button`,className:`vault-topbar__icon`,title:`Notifications`,children:(0,e.jsx)(t.Bell,{size:17,"aria-hidden":`true`})}),l&&(0,e.jsxs)(`button`,{type:`button`,className:`vault-btn vault-user`,style:{padding:0,background:`none`},title:l.email,children:[(0,e.jsx)(n,{name:l.name,photo:l.photo}),(0,e.jsxs)(`span`,{className:`min-w-0`,style:{textAlign:`left`},children:[(0,e.jsx)(`span`,{className:`vault-user__name block`,children:l.name}),(0,e.jsx)(`span`,{className:`vault-user__role block`,children:l.role})]}),(0,e.jsx)(t.ChevronDown,{size:14,"aria-hidden":`true`,style:{color:`rgb(var(--v-ink-faint))`}})]})]}),(0,e.jsx)(`div`,{className:`vault-shell__content`,children:d})]})]})}function i({item:t,active:n,onClick:r}){let i=t.icon;return(0,e.jsxs)(`button`,{type:`button`,className:`vault-nav-item`,"aria-current":n?`page`:void 0,onClick:r,title:t.label,children:[i&&(0,e.jsx)(i,{size:17,"aria-hidden":`true`}),(0,e.jsx)(`span`,{className:`truncate`,children:t.label})]})}function a({tabs:t=[],value:n,onChange:r,className:i=``}){return(0,e.jsx)(`div`,{className:`vault-tabs ${i}`,role:`tablist`,children:t.map(t=>(0,e.jsx)(`button`,{type:`button`,role:`tab`,className:`vault-tab`,"aria-selected":t.value===n,onClick:()=>r?.(t.value),children:t.label},t.value))})}function o({onBack:n,title:r,tabs:i,value:o,onTabChange:s,figure:c,figureSub:l,pill:u}){return(0,e.jsxs)(e.Fragment,{children:[n&&(0,e.jsxs)(`button`,{type:`button`,className:`vault-back`,onClick:n,children:[(0,e.jsx)(t.ChevronLeft,{size:14,"aria-hidden":`true`}),`Back`]}),(0,e.jsxs)(`div`,{className:`vault-header`,children:[(0,e.jsxs)(`div`,{children:[(0,e.jsx)(`h1`,{className:`vault-header__title`,children:r}),i&&(0,e.jsx)(a,{tabs:i,value:o,onChange:s})]}),(c||u)&&(0,e.jsxs)(`div`,{children:[c&&(0,e.jsx)(`div`,{className:`vault-header__figure`,children:c}),l&&(0,e.jsx)(`div`,{className:`vault-header__sub`,children:l}),u&&(0,e.jsx)(`div`,{className:`vault-header__pill-row`,children:u})]})]})]})}function s({tone:t=`accent`,children:n,className:r=``}){return(0,e.jsx)(`span`,{className:`vault-pill vault-pill--${t} ${r}`,children:n})}function c({figure:t,label:n,tone:r,className:i=``}){let a=r===`accent`?`rgb(var(--v-accent))`:r===`warn`?`rgb(var(--v-warn))`:r===`danger`?`rgb(var(--v-danger))`:void 0;return(0,e.jsxs)(`div`,{className:`vault-stat ${i}`,style:a?{"--tone":a}:void 0,children:[(0,e.jsx)(`span`,{className:`vault-stat__figure`,children:t}),(0,e.jsx)(`span`,{className:`vault-stat__label`,children:n})]})}function l({variant:t=`primary`,icon:n,className:r=``,children:i,...a}){return(0,e.jsxs)(`button`,{type:`button`,className:`vault-btn vault-btn--${t} ${r}`,...a,children:[n&&(0,e.jsx)(n,{size:t===`icon`?18:15,"aria-hidden":`true`}),t!==`icon`&&i]})}function u({checked:n=!1,onChange:r,label:i,className:a=``}){return(0,e.jsxs)(`label`,{className:`vault-check-row ${a}`,style:{cursor:`pointer`},children:[(0,e.jsx)(`button`,{type:`button`,role:`checkbox`,"aria-checked":n,className:`vault-checkbox`,onClick:()=>r?.(!n),children:n&&(0,e.jsx)(t.Check,{size:12,"aria-hidden":`true`})}),i&&(0,e.jsx)(`span`,{className:`vault-check-row__label`,children:i})]})}function d({clearable:n=!1,onClear:r,className:i=``,...a}){return n?(0,e.jsxs)(`span`,{className:`vault-inp-wrap`,children:[(0,e.jsx)(`input`,{className:`vault-inp ${i}`,...a}),(0,e.jsx)(`button`,{type:`button`,onClick:r,title:`Clear`,"aria-label":`Clear`,children:(0,e.jsx)(t.X,{size:14,"aria-hidden":`true`})})]}):(0,e.jsx)(`input`,{className:`vault-inp ${i}`,...a})}function f({className:t=``,children:n,...r}){return(0,e.jsx)(`select`,{className:`vault-select ${t}`,...r,children:n})}function p({columns:t=[],rows:n=[],getRowId:r=(e,t)=>e.id??t,className:i=``}){return(0,e.jsx)(`div`,{className:`vault-table-wrap`,children:(0,e.jsxs)(`table`,{className:`vault-table ${i}`,children:[(0,e.jsx)(`thead`,{children:(0,e.jsx)(`tr`,{children:t.map(t=>(0,e.jsx)(`th`,{style:t.align===`right`?{textAlign:`right`}:void 0,children:t.label},t.key))})}),(0,e.jsx)(`tbody`,{children:n.map((n,i)=>(0,e.jsx)(`tr`,{children:t.map(t=>(0,e.jsx)(`td`,{style:t.align===`right`?{textAlign:`right`}:void 0,children:t.render?t.render(n):n[t.key]},t.key))},r(n,i)))})]})})}exports.VaultAvatar=n,exports.VaultButton=l,exports.VaultCheckbox=u,exports.VaultInput=d,exports.VaultPageHeader=o,exports.VaultSelect=f,exports.VaultShell=r,exports.VaultStatTile=c,exports.VaultStatusPill=s,exports.VaultTable=p,exports.VaultTabs=a;
2
+ //# sourceMappingURL=vault.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.cjs","names":[],"sources":["../src/vault/VaultAvatar.jsx","../src/vault/VaultShell.jsx","../src/vault/VaultPageHeader.jsx","../src/vault/VaultStatusPill.jsx","../src/vault/VaultStatTile.jsx","../src/vault/VaultControls.jsx","../src/vault/VaultTable.jsx"],"sourcesContent":["/** Circular avatar restyled for a dark ground — see VaultShell for usage. */\nexport default function VaultAvatar({ name = \"\", photo, size = 32, className = \"\" }) {\n const initials = name\n .trim()\n .split(/\\s+/)\n .slice(0, 2)\n .map((p) => p[0])\n .join(\"\")\n .toUpperCase();\n return (\n <span className={`vault-avatar ${className}`} style={{ width: size, height: size, fontSize: Math.round(size * 0.4) }}>\n {photo ? <img src={photo} alt=\"\" /> : initials}\n </span>\n );\n}\n","import { Bell, ChevronDown } from \"lucide-react\";\nimport VaultAvatar from \"./VaultAvatar\";\n\n/**\n * VaultShell — the sidebar + top bar chrome for Vault UI\n * (docs/design-system-vault.md §3). Dark flows continuously: sidebar,\n * top bar and content are all --v-canvas, no lighter sidebar surface.\n *\n * <VaultShell nav={NAV} activeId={id} onNavigate={setId} brand={{...}} user={{...}}>\n * {content}\n * </VaultShell>\n */\nexport default function VaultShell({ brand, nav = [], activeId, onNavigate, footNav = [], user, className = \"\", children }) {\n return (\n <div className={`vault-shell ${className}`}>\n <aside className=\"vault-shell__sidebar\">\n <div className=\"vault-shell__brand\">\n <span className=\"vault-shell__brand-mark\">{brand?.mark ?? \"V\"}</span>\n <span className=\"vault-shell__brand-name\">{brand?.name}</span>\n </div>\n <nav className=\"vault-shell__nav\">\n {nav.map((item) => (\n <VaultNavItem key={item.id} item={item} active={item.id === activeId} onClick={() => onNavigate?.(item.id)} />\n ))}\n </nav>\n {footNav.length > 0 && (\n <div className=\"vault-shell__foot\">\n <div className=\"vault-shell__foot-label\">Support</div>\n {footNav.map((item) => (\n <VaultNavItem key={item.id} item={item} active={false} onClick={() => onNavigate?.(item.id)} />\n ))}\n </div>\n )}\n </aside>\n <div className=\"vault-shell__main\">\n <header className=\"vault-shell__topbar\">\n <button type=\"button\" className=\"vault-topbar__icon\" title=\"Notifications\">\n <Bell size={17} aria-hidden=\"true\" />\n </button>\n {user && (\n <button type=\"button\" className=\"vault-btn vault-user\" style={{ padding: 0, background: \"none\" }} title={user.email}>\n <VaultAvatar name={user.name} photo={user.photo} />\n <span className=\"min-w-0\" style={{ textAlign: \"left\" }}>\n <span className=\"vault-user__name block\">{user.name}</span>\n <span className=\"vault-user__role block\">{user.role}</span>\n </span>\n <ChevronDown size={14} aria-hidden=\"true\" style={{ color: \"rgb(var(--v-ink-faint))\" }} />\n </button>\n )}\n </header>\n <div className=\"vault-shell__content\">{children}</div>\n </div>\n </div>\n );\n}\n\nfunction VaultNavItem({ item, active, onClick }) {\n const Icon = item.icon;\n return (\n <button type=\"button\" className=\"vault-nav-item\" aria-current={active ? \"page\" : undefined} onClick={onClick} title={item.label}>\n {Icon && <Icon size={17} aria-hidden=\"true\" />}\n <span className=\"truncate\">{item.label}</span>\n </button>\n );\n}\n","import { ChevronLeft } from \"lucide-react\";\n\n/** Underline tabs (§4) — a different composition earns a different tab shape than the main system's segmented track. */\nexport function VaultTabs({ tabs = [], value, onChange, className = \"\" }) {\n return (\n <div className={`vault-tabs ${className}`} role=\"tablist\">\n {tabs.map((t) => (\n <button\n key={t.value}\n type=\"button\"\n role=\"tab\"\n className=\"vault-tab\"\n aria-selected={t.value === value}\n onClick={() => onChange?.(t.value)}\n >\n {t.label}\n </button>\n ))}\n </div>\n );\n}\n\n/**\n * VaultPageHeader — back control, bold title, underline tabs, and a\n * right-hand slot for the one figure that matters most on this page plus\n * its status pill (docs/design-system-vault.md §3).\n *\n * <VaultPageHeader onBack={...} title=\"Marketing\" tabs={...} value={tab} onTabChange={setTab}\n * figure=\"2,345.6789 BTC\" figureSub=\"≈59,339,969.79 CNY\" pill={<VaultStatusPill tone=\"warn\">Effective after 23:59:59</VaultStatusPill>} />\n */\nexport default function VaultPageHeader({ onBack, title, tabs, value, onTabChange, figure, figureSub, pill }) {\n return (\n <>\n {onBack && (\n <button type=\"button\" className=\"vault-back\" onClick={onBack}>\n <ChevronLeft size={14} aria-hidden=\"true\" />\n Back\n </button>\n )}\n <div className=\"vault-header\">\n <div>\n <h1 className=\"vault-header__title\">{title}</h1>\n {tabs && <VaultTabs tabs={tabs} value={value} onChange={onTabChange} />}\n </div>\n {(figure || pill) && (\n <div>\n {figure && <div className=\"vault-header__figure\">{figure}</div>}\n {figureSub && <div className=\"vault-header__sub\">{figureSub}</div>}\n {pill && <div className=\"vault-header__pill-row\">{pill}</div>}\n </div>\n )}\n </div>\n </>\n );\n}\n","/**\n * VaultStatusPill — solid fill, one of the three semantic colours\n * (§1, §4). Sparingly: the record's one current state, never decoration.\n *\n * <VaultStatusPill tone=\"warn\">Effective after 23:59:59</VaultStatusPill>\n */\nexport default function VaultStatusPill({ tone = \"accent\", children, className = \"\" }) {\n return <span className={`vault-pill vault-pill--${tone} ${className}`}>{children}</span>;\n}\n","/**\n * VaultStatTile — a dense KPI figure. Colour only on the left edge, and\n * only when `tone` is set — most tiles stay neutral ink (§5 law 1: colour\n * reserved for state that actually matters, never decoration).\n *\n * <VaultStatTile figure={3} label=\"Approved\" tone=\"accent\" />\n */\nexport default function VaultStatTile({ figure, label, tone, className = \"\" }) {\n const toneVar = tone === \"accent\" ? \"rgb(var(--v-accent))\" : tone === \"warn\" ? \"rgb(var(--v-warn))\" : tone === \"danger\" ? \"rgb(var(--v-danger))\" : undefined;\n return (\n <div className={`vault-stat ${className}`} style={toneVar ? { \"--tone\": toneVar } : undefined}>\n <span className=\"vault-stat__figure\">{figure}</span>\n <span className=\"vault-stat__label\">{label}</span>\n </div>\n );\n}\n","import { Check, X } from \"lucide-react\";\n\n/**\n * Vault UI controls (§4): Button, Checkbox, Input, Select. Dark fields,\n * 8px radius, accent border + ring on focus.\n */\nexport function VaultButton({ variant = \"primary\", icon: Icon, className = \"\", children, ...rest }) {\n return (\n <button type=\"button\" className={`vault-btn vault-btn--${variant} ${className}`} {...rest}>\n {Icon && <Icon size={variant === \"icon\" ? 18 : 15} aria-hidden=\"true\" />}\n {variant !== \"icon\" && children}\n </button>\n );\n}\n\n/** `checked` + `onChange` — a plain toggle, not the main system's native checkbox skin. */\nexport function VaultCheckbox({ checked = false, onChange, label, className = \"\" }) {\n return (\n <label className={`vault-check-row ${className}`} style={{ cursor: \"pointer\" }}>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n className=\"vault-checkbox\"\n onClick={() => onChange?.(!checked)}\n >\n {checked && <Check size={12} aria-hidden=\"true\" />}\n </button>\n {label && <span className=\"vault-check-row__label\">{label}</span>}\n </label>\n );\n}\n\nexport function VaultInput({ clearable = false, onClear, className = \"\", ...rest }) {\n if (!clearable) return <input className={`vault-inp ${className}`} {...rest} />;\n return (\n <span className=\"vault-inp-wrap\">\n <input className={`vault-inp ${className}`} {...rest} />\n <button type=\"button\" onClick={onClear} title=\"Clear\" aria-label=\"Clear\">\n <X size={14} aria-hidden=\"true\" />\n </button>\n </span>\n );\n}\n\nexport function VaultSelect({ className = \"\", children, ...rest }) {\n return (\n <select className={`vault-select ${className}`} {...rest}>\n {children}\n </select>\n );\n}\n","/**\n * VaultTable (§4) — dense, muted sentence-case headers (not the main\n * system's uppercase-tracked style), hairline row dividers, no zebra.\n *\n * Column: { key, label, render?(row), align? }\n */\nexport default function VaultTable({ columns = [], rows = [], getRowId = (row, i) => row.id ?? i, className = \"\" }) {\n return (\n <div className=\"vault-table-wrap\">\n <table className={`vault-table ${className}`}>\n <thead>\n <tr>\n {columns.map((c) => (\n <th key={c.key} style={c.align === \"right\" ? { textAlign: \"right\" } : undefined}>\n {c.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr key={getRowId(row, i)}>\n {columns.map((c) => (\n <td key={c.key} style={c.align === \"right\" ? { textAlign: \"right\" } : undefined}>\n {c.render ? c.render(row) : row[c.key]}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ /*
2
+ * Vault UI — a second design composition (docs/design-system-vault.md).
3
+ * Everything scoped under .vault so it never reaches an Indira Workspace UI
4
+ * screen sharing the same page, and never needs edits to src/index.css.
5
+ */
6
+
7
+ .vault {
8
+ --v-canvas: 13 16 22;
9
+ --v-surface: 20 24 32;
10
+ --v-surface-2: 27 32 42;
11
+ --v-line: 38 44 56;
12
+ --v-ink: 237 239 243;
13
+ --v-ink-muted: 138 145 158;
14
+ --v-ink-faint: 86 93 108;
15
+ --v-accent: 52 201 142;
16
+ --v-accent-on: 8 20 16;
17
+ --v-warn: 224 138 62;
18
+ --v-danger: 224 96 122;
19
+ --v-dur: 150ms;
20
+
21
+ background: rgb(var(--v-canvas));
22
+ color: rgb(var(--v-ink));
23
+ font-variant-numeric: tabular-nums;
24
+ border-radius: 20px;
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* ---- presentation frame for the catalog screen only: a floating window
29
+ on a near-black backdrop, echoing how this composition is usually shown ---- */
30
+ .vault-frame-backdrop { background: #05070c; border-radius: 24px; padding: 32px; }
31
+ .vault-frame { max-width: 1040px; margin: 0 auto; box-shadow: 0 30px 60px rgb(0 0 0 / 0.45); }
32
+
33
+ /* ---- shell: sidebar + top bar ---- */
34
+ .vault-shell { display: flex; min-height: 100%; }
35
+ .vault-shell__sidebar { width: 220px; flex-shrink: 0; display: flex; flex-direction: column; padding: 20px 12px; }
36
+ .vault-shell__brand { display: flex; align-items: center; gap: 10px; padding: 0 8px; margin-bottom: 28px; }
37
+ .vault-shell__brand-mark { width: 28px; height: 28px; border-radius: 8px; background: rgb(var(--v-accent) / 0.16); color: rgb(var(--v-accent)); display: grid; place-items: center; font-weight: 700; font-size: 13px; }
38
+ .vault-shell__brand-name { font-size: 13px; font-weight: 700; letter-spacing: 0.02em; }
39
+ .vault-shell__nav { display: flex; flex-direction: column; gap: 2px; }
40
+ .vault-nav-item {
41
+ display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 10px; border-radius: 8px;
42
+ font-size: 13px; font-weight: 500; color: rgb(var(--v-ink-muted)); background: transparent; border: 0; text-align: left;
43
+ transition: background-color var(--v-dur) ease, color var(--v-dur) ease;
44
+ }
45
+ .vault-nav-item:hover { background: rgb(var(--v-surface)); color: rgb(var(--v-ink)); }
46
+ .vault-nav-item[aria-current="page"] { background: rgb(var(--v-surface)); color: rgb(var(--v-ink)); }
47
+ .vault-nav-item[aria-current="page"] svg { color: rgb(var(--v-accent)); }
48
+ .vault-shell__foot { margin-top: auto; display: flex; flex-direction: column; gap: 2px; padding-top: 12px; }
49
+ .vault-shell__foot-label { font-size: 10px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: rgb(var(--v-ink-faint)); padding: 0 10px; margin-bottom: 6px; }
50
+
51
+ .vault-shell__main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
52
+ .vault-shell__topbar { height: 56px; flex-shrink: 0; display: flex; align-items: center; justify-content: flex-end; gap: 16px; padding: 0 24px; }
53
+ .vault-topbar__icon { width: 32px; height: 32px; border-radius: 9999px; display: grid; place-items: center; color: rgb(var(--v-ink-muted)); background: transparent; border: 0; }
54
+ .vault-topbar__icon:hover { background: rgb(var(--v-surface)); color: rgb(var(--v-ink)); }
55
+ .vault-user { display: flex; align-items: center; gap: 10px; }
56
+ .vault-user__name { font-size: 13px; font-weight: 600; line-height: 16px; }
57
+ .vault-user__role { font-size: 11px; color: rgb(var(--v-ink-muted)); line-height: 14px; }
58
+
59
+ .vault-shell__content { flex: 1; min-width: 0; padding: 8px 28px 28px; overflow-y: auto; }
60
+
61
+ /* ---- avatar ---- */
62
+ .vault-avatar { width: 32px; height: 32px; border-radius: 9999px; display: grid; place-items: center; overflow: hidden; flex-shrink: 0; font-size: 12px; font-weight: 700; color: rgb(var(--v-ink)); background: rgb(var(--v-surface-2)); }
63
+ .vault-avatar > img { width: 100%; height: 100%; object-fit: cover; }
64
+
65
+ /* ---- page header ---- */
66
+ .vault-back {
67
+ display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; margin-bottom: 20px;
68
+ border-radius: 9999px; border: 1px solid rgb(var(--v-line)); background: transparent; color: rgb(var(--v-ink-muted));
69
+ font-size: 12px; font-weight: 500; transition: border-color var(--v-dur) ease, color var(--v-dur) ease;
70
+ }
71
+ .vault-back:hover { color: rgb(var(--v-ink)); border-color: rgb(var(--v-ink-faint)); }
72
+ .vault-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; flex-wrap: wrap; margin-bottom: 24px; }
73
+ .vault-header__title { font-size: 20px; font-weight: 700; margin: 0 0 16px; }
74
+ .vault-header__figure { font-size: 20px; font-weight: 700; text-align: right; }
75
+ .vault-header__sub { font-size: 12px; color: rgb(var(--v-warn)); text-align: right; margin-top: 2px; }
76
+ .vault-header__pill-row { display: flex; justify-content: flex-end; margin-top: 8px; }
77
+
78
+ /* ---- tabs: underline, not the main system's segmented track ---- */
79
+ .vault-tabs { display: flex; align-items: center; gap: 24px; }
80
+ .vault-tab {
81
+ position: relative; background: none; border: 0; padding: 0 0 12px; font-size: 14px; font-weight: 500;
82
+ color: rgb(var(--v-ink-muted)); transition: color var(--v-dur) ease;
83
+ }
84
+ .vault-tab:hover { color: rgb(var(--v-ink)); }
85
+ .vault-tab[aria-selected="true"] { color: rgb(var(--v-ink)); font-weight: 700; }
86
+ .vault-tab[aria-selected="true"]::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: rgb(var(--v-accent)); border-radius: 2px; }
87
+
88
+ /* ---- section ---- */
89
+ .vault-section { margin-bottom: 28px; }
90
+ .vault-section__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
91
+ .vault-section__title { font-size: 15px; font-weight: 700; margin: 0; }
92
+ .vault-section__hint { font-size: 12px; color: rgb(var(--v-ink-muted)); margin: 4px 0 0; }
93
+ .vault-section__hint a { color: rgb(var(--v-accent)); text-decoration: underline; text-underline-offset: 2px; }
94
+
95
+ /* ---- checkbox row ---- */
96
+ .vault-check-row { display: flex; align-items: center; gap: 10px; }
97
+ .vault-checkbox {
98
+ width: 18px; height: 18px; border-radius: 5px; border: 1px solid rgb(var(--v-line)); background: rgb(var(--v-surface));
99
+ display: inline-grid; place-items: center; flex-shrink: 0; padding: 0; color: rgb(var(--v-accent-on));
100
+ transition: background-color var(--v-dur) ease, border-color var(--v-dur) ease;
101
+ }
102
+ .vault-checkbox[aria-checked="true"] { background: rgb(var(--v-accent)); border-color: rgb(var(--v-accent)); }
103
+ .vault-check-row__label { font-size: 13px; color: rgb(var(--v-ink-muted)); }
104
+
105
+ /* ---- form grid ---- */
106
+ .vault-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; align-items: end; }
107
+ .vault-field { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
108
+ .vault-label { font-size: 12px; font-weight: 500; color: rgb(var(--v-ink-muted)); }
109
+ .vault-inp, .vault-select {
110
+ height: 38px; padding: 0 12px; border-radius: 8px; border: 1px solid rgb(var(--v-line)); background: rgb(var(--v-surface));
111
+ color: rgb(var(--v-ink)); font-size: 13px; font-family: inherit; outline: none; width: 100%;
112
+ transition: border-color var(--v-dur) ease, box-shadow var(--v-dur) ease;
113
+ }
114
+ .vault-inp::placeholder { color: rgb(var(--v-ink-faint)); }
115
+ .vault-inp:focus, .vault-select:focus { border-color: rgb(var(--v-accent)); box-shadow: 0 0 0 3px rgb(var(--v-accent) / 0.16); }
116
+ .vault-inp--clear { display: flex; align-items: center; }
117
+ .vault-inp-wrap { position: relative; }
118
+ .vault-inp-wrap > .vault-inp { padding-right: 32px; }
119
+ .vault-inp-wrap > button { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); color: rgb(var(--v-ink-faint)); background: none; border: 0; display: grid; place-items: center; width: 20px; height: 20px; }
120
+ .vault-inp-wrap > button:hover { color: rgb(var(--v-ink)); }
121
+
122
+ /* ---- buttons ---- */
123
+ .vault-btn { display: inline-flex; align-items: center; gap: 8px; border: 0; cursor: pointer; font-family: inherit; transition: background-color var(--v-dur) ease, color var(--v-dur) ease, opacity var(--v-dur) ease; }
124
+ .vault-btn--primary { height: 38px; padding: 0 18px; border-radius: 9999px; background: rgb(var(--v-accent)); color: rgb(var(--v-accent-on)); font-size: 13px; font-weight: 700; }
125
+ .vault-btn--primary:hover { background: rgb(var(--v-accent) / 0.88); }
126
+ .vault-btn--icon { width: 38px; height: 38px; border-radius: 9999px; background: rgb(var(--v-accent)); color: rgb(var(--v-accent-on)); justify-content: center; flex-shrink: 0; }
127
+ .vault-btn--icon:hover { background: rgb(var(--v-accent) / 0.88); }
128
+ .vault-btn--text { height: 28px; padding: 0 4px; background: none; color: rgb(var(--v-ink-muted)); font-size: 12px; font-weight: 500; }
129
+ .vault-btn--text:hover { color: rgb(var(--v-ink)); }
130
+ .vault-btn--danger-text { height: 28px; padding: 0; background: none; color: rgb(var(--v-danger)); font-size: 12px; font-weight: 600; }
131
+ .vault-btn--danger-text:hover { opacity: 0.8; }
132
+
133
+ /* ---- status pill ---- */
134
+ .vault-pill { display: inline-flex; align-items: center; height: 24px; padding: 0 12px; border-radius: 9999px; font-size: 11px; font-weight: 700; white-space: nowrap; }
135
+ .vault-pill--accent { background: rgb(var(--v-accent)); color: rgb(var(--v-accent-on)); }
136
+ .vault-pill--warn { background: rgb(var(--v-warn)); color: rgb(15 12 8); }
137
+ .vault-pill--danger { background: rgb(var(--v-danger) / 0.16); color: rgb(var(--v-danger)); }
138
+
139
+ /* ---- stat tile: dense KPI, colour only when the state actually matters ---- */
140
+ .vault-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 24px; }
141
+ @media (min-width: 900px) { .vault-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
142
+ .vault-stat {
143
+ background: rgb(var(--v-surface)); border-radius: 8px; padding: 14px 16px; text-align: left; border: 0;
144
+ border-left: 3px solid var(--tone, transparent); cursor: default;
145
+ }
146
+ .vault-stat__figure { display: block; font-size: 22px; font-weight: 700; line-height: 1.2; color: rgb(var(--v-ink)); }
147
+ .vault-stat__label { display: block; margin-top: 4px; font-size: 12px; color: rgb(var(--v-ink-muted)); }
148
+
149
+ /* ---- table ---- */
150
+ .vault-table-wrap { background: rgb(var(--v-surface)); border-radius: 12px; overflow: hidden; }
151
+ .vault-table { width: 100%; border-collapse: collapse; }
152
+ .vault-table th {
153
+ text-align: left; font-size: 12px; font-weight: 500; color: rgb(var(--v-ink-muted)); padding: 12px 16px;
154
+ border-bottom: 1px solid rgb(var(--v-line));
155
+ }
156
+ .vault-table td { padding: 12px 16px; font-size: 13px; color: rgb(var(--v-ink)); border-bottom: 1px solid rgb(var(--v-line)); vertical-align: middle; }
157
+ .vault-table tr:last-child td { border-bottom: 0; }
158
+ .vault-table tbody tr { transition: background-color var(--v-dur) ease; }
159
+ .vault-table tbody tr:hover td { background: rgb(var(--v-surface-2)); }
@@ -0,0 +1,279 @@
1
+ import { Fragment as e, jsx as t, jsxs as n } from "react/jsx-runtime";
2
+ import { Bell as r, Check as i, ChevronDown as a, ChevronLeft as o, X as s } from "lucide-react";
3
+ //#region src/vault/VaultAvatar.jsx
4
+ function c({ name: e = "", photo: n, size: r = 32, className: i = "" }) {
5
+ let a = e.trim().split(/\s+/).slice(0, 2).map((e) => e[0]).join("").toUpperCase();
6
+ return /* @__PURE__ */ t("span", {
7
+ className: `vault-avatar ${i}`,
8
+ style: {
9
+ width: r,
10
+ height: r,
11
+ fontSize: Math.round(r * .4)
12
+ },
13
+ children: n ? /* @__PURE__ */ t("img", {
14
+ src: n,
15
+ alt: ""
16
+ }) : a
17
+ });
18
+ }
19
+ //#endregion
20
+ //#region src/vault/VaultShell.jsx
21
+ function l({ brand: e, nav: i = [], activeId: o, onNavigate: s, footNav: l = [], user: d, className: f = "", children: p }) {
22
+ return /* @__PURE__ */ n("div", {
23
+ className: `vault-shell ${f}`,
24
+ children: [/* @__PURE__ */ n("aside", {
25
+ className: "vault-shell__sidebar",
26
+ children: [
27
+ /* @__PURE__ */ n("div", {
28
+ className: "vault-shell__brand",
29
+ children: [/* @__PURE__ */ t("span", {
30
+ className: "vault-shell__brand-mark",
31
+ children: e?.mark ?? "V"
32
+ }), /* @__PURE__ */ t("span", {
33
+ className: "vault-shell__brand-name",
34
+ children: e?.name
35
+ })]
36
+ }),
37
+ /* @__PURE__ */ t("nav", {
38
+ className: "vault-shell__nav",
39
+ children: i.map((e) => /* @__PURE__ */ t(u, {
40
+ item: e,
41
+ active: e.id === o,
42
+ onClick: () => s?.(e.id)
43
+ }, e.id))
44
+ }),
45
+ l.length > 0 && /* @__PURE__ */ n("div", {
46
+ className: "vault-shell__foot",
47
+ children: [/* @__PURE__ */ t("div", {
48
+ className: "vault-shell__foot-label",
49
+ children: "Support"
50
+ }), l.map((e) => /* @__PURE__ */ t(u, {
51
+ item: e,
52
+ active: !1,
53
+ onClick: () => s?.(e.id)
54
+ }, e.id))]
55
+ })
56
+ ]
57
+ }), /* @__PURE__ */ n("div", {
58
+ className: "vault-shell__main",
59
+ children: [/* @__PURE__ */ n("header", {
60
+ className: "vault-shell__topbar",
61
+ children: [/* @__PURE__ */ t("button", {
62
+ type: "button",
63
+ className: "vault-topbar__icon",
64
+ title: "Notifications",
65
+ children: /* @__PURE__ */ t(r, {
66
+ size: 17,
67
+ "aria-hidden": "true"
68
+ })
69
+ }), d && /* @__PURE__ */ n("button", {
70
+ type: "button",
71
+ className: "vault-btn vault-user",
72
+ style: {
73
+ padding: 0,
74
+ background: "none"
75
+ },
76
+ title: d.email,
77
+ children: [
78
+ /* @__PURE__ */ t(c, {
79
+ name: d.name,
80
+ photo: d.photo
81
+ }),
82
+ /* @__PURE__ */ n("span", {
83
+ className: "min-w-0",
84
+ style: { textAlign: "left" },
85
+ children: [/* @__PURE__ */ t("span", {
86
+ className: "vault-user__name block",
87
+ children: d.name
88
+ }), /* @__PURE__ */ t("span", {
89
+ className: "vault-user__role block",
90
+ children: d.role
91
+ })]
92
+ }),
93
+ /* @__PURE__ */ t(a, {
94
+ size: 14,
95
+ "aria-hidden": "true",
96
+ style: { color: "rgb(var(--v-ink-faint))" }
97
+ })
98
+ ]
99
+ })]
100
+ }), /* @__PURE__ */ t("div", {
101
+ className: "vault-shell__content",
102
+ children: p
103
+ })]
104
+ })]
105
+ });
106
+ }
107
+ function u({ item: e, active: r, onClick: i }) {
108
+ let a = e.icon;
109
+ return /* @__PURE__ */ n("button", {
110
+ type: "button",
111
+ className: "vault-nav-item",
112
+ "aria-current": r ? "page" : void 0,
113
+ onClick: i,
114
+ title: e.label,
115
+ children: [a && /* @__PURE__ */ t(a, {
116
+ size: 17,
117
+ "aria-hidden": "true"
118
+ }), /* @__PURE__ */ t("span", {
119
+ className: "truncate",
120
+ children: e.label
121
+ })]
122
+ });
123
+ }
124
+ //#endregion
125
+ //#region src/vault/VaultPageHeader.jsx
126
+ function d({ tabs: e = [], value: n, onChange: r, className: i = "" }) {
127
+ return /* @__PURE__ */ t("div", {
128
+ className: `vault-tabs ${i}`,
129
+ role: "tablist",
130
+ children: e.map((e) => /* @__PURE__ */ t("button", {
131
+ type: "button",
132
+ role: "tab",
133
+ className: "vault-tab",
134
+ "aria-selected": e.value === n,
135
+ onClick: () => r?.(e.value),
136
+ children: e.label
137
+ }, e.value))
138
+ });
139
+ }
140
+ function f({ onBack: r, title: i, tabs: a, value: s, onTabChange: c, figure: l, figureSub: u, pill: f }) {
141
+ return /* @__PURE__ */ n(e, { children: [r && /* @__PURE__ */ n("button", {
142
+ type: "button",
143
+ className: "vault-back",
144
+ onClick: r,
145
+ children: [/* @__PURE__ */ t(o, {
146
+ size: 14,
147
+ "aria-hidden": "true"
148
+ }), "Back"]
149
+ }), /* @__PURE__ */ n("div", {
150
+ className: "vault-header",
151
+ children: [/* @__PURE__ */ n("div", { children: [/* @__PURE__ */ t("h1", {
152
+ className: "vault-header__title",
153
+ children: i
154
+ }), a && /* @__PURE__ */ t(d, {
155
+ tabs: a,
156
+ value: s,
157
+ onChange: c
158
+ })] }), (l || f) && /* @__PURE__ */ n("div", { children: [
159
+ l && /* @__PURE__ */ t("div", {
160
+ className: "vault-header__figure",
161
+ children: l
162
+ }),
163
+ u && /* @__PURE__ */ t("div", {
164
+ className: "vault-header__sub",
165
+ children: u
166
+ }),
167
+ f && /* @__PURE__ */ t("div", {
168
+ className: "vault-header__pill-row",
169
+ children: f
170
+ })
171
+ ] })]
172
+ })] });
173
+ }
174
+ //#endregion
175
+ //#region src/vault/VaultStatusPill.jsx
176
+ function p({ tone: e = "accent", children: n, className: r = "" }) {
177
+ return /* @__PURE__ */ t("span", {
178
+ className: `vault-pill vault-pill--${e} ${r}`,
179
+ children: n
180
+ });
181
+ }
182
+ //#endregion
183
+ //#region src/vault/VaultStatTile.jsx
184
+ function m({ figure: e, label: r, tone: i, className: a = "" }) {
185
+ let o = i === "accent" ? "rgb(var(--v-accent))" : i === "warn" ? "rgb(var(--v-warn))" : i === "danger" ? "rgb(var(--v-danger))" : void 0;
186
+ return /* @__PURE__ */ n("div", {
187
+ className: `vault-stat ${a}`,
188
+ style: o ? { "--tone": o } : void 0,
189
+ children: [/* @__PURE__ */ t("span", {
190
+ className: "vault-stat__figure",
191
+ children: e
192
+ }), /* @__PURE__ */ t("span", {
193
+ className: "vault-stat__label",
194
+ children: r
195
+ })]
196
+ });
197
+ }
198
+ //#endregion
199
+ //#region src/vault/VaultControls.jsx
200
+ function h({ variant: e = "primary", icon: r, className: i = "", children: a, ...o }) {
201
+ return /* @__PURE__ */ n("button", {
202
+ type: "button",
203
+ className: `vault-btn vault-btn--${e} ${i}`,
204
+ ...o,
205
+ children: [r && /* @__PURE__ */ t(r, {
206
+ size: e === "icon" ? 18 : 15,
207
+ "aria-hidden": "true"
208
+ }), e !== "icon" && a]
209
+ });
210
+ }
211
+ function g({ checked: e = !1, onChange: r, label: a, className: o = "" }) {
212
+ return /* @__PURE__ */ n("label", {
213
+ className: `vault-check-row ${o}`,
214
+ style: { cursor: "pointer" },
215
+ children: [/* @__PURE__ */ t("button", {
216
+ type: "button",
217
+ role: "checkbox",
218
+ "aria-checked": e,
219
+ className: "vault-checkbox",
220
+ onClick: () => r?.(!e),
221
+ children: e && /* @__PURE__ */ t(i, {
222
+ size: 12,
223
+ "aria-hidden": "true"
224
+ })
225
+ }), a && /* @__PURE__ */ t("span", {
226
+ className: "vault-check-row__label",
227
+ children: a
228
+ })]
229
+ });
230
+ }
231
+ function _({ clearable: e = !1, onClear: r, className: i = "", ...a }) {
232
+ return e ? /* @__PURE__ */ n("span", {
233
+ className: "vault-inp-wrap",
234
+ children: [/* @__PURE__ */ t("input", {
235
+ className: `vault-inp ${i}`,
236
+ ...a
237
+ }), /* @__PURE__ */ t("button", {
238
+ type: "button",
239
+ onClick: r,
240
+ title: "Clear",
241
+ "aria-label": "Clear",
242
+ children: /* @__PURE__ */ t(s, {
243
+ size: 14,
244
+ "aria-hidden": "true"
245
+ })
246
+ })]
247
+ }) : /* @__PURE__ */ t("input", {
248
+ className: `vault-inp ${i}`,
249
+ ...a
250
+ });
251
+ }
252
+ function v({ className: e = "", children: n, ...r }) {
253
+ return /* @__PURE__ */ t("select", {
254
+ className: `vault-select ${e}`,
255
+ ...r,
256
+ children: n
257
+ });
258
+ }
259
+ //#endregion
260
+ //#region src/vault/VaultTable.jsx
261
+ function y({ columns: e = [], rows: r = [], getRowId: i = (e, t) => e.id ?? t, className: a = "" }) {
262
+ return /* @__PURE__ */ t("div", {
263
+ className: "vault-table-wrap",
264
+ children: /* @__PURE__ */ n("table", {
265
+ className: `vault-table ${a}`,
266
+ children: [/* @__PURE__ */ t("thead", { children: /* @__PURE__ */ t("tr", { children: e.map((e) => /* @__PURE__ */ t("th", {
267
+ style: e.align === "right" ? { textAlign: "right" } : void 0,
268
+ children: e.label
269
+ }, e.key)) }) }), /* @__PURE__ */ t("tbody", { children: r.map((n, r) => /* @__PURE__ */ t("tr", { children: e.map((e) => /* @__PURE__ */ t("td", {
270
+ style: e.align === "right" ? { textAlign: "right" } : void 0,
271
+ children: e.render ? e.render(n) : n[e.key]
272
+ }, e.key)) }, i(n, r))) })]
273
+ })
274
+ });
275
+ }
276
+ //#endregion
277
+ export { c as VaultAvatar, h as VaultButton, g as VaultCheckbox, _ as VaultInput, f as VaultPageHeader, v as VaultSelect, l as VaultShell, m as VaultStatTile, p as VaultStatusPill, y as VaultTable, d as VaultTabs };
278
+
279
+ //# sourceMappingURL=vault.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.mjs","names":[],"sources":["../src/vault/VaultAvatar.jsx","../src/vault/VaultShell.jsx","../src/vault/VaultPageHeader.jsx","../src/vault/VaultStatusPill.jsx","../src/vault/VaultStatTile.jsx","../src/vault/VaultControls.jsx","../src/vault/VaultTable.jsx"],"sourcesContent":["/** Circular avatar restyled for a dark ground — see VaultShell for usage. */\nexport default function VaultAvatar({ name = \"\", photo, size = 32, className = \"\" }) {\n const initials = name\n .trim()\n .split(/\\s+/)\n .slice(0, 2)\n .map((p) => p[0])\n .join(\"\")\n .toUpperCase();\n return (\n <span className={`vault-avatar ${className}`} style={{ width: size, height: size, fontSize: Math.round(size * 0.4) }}>\n {photo ? <img src={photo} alt=\"\" /> : initials}\n </span>\n );\n}\n","import { Bell, ChevronDown } from \"lucide-react\";\nimport VaultAvatar from \"./VaultAvatar\";\n\n/**\n * VaultShell — the sidebar + top bar chrome for Vault UI\n * (docs/design-system-vault.md §3). Dark flows continuously: sidebar,\n * top bar and content are all --v-canvas, no lighter sidebar surface.\n *\n * <VaultShell nav={NAV} activeId={id} onNavigate={setId} brand={{...}} user={{...}}>\n * {content}\n * </VaultShell>\n */\nexport default function VaultShell({ brand, nav = [], activeId, onNavigate, footNav = [], user, className = \"\", children }) {\n return (\n <div className={`vault-shell ${className}`}>\n <aside className=\"vault-shell__sidebar\">\n <div className=\"vault-shell__brand\">\n <span className=\"vault-shell__brand-mark\">{brand?.mark ?? \"V\"}</span>\n <span className=\"vault-shell__brand-name\">{brand?.name}</span>\n </div>\n <nav className=\"vault-shell__nav\">\n {nav.map((item) => (\n <VaultNavItem key={item.id} item={item} active={item.id === activeId} onClick={() => onNavigate?.(item.id)} />\n ))}\n </nav>\n {footNav.length > 0 && (\n <div className=\"vault-shell__foot\">\n <div className=\"vault-shell__foot-label\">Support</div>\n {footNav.map((item) => (\n <VaultNavItem key={item.id} item={item} active={false} onClick={() => onNavigate?.(item.id)} />\n ))}\n </div>\n )}\n </aside>\n <div className=\"vault-shell__main\">\n <header className=\"vault-shell__topbar\">\n <button type=\"button\" className=\"vault-topbar__icon\" title=\"Notifications\">\n <Bell size={17} aria-hidden=\"true\" />\n </button>\n {user && (\n <button type=\"button\" className=\"vault-btn vault-user\" style={{ padding: 0, background: \"none\" }} title={user.email}>\n <VaultAvatar name={user.name} photo={user.photo} />\n <span className=\"min-w-0\" style={{ textAlign: \"left\" }}>\n <span className=\"vault-user__name block\">{user.name}</span>\n <span className=\"vault-user__role block\">{user.role}</span>\n </span>\n <ChevronDown size={14} aria-hidden=\"true\" style={{ color: \"rgb(var(--v-ink-faint))\" }} />\n </button>\n )}\n </header>\n <div className=\"vault-shell__content\">{children}</div>\n </div>\n </div>\n );\n}\n\nfunction VaultNavItem({ item, active, onClick }) {\n const Icon = item.icon;\n return (\n <button type=\"button\" className=\"vault-nav-item\" aria-current={active ? \"page\" : undefined} onClick={onClick} title={item.label}>\n {Icon && <Icon size={17} aria-hidden=\"true\" />}\n <span className=\"truncate\">{item.label}</span>\n </button>\n );\n}\n","import { ChevronLeft } from \"lucide-react\";\n\n/** Underline tabs (§4) — a different composition earns a different tab shape than the main system's segmented track. */\nexport function VaultTabs({ tabs = [], value, onChange, className = \"\" }) {\n return (\n <div className={`vault-tabs ${className}`} role=\"tablist\">\n {tabs.map((t) => (\n <button\n key={t.value}\n type=\"button\"\n role=\"tab\"\n className=\"vault-tab\"\n aria-selected={t.value === value}\n onClick={() => onChange?.(t.value)}\n >\n {t.label}\n </button>\n ))}\n </div>\n );\n}\n\n/**\n * VaultPageHeader — back control, bold title, underline tabs, and a\n * right-hand slot for the one figure that matters most on this page plus\n * its status pill (docs/design-system-vault.md §3).\n *\n * <VaultPageHeader onBack={...} title=\"Marketing\" tabs={...} value={tab} onTabChange={setTab}\n * figure=\"2,345.6789 BTC\" figureSub=\"≈59,339,969.79 CNY\" pill={<VaultStatusPill tone=\"warn\">Effective after 23:59:59</VaultStatusPill>} />\n */\nexport default function VaultPageHeader({ onBack, title, tabs, value, onTabChange, figure, figureSub, pill }) {\n return (\n <>\n {onBack && (\n <button type=\"button\" className=\"vault-back\" onClick={onBack}>\n <ChevronLeft size={14} aria-hidden=\"true\" />\n Back\n </button>\n )}\n <div className=\"vault-header\">\n <div>\n <h1 className=\"vault-header__title\">{title}</h1>\n {tabs && <VaultTabs tabs={tabs} value={value} onChange={onTabChange} />}\n </div>\n {(figure || pill) && (\n <div>\n {figure && <div className=\"vault-header__figure\">{figure}</div>}\n {figureSub && <div className=\"vault-header__sub\">{figureSub}</div>}\n {pill && <div className=\"vault-header__pill-row\">{pill}</div>}\n </div>\n )}\n </div>\n </>\n );\n}\n","/**\n * VaultStatusPill — solid fill, one of the three semantic colours\n * (§1, §4). Sparingly: the record's one current state, never decoration.\n *\n * <VaultStatusPill tone=\"warn\">Effective after 23:59:59</VaultStatusPill>\n */\nexport default function VaultStatusPill({ tone = \"accent\", children, className = \"\" }) {\n return <span className={`vault-pill vault-pill--${tone} ${className}`}>{children}</span>;\n}\n","/**\n * VaultStatTile — a dense KPI figure. Colour only on the left edge, and\n * only when `tone` is set — most tiles stay neutral ink (§5 law 1: colour\n * reserved for state that actually matters, never decoration).\n *\n * <VaultStatTile figure={3} label=\"Approved\" tone=\"accent\" />\n */\nexport default function VaultStatTile({ figure, label, tone, className = \"\" }) {\n const toneVar = tone === \"accent\" ? \"rgb(var(--v-accent))\" : tone === \"warn\" ? \"rgb(var(--v-warn))\" : tone === \"danger\" ? \"rgb(var(--v-danger))\" : undefined;\n return (\n <div className={`vault-stat ${className}`} style={toneVar ? { \"--tone\": toneVar } : undefined}>\n <span className=\"vault-stat__figure\">{figure}</span>\n <span className=\"vault-stat__label\">{label}</span>\n </div>\n );\n}\n","import { Check, X } from \"lucide-react\";\n\n/**\n * Vault UI controls (§4): Button, Checkbox, Input, Select. Dark fields,\n * 8px radius, accent border + ring on focus.\n */\nexport function VaultButton({ variant = \"primary\", icon: Icon, className = \"\", children, ...rest }) {\n return (\n <button type=\"button\" className={`vault-btn vault-btn--${variant} ${className}`} {...rest}>\n {Icon && <Icon size={variant === \"icon\" ? 18 : 15} aria-hidden=\"true\" />}\n {variant !== \"icon\" && children}\n </button>\n );\n}\n\n/** `checked` + `onChange` — a plain toggle, not the main system's native checkbox skin. */\nexport function VaultCheckbox({ checked = false, onChange, label, className = \"\" }) {\n return (\n <label className={`vault-check-row ${className}`} style={{ cursor: \"pointer\" }}>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n className=\"vault-checkbox\"\n onClick={() => onChange?.(!checked)}\n >\n {checked && <Check size={12} aria-hidden=\"true\" />}\n </button>\n {label && <span className=\"vault-check-row__label\">{label}</span>}\n </label>\n );\n}\n\nexport function VaultInput({ clearable = false, onClear, className = \"\", ...rest }) {\n if (!clearable) return <input className={`vault-inp ${className}`} {...rest} />;\n return (\n <span className=\"vault-inp-wrap\">\n <input className={`vault-inp ${className}`} {...rest} />\n <button type=\"button\" onClick={onClear} title=\"Clear\" aria-label=\"Clear\">\n <X size={14} aria-hidden=\"true\" />\n </button>\n </span>\n );\n}\n\nexport function VaultSelect({ className = \"\", children, ...rest }) {\n return (\n <select className={`vault-select ${className}`} {...rest}>\n {children}\n </select>\n );\n}\n","/**\n * VaultTable (§4) — dense, muted sentence-case headers (not the main\n * system's uppercase-tracked style), hairline row dividers, no zebra.\n *\n * Column: { key, label, render?(row), align? }\n */\nexport default function VaultTable({ columns = [], rows = [], getRowId = (row, i) => row.id ?? i, className = \"\" }) {\n return (\n <div className=\"vault-table-wrap\">\n <table className={`vault-table ${className}`}>\n <thead>\n <tr>\n {columns.map((c) => (\n <th key={c.key} style={c.align === \"right\" ? { textAlign: \"right\" } : undefined}>\n {c.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, i) => (\n <tr key={getRowId(row, i)}>\n {columns.map((c) => (\n <td key={c.key} style={c.align === \"right\" ? { textAlign: \"right\" } : undefined}>\n {c.render ? c.render(row) : row[c.key]}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ # Vault UI — a second design composition, for serious data
2
+
3
+ Indira Workspace UI (`docs/design-system.md`) is the default: light, one rose
4
+ accent, soft rounded shapes. It is right for governance/workflow tools where
5
+ the *product* should feel approachable.
6
+
7
+ Vault UI is the other option. It exists for screens where the *data itself*
8
+ carries the seriousness — balances, spend limits, custody/treasury,
9
+ compliance ledgers — and a light, friendly surface would undersell that.
10
+ Dark by default (it does not ship a light mode; committing to dark is part
11
+ of the seriousness), one desaturated accent, moderate rounding, dense
12
+ tabular layout. Inspired by custody/treasury dashboards (Fireblocks, BitGo,
13
+ Coinbase Custody-style admin tools) — not a literal clone of any one of
14
+ them.
15
+
16
+ Same method as the main system: a token set, a fixed set of scales, one
17
+ shell, a small set of components, a short list of laws. Component code
18
+ lives in `src/vault/`, styles in `src/vault/vault.css`, everything scoped
19
+ under one root class (`.vault`) so it never bleeds into an Indira Workspace
20
+ UI screen sharing the same page.
21
+
22
+ ---
23
+
24
+ ## 1. Tokens
25
+
26
+ RGB triplets, same convention as the main system, so any colour can take an
27
+ opacity modifier.
28
+
29
+ | Token | Value | Role |
30
+ | --- | --- | --- |
31
+ | `--v-canvas` | `13 16 22` | Page ground. Sidebar, header and content panel are all this — dark flows continuously, no lighter sidebar. |
32
+ | `--v-surface` | `20 24 32` | Raised regions: the table block, inputs, the active-nav pill. |
33
+ | `--v-surface-2` | `27 32 42` | Hover / pressed state on a surface. |
34
+ | `--v-line` | `38 44 56` | Hairlines: table row dividers, input borders at rest. |
35
+ | `--v-ink` | `237 239 243` | Primary text. |
36
+ | `--v-ink-muted` | `138 145 158` | Secondary text, column headers, placeholders. |
37
+ | `--v-ink-faint` | `86 93 108` | Tertiary — disabled, least important. |
38
+ | `--v-accent` | `52 201 142` | The one accent: primary button, focus ring, active tab underline, positive status, checkbox check. |
39
+ | `--v-accent-on` | `8 20 16` | Text/icon on the accent fill. |
40
+ | `--v-warn` | `224 138 62` | Pending / "effective after" / needs-attention status. Used on a pill only, never decoratively. |
41
+ | `--v-danger` | `224 96 122` | Destructive text action (e.g. "Remove"). Text colour only — no filled danger buttons in this set; a destructive action here is a plain link-weight word, confirmed before it fires. |
42
+
43
+ Exactly three semantic colours (`accent`, `warn`, `danger`) — the same
44
+ discipline as the main system's six statuses, just a smaller palette
45
+ because Vault screens carry fewer distinct states. A fourth is a product
46
+ decision, not a per-screen one.
47
+
48
+ ## 2. Scales
49
+
50
+ - **Radius**: `8px` controls (inputs, buttons, table block), `9999px` pills
51
+ and circular icon buttons and avatars. Two values, not four — this system
52
+ doesn't need the main system's card/nav-item middle step.
53
+ - **Type**: same family as the main system (Inter / system sans). Sizes:
54
+ `11px` column headers and micro-labels, `13px` body/table cells, `14px`
55
+ controls, `16px` section titles, `20px` page title, `24px` the balance
56
+ figure. Numbers are always `tabular-nums`.
57
+ - **Spacing**: 4/8 grid, same as the main system. Sidebar `220px`, header
58
+ row `64px`, panel padding `24px`, table cell padding `12px 16px`.
59
+ - **Motion**: `150ms` ease, background/border/colour transitions only. No
60
+ spring, no bounce — same law as the main system.
61
+
62
+ ## 3. Shell
63
+
64
+ Sidebar (dark, `220px`, icon + label nav, active item gets a `--v-surface`
65
+ pill behind it and an accent-tinted icon) and a slim top bar (icon actions,
66
+ bell, avatar + name + role + chevron, right-aligned) — no search field by
67
+ default; Vault screens are narrower in scope than a full workspace. A page
68
+ inside it gets its own header block: a "Back" control, a bold title, an
69
+ underline tab row for switching views, and — right-aligned — the figure
70
+ that matters most on this page (a balance, a total, a count) plus a status
71
+ pill for the record's current state.
72
+
73
+ ## 4. Components
74
+
75
+ - **`VaultShell`** — the sidebar + top bar chrome described above.
76
+ - **`VaultPageHeader`** — back control, title, `VaultTabs`, and a right-hand
77
+ slot for a figure + `VaultStatusPill`.
78
+ - **`VaultTabs`** — underline style: active tab is ink text over a
79
+ `2px` accent underline; inactive tabs are muted text, no track/pill
80
+ (this is *not* the main system's segmented-track tabs — a different
81
+ composition earns a different tab treatment).
82
+ - **Controls** (`VaultButton`, `VaultCheckbox`, `VaultInput`, `VaultSelect`)
83
+ — dark fields, `8px` radius, accent border + ring on focus. `VaultButton`
84
+ variants: `primary` (solid accent), `icon` (circular, for the one
85
+ add/create action per section), `text` (row actions), `danger-text` (the
86
+ one destructive action, coloured, never filled).
87
+ - **`VaultStatusPill`** — pill, solid fill in one of the three semantic
88
+ colours, white/on-colour text. Sparingly: the record's one current state,
89
+ never decoration.
90
+ - **`VaultTable`** — dense, header row in muted sentence-case (not the main
91
+ system's uppercase-tracked style — Vault's own voice), hairline row
92
+ dividers, no zebra striping, a `text`/`danger-text` button for row
93
+ actions.
94
+ - **`VaultAvatar`** — circular, photo or initials, same idea as the main
95
+ system's Avatar, restyled for a dark ground.
96
+
97
+ ## 5. Laws
98
+
99
+ 1. **One accent, spent twice at most per view**: the primary button and the
100
+ one positive/active status. Warn and danger are for state, never chrome.
101
+ 2. **Dark, not dimmed** — this is not the main system's palette with the
102
+ lights off; every token above is authored for a dark ground, so contrast
103
+ and the accent's saturation are tuned for it directly.
104
+ 3. **Density is the point.** A Vault screen assumes the reader wants to
105
+ scan a lot of figures fast — tabular numbers, tight rows, minimal
106
+ decoration. If a control isn't load-bearing, it isn't on the screen.
107
+ 4. **Read-only is visible.** A view a person can't act on says so (a
108
+ labelled state, not just the absence of a button) — the main system's
109
+ law 3 (no over-data) and law 5 (status is a shape and a colour) both
110
+ still apply here; this system just draws them in a different hand.
111
+ 5. **No destructive action fires unconfirmed.** `danger-text` is a trigger,
112
+ not the action itself — same rule as the main system's law 14, restated
113
+ because this system's whole reason to exist is data where that matters.
114
+
115
+ ---
116
+
117
+ ## 6. Building it
118
+
119
+ 1. Tokens + component CSS live in `src/vault/vault.css`, scoped under one
120
+ `.vault` root class — never edit `src/index.css` or the main system's
121
+ component classes to make room for this one.
122
+ 2. Build `VaultShell` first, then controls, then `VaultTable` and
123
+ `VaultPageHeader`.
124
+ 3. A screen either lives entirely in Indira Workspace UI or entirely in
125
+ Vault UI — the two are never mixed on one screen (that's exactly the
126
+ "variety reads as generated" failure the main system's law 1 warns
127
+ about, just as true across two systems as within one).
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "indira-workspace-ui",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
- "description": "Indira Workspace UI — the component library specified in docs/design-system.md, plus a CLI to install the indira-ui and indira-api Claude Code skills",
6
+ "description": "Indira Workspace UI — the component library specified in docs/design-system.md, plus Vault UI (docs/design-system-vault.md, a second dark/serious-data composition) and a CLI to install the indira-ui and indira-api Claude Code skills",
7
7
  "keywords": [
8
8
  "react",
9
9
  "component-library",
@@ -17,7 +17,8 @@
17
17
  "bin",
18
18
  ".claude/skills/indira-ui",
19
19
  ".claude/skills/indira-api",
20
- "docs/design-system.md"
20
+ "docs/design-system.md",
21
+ "docs/design-system-vault.md"
21
22
  ],
22
23
  "main": "./dist-lib/indira-workspace-ui.cjs",
23
24
  "module": "./dist-lib/indira-workspace-ui.mjs",
@@ -26,7 +27,12 @@
26
27
  "import": "./dist-lib/indira-workspace-ui.mjs",
27
28
  "require": "./dist-lib/indira-workspace-ui.cjs"
28
29
  },
29
- "./style.css": "./dist-lib/style.css"
30
+ "./style.css": "./dist-lib/style.css",
31
+ "./vault": {
32
+ "import": "./dist-lib/vault.mjs",
33
+ "require": "./dist-lib/vault.cjs"
34
+ },
35
+ "./vault/style.css": "./dist-lib/vault.css"
30
36
  },
31
37
  "bin": {
32
38
  "indira-workspace-ui": "bin/cli.js"