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 +22 -3
- package/dist-lib/vault.cjs +2 -0
- package/dist-lib/vault.cjs.map +1 -0
- package/dist-lib/vault.css +159 -0
- package/dist-lib/vault.mjs +279 -0
- package/dist-lib/vault.mjs.map +1 -0
- package/docs/design-system-vault.md +127 -0
- package/package.json +10 -4
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
|
|
14
|
-
|
|
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":"gIACA,SAAwB,EAAY,CAAE,OAAO,GAAI,QAAO,OAAO,GAAI,YAAY,IAAM,CACnF,IAAM,EAAW,EACd,KAAK,CAAC,CACN,MAAM,KAAK,CAAC,CACZ,MAAM,EAAG,CAAC,CAAC,CACX,IAAK,GAAM,EAAE,EAAE,CAAC,CAChB,KAAK,EAAE,CAAC,CACR,YAAY,EACf,OACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,gBAAgB,IAAa,MAAO,CAAE,MAAO,EAAM,OAAQ,EAAM,SAAU,KAAK,MAAM,EAAO,EAAG,CAAE,EAChH,SAAA,GAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,IAAK,EAAO,IAAI,EAAI,CAAA,EAAI,CAClC,CAAA,CAEV,CCFA,SAAwB,EAAW,CAAE,QAAO,MAAM,CAAC,EAAG,WAAU,aAAY,UAAU,CAAC,EAAG,OAAM,YAAY,GAAI,YAAY,CAC1H,OACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,eAAe,IAA/B,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,QAAD,CAAO,UAAU,uBAAjB,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,qBAAf,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,0BAA2B,SAAA,GAAO,MAAQ,GAAU,CAAA,GACpE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,0BAA2B,SAAA,GAAO,IAAW,CAAA,CAC1D,KACL,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,mBACZ,SAAA,EAAI,IAAK,IACR,EAAA,EAAA,IAAA,CAAC,EAAD,CAAkC,OAAM,OAAQ,EAAK,KAAO,EAAU,YAAe,IAAa,EAAK,EAAE,CAAI,EAA1F,EAAK,EAAqF,CAC9G,CACE,CAAA,EACJ,EAAQ,OAAS,IAChB,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,oBAAf,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,0BAA0B,SAAA,SAAY,CAAA,EACpD,EAAQ,IAAK,IACZ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAkC,OAAM,OAAQ,GAAO,YAAe,IAAa,EAAK,EAAE,CAAI,EAA3E,EAAK,EAAsE,CAC/F,CACE,GAEF,CACP,CAAA,GAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,oBAAf,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAU,sBAAlB,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,UAAU,qBAAqB,MAAM,gBACzD,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,KAAD,CAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,CAC9B,CAAA,EACP,IACC,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,UAAU,uBAAuB,MAAO,CAAE,QAAS,EAAG,WAAY,MAAO,EAAG,MAAO,EAAK,MAA9G,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,EAAD,CAAa,KAAM,EAAK,KAAM,MAAO,EAAK,KAAQ,CAAA,GAClD,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,UAAU,MAAO,CAAE,UAAW,MAAO,EAArD,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,yBAA0B,SAAA,EAAK,IAAW,CAAA,GAC1D,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,yBAA0B,SAAA,EAAK,IAAW,CAAA,CACtD,KACN,EAAA,EAAA,IAAA,CAAC,EAAA,YAAD,CAAa,KAAM,GAAI,cAAY,OAAO,MAAO,CAAE,MAAO,yBAA0B,CAAI,CAAA,CAClF,CAEJ,CAAA,CAAA,CACR,CAAA,GAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,uBAAwB,UAAc,CAAA,CAClD,CACF,CAAA,CAAA,GAET,CAEA,SAAS,EAAa,CAAE,OAAM,SAAQ,WAAW,CAC/C,IAAM,EAAO,EAAK,KAClB,OACE,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,UAAU,iBAAiB,eAAc,EAAS,OAAS,IAAA,GAAoB,UAAS,MAAO,EAAK,MAA1H,SAAA,CACG,IAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,KAAM,GAAI,cAAY,MAAQ,CAAA,GAC7C,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,WAAY,SAAA,EAAK,KAAY,CAAA,CACvC,GAEZ,CC7DA,SAAgB,EAAU,CAAE,OAAO,CAAC,EAAG,QAAO,WAAU,YAAY,IAAM,CACxE,OACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,cAAc,IAAa,KAAK,UAC7C,SAAA,EAAK,IAAK,IACT,EAAA,EAAA,IAAA,CAAC,SAAD,CAEE,KAAK,SACL,KAAK,MACL,UAAU,YACV,gBAAe,EAAE,QAAU,EAC3B,YAAe,IAAW,EAAE,KAAK,EAEhC,SAAA,EAAE,KACG,EARD,EAAE,KAQD,CACT,CACE,CAAA,CAET,CAUA,SAAwB,EAAgB,CAAE,SAAQ,QAAO,OAAM,QAAO,cAAa,SAAQ,YAAW,QAAQ,CAC5G,OACE,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACG,IACC,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,UAAU,aAAa,QAAS,EAAtD,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,EAAA,YAAD,CAAa,KAAM,GAAI,cAAY,MAAQ,CAAA,EAAC,MAEtC,CAEV,CAAA,GAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,eAAf,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,sBAAuB,SAAA,CAAU,CAAA,EAC9C,IAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAiB,OAAa,QAAO,SAAU,CAAc,CAAA,CACnE,CAAA,CAAA,GACH,GAAU,KACV,EAAA,EAAA,KAAA,CAAC,MAAD,CAAA,SAAA,CACG,IAAU,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,uBAAwB,SAAA,CAAY,CAAA,EAC7D,IAAa,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,oBAAqB,SAAA,CAAe,CAAA,EAChE,IAAQ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,yBAA0B,SAAA,CAAU,CAAA,CACzD,CAAA,CAAA,CAEJ,CACL,CAAA,CAAA,CAAA,CAAA,CAEN,CChDA,SAAwB,EAAgB,CAAE,OAAO,SAAU,WAAU,YAAY,IAAM,CACrF,OAAO,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,0BAA0B,EAAK,GAAG,IAAc,UAAe,CAAA,CACzF,CCDA,SAAwB,EAAc,CAAE,SAAQ,QAAO,OAAM,YAAY,IAAM,CAC7E,IAAM,EAAU,IAAS,SAAW,uBAAyB,IAAS,OAAS,qBAAuB,IAAS,SAAW,uBAAyB,IAAA,GACnJ,OACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,cAAc,IAAa,MAAO,EAAU,CAAE,SAAU,CAAQ,EAAI,IAAA,GAApF,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,qBAAsB,SAAA,CAAa,CAAA,GACnD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,oBAAqB,SAAA,CAAY,CAAA,CAC9C,GAET,CCTA,SAAgB,EAAY,CAAE,UAAU,UAAW,KAAM,EAAM,YAAY,GAAI,WAAU,GAAG,GAAQ,CAClG,OACE,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,UAAW,wBAAwB,EAAQ,GAAG,IAAa,GAAI,EAArF,SAAA,CACG,IAAQ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAM,KAAM,IAAY,OAAS,GAAK,GAAI,cAAY,MAAQ,CAAA,EACtE,IAAY,QAAU,CACjB,GAEZ,CAGA,SAAgB,EAAc,CAAE,UAAU,GAAO,WAAU,QAAO,YAAY,IAAM,CAClF,OACE,EAAA,EAAA,KAAA,CAAC,QAAD,CAAO,UAAW,mBAAmB,IAAa,MAAO,CAAE,OAAQ,SAAU,EAA7E,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACE,KAAK,SACL,KAAK,WACL,eAAc,EACd,UAAU,iBACV,YAAe,IAAW,CAAC,CAAO,EAEjC,SAAA,IAAW,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CAAO,KAAM,GAAI,cAAY,MAAQ,CAAA,CAC3C,CAAA,EACP,IAAS,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAU,yBAA0B,SAAA,CAAY,CAAA,CAC3D,GAEX,CAEA,SAAgB,EAAW,CAAE,YAAY,GAAO,UAAS,YAAY,GAAI,GAAG,GAAQ,CAElF,OADK,GAEH,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,iBAAhB,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,QAAD,CAAO,UAAW,aAAa,IAAa,GAAI,CAAO,CAAA,GACvD,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,KAAK,SAAS,QAAS,EAAS,MAAM,QAAQ,aAAW,QAC/D,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAG,KAAM,GAAI,cAAY,MAAQ,CAAA,CAC3B,CAAA,CACJ,KAPe,EAAA,EAAA,IAAA,CAAC,QAAD,CAAO,UAAW,aAAa,IAAa,GAAI,CAAO,CAAA,CAShF,CAEA,SAAgB,EAAY,CAAE,YAAY,GAAI,WAAU,GAAG,GAAQ,CACjE,OACE,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,gBAAgB,IAAa,GAAI,EACjD,UACK,CAAA,CAEZ,CC7CA,SAAwB,EAAW,CAAE,UAAU,CAAC,EAAG,OAAO,CAAC,EAAG,YAAY,EAAK,IAAM,EAAI,IAAM,EAAG,YAAY,IAAM,CAClH,OACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,mBACb,UAAA,EAAA,EAAA,KAAA,CAAC,QAAD,CAAO,UAAW,eAAe,IAAjC,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,QAAD,CAAA,UACE,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,SACG,EAAQ,IAAK,IACZ,EAAA,EAAA,IAAA,CAAC,KAAD,CAAgB,MAAO,EAAE,QAAU,QAAU,CAAE,UAAW,OAAQ,EAAI,IAAA,GACnE,SAAA,EAAE,KACD,EAFK,EAAE,GAEP,CACL,CACC,CAAA,CACC,CAAA,GACP,EAAA,EAAA,IAAA,CAAC,QAAD,CAAA,SACG,EAAK,KAAK,EAAK,KACd,EAAA,EAAA,IAAA,CAAC,KAAD,CAAA,SACG,EAAQ,IAAK,IACZ,EAAA,EAAA,IAAA,CAAC,KAAD,CAAgB,MAAO,EAAE,QAAU,QAAU,CAAE,UAAW,OAAQ,EAAI,IAAA,GACnE,SAAA,EAAE,OAAS,EAAE,OAAO,CAAG,EAAI,EAAI,EAAE,IAChC,EAFK,EAAE,GAEP,CACL,CACC,EANK,EAAS,EAAK,CAAC,CAMpB,CACL,CACI,CAAA,CACF,GACJ,CAAA,CAET"}
|
|
@@ -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.
|
|
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"
|