Apply the Kestrove design system to the desktop UI
- src/ui: tokens.css (generated from the design system's tokens.json), kv.css components, fonts.css with the bundled Inter and JetBrains Mono woff2, kv.ts DOM builders (icons, tiles, item rows, secrets, codes, TOTP ring, pills, banners). The browser extension uses the same module. - Layout: custom title bar in bg (macOS overlay title bar, traffic lights kept), left rail, list and detail. - Item detail: field rows with 10-dot masking, coloured digits/symbols on reveal (re-masks after 30 s), copy turns into a check for 1.5 s, TOTP in code-lg with the ring. - Add-a-device QR card, Use my phone QR with countdown and confirmation code, approve sheet. - Status pills, banners and the integrity alert; Light / Dark / System in Settings (also sets the window theme). - totp command returns the period for the ring. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+101
-58
@@ -1,58 +1,101 @@
|
||||
:root { color-scheme: light dark; font: 14px system-ui, sans-serif; }
|
||||
body { margin: 0; }
|
||||
main { display: grid; grid-template-columns: 300px 1fr; height: 100vh; overflow: auto; }
|
||||
.center { display: grid; place-items: center; min-height: 100vh; padding: 16px 0; box-sizing: border-box; grid-column: 1 / -1; }
|
||||
.card { display: grid; gap: 8px; width: 340px; }
|
||||
input, button { font: inherit; padding: 8px; border-radius: 6px; border: 1px solid #8886; }
|
||||
button { cursor: pointer; }
|
||||
aside { border-right: 1px solid #8884; overflow: auto; }
|
||||
aside li { list-style: none; padding: 8px 12px; cursor: pointer; }
|
||||
aside li:hover, aside li.sel { background: #8882; }
|
||||
aside ul { margin: 0; padding: 0; }
|
||||
section { padding: 16px; }
|
||||
.muted { opacity: .7; }
|
||||
.err { color: #d33; }
|
||||
.mono { font-family: ui-monospace, monospace; }
|
||||
.toolbar { display: flex; gap: 8px; padding: 8px; }
|
||||
h3 { margin: 16px 0 8px; }
|
||||
select { font: inherit; padding: 6px; border-radius: 6px; }
|
||||
.row { display: flex; gap: 8px; align-items: center; }
|
||||
.pad { padding: 0 12px; }
|
||||
.primary { background: #2c6e86; color: white; border-color: transparent; }
|
||||
.danger { color: #d33; }
|
||||
ul.plain { padding: 0; }
|
||||
ul.plain li { list-style: none; padding: 8px 0; border-bottom: 1px solid #8883; display: grid; gap: 4px; justify-items: start; }
|
||||
#overlay { position: fixed; inset: 0; background: #0006; display: grid; place-content: center; gap: 12px; }
|
||||
#overlay[hidden] { display: none; }
|
||||
.prompt { background: Canvas; color: CanvasText; border-radius: 10px; padding: 16px 20px; width: 420px; box-shadow: 0 10px 40px #0005; }
|
||||
.words { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
|
||||
.words span { font: 600 15px ui-monospace, monospace; padding: 4px 8px; border-radius: 6px; background: #8882; }
|
||||
#modal { position: fixed; inset: 0; background: #0006; display: grid; place-content: center; z-index: 2; }
|
||||
#modal[hidden] { display: none; }
|
||||
#modal .prompt { display: grid; gap: 8px; max-height: 90vh; overflow: auto; }
|
||||
aside { display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; }
|
||||
main > section { height: 100vh; overflow: auto; box-sizing: border-box; }
|
||||
aside > ul { flex: 1; }
|
||||
.sync { border-top: 1px solid #8884; padding: 8px 12px; display: grid; gap: 8px; }
|
||||
.between { justify-content: space-between; }
|
||||
.small { font-size: 12px; }
|
||||
.big { font-size: 18px; }
|
||||
.grid { display: grid; gap: 8px; }
|
||||
.card.wide { width: 440px; }
|
||||
.tabs { display: flex; gap: 4px; flex-wrap: wrap; }
|
||||
.tab { border-color: transparent; background: none; }
|
||||
.tab.sel { background: #8883; font-weight: 600; }
|
||||
textarea { font: inherit; padding: 8px; border-radius: 6px; border: 1px solid #8886; resize: vertical; width: 100%; box-sizing: border-box; }
|
||||
.qr { width: 240px; height: 240px; justify-self: center; image-rendering: pixelated; border-radius: 6px; }
|
||||
.code { text-align: center; padding: 8px; border-radius: 8px; background: #8881; }
|
||||
.digits { font: 700 32px ui-monospace, monospace; letter-spacing: 2px; text-align: center; }
|
||||
.steps { margin: 0; padding-left: 20px; display: grid; gap: 4px; }
|
||||
.notice { background: #2c6e8622; border-radius: 8px; padding: 8px 10px; }
|
||||
.alarm { background: #d3333322; border: 1px solid #d33; border-radius: 8px; padding: 8px 10px; }
|
||||
.alarm p { margin: 4px 0 0; }
|
||||
.warn { color: #b26b00; }
|
||||
.link { border: none; background: none; color: #2c6e86; padding: 0; text-decoration: underline; justify-self: start; }
|
||||
.facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0 0 8px; }
|
||||
.facts dd { margin: 0; overflow-wrap: anywhere; }
|
||||
details { display: grid; gap: 6px; }
|
||||
summary { cursor: pointer; }
|
||||
/* Desktop layout. Colours, type and components come from the design system (./ui). */
|
||||
@import "./ui/fonts.css";
|
||||
@import "./ui/tokens.css";
|
||||
@import "./ui/kv.css";
|
||||
|
||||
html, body { margin: 0; height: 100%; }
|
||||
body { overflow: hidden; }
|
||||
#app { height: 100vh; display: flex; flex-direction: column; }
|
||||
p { margin: 0; }
|
||||
h1, h2, h3 { margin: 0; }
|
||||
.grow { flex: 1 1 auto; min-width: 0; }
|
||||
.row { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
||||
.stack { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
|
||||
.actions { display: flex; gap: var(--space-2); align-items: center; }
|
||||
.actions.end { justify-content: flex-end; }
|
||||
.center-row { display: flex; justify-content: center; }
|
||||
.fingerprint { font-family: var(--font-mono); font-variant-ligatures: none; letter-spacing: 0.04em; word-spacing: 0.2em; }
|
||||
.fingerprint.lg { font-size: 17px; line-height: 26px; }
|
||||
.kv-error-text { color: var(--danger); }
|
||||
details { display: grid; gap: var(--space-2); }
|
||||
summary { cursor: pointer; color: var(--text-2); }
|
||||
|
||||
/* Custom title bar in bg with the native window controls. */
|
||||
.titlebar {
|
||||
height: 44px; flex: none; display: flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-3) 0 var(--space-4);
|
||||
background: var(--bg); border-bottom: 1px solid var(--border); user-select: none; -webkit-user-select: none;
|
||||
}
|
||||
.mac .titlebar { padding-left: 84px; }
|
||||
.kv-word.sm { font-size: 15px; line-height: 20px; }
|
||||
|
||||
/* Segmented control: border-control outline (WCAG 1.4.11). */
|
||||
.seg { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 2px; border: 1px solid var(--border-control); border-radius: var(--radius); background: var(--surface-2); align-self: flex-start; }
|
||||
.seg .kv-btn { height: 32px; background: transparent; color: var(--text-2); border-radius: 8px; font-weight: 600; padding: 0 var(--space-3); }
|
||||
.seg .kv-btn[aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
|
||||
|
||||
/* Unlock and onboarding */
|
||||
#app.auth { background: var(--bg); }
|
||||
.center { flex: 1; overflow: auto; display: grid; place-items: center; padding: var(--space-12) var(--space-4); }
|
||||
.auth-card { width: 400px; max-width: 100%; padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-5); box-shadow: var(--shadow-sm); }
|
||||
.auth-card.wide { width: 580px; }
|
||||
.unlock-mark { display: flex; justify-content: center; }
|
||||
.auth-card .kv-title-1 { text-align: left; }
|
||||
.secret-key { padding: var(--space-3) var(--space-4); border-radius: var(--radius); background: var(--surface-2); }
|
||||
.secret-key .kv-secret { font-size: 17px; line-height: 26px; }
|
||||
|
||||
/* QR card and confirmation code (AddDevice) */
|
||||
.qr-sheet { display: flex; flex-direction: column; gap: var(--space-3); align-items: center; text-align: center; }
|
||||
.qr-sheet .steps { align-self: stretch; text-align: left; }
|
||||
.steps { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); }
|
||||
.confirm { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); min-height: 54px; justify-content: center; }
|
||||
.words { display: flex; flex-wrap: wrap; gap: var(--space-1\.5); }
|
||||
.words span { font: 600 15px/22px var(--font-mono); padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); background: var(--surface-2); }
|
||||
|
||||
/* Vault: left rail, list, detail */
|
||||
#app.shell { background: var(--bg); }
|
||||
.alarm-slot:not(:empty) { padding: var(--space-3) var(--space-4) 0; }
|
||||
.cols { flex: 1; min-height: 0; display: grid; grid-template-columns: 200px minmax(260px, 340px) 1fr; }
|
||||
.rail { background: var(--surface-2); padding: var(--space-3) var(--space-2); display: flex; flex-direction: column; gap: 2px; overflow: auto; border-right: 1px solid var(--border); }
|
||||
.rail-h { padding: var(--space-3) var(--space-2\.5) var(--space-1); }
|
||||
.rail-item {
|
||||
display: flex; align-items: center; gap: var(--space-2\.5); padding: var(--space-1\.5) var(--space-2\.5); border-radius: var(--radius-sm);
|
||||
border: 0; background: transparent; color: var(--text-2); font: 400 14px/20px var(--font-sans); cursor: pointer; text-align: left;
|
||||
}
|
||||
.rail-item:hover { background: var(--surface-3); color: var(--text); }
|
||||
.rail-item[aria-current="page"] { background: var(--primary-soft); color: var(--text); font-weight: 600; }
|
||||
.rail-foot { display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-2); }
|
||||
.rail-foot .kv-banner { padding: var(--space-2\.5) var(--space-3); font-size: 13px; line-height: 18px; gap: var(--space-2); }
|
||||
.sync-line { display: flex; align-items: center; gap: var(--space-1); padding: 0 var(--space-1) 0 var(--space-2\.5); }
|
||||
.listpane { background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; min-height: 0; }
|
||||
.list-head { display: flex; gap: var(--space-2); padding: var(--space-3); border-bottom: 1px solid var(--border); }
|
||||
.search { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-2\.5); height: var(--control); border-radius: var(--radius); background: var(--surface-2); color: var(--text-3); }
|
||||
.search:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; }
|
||||
.search input { flex: 1; min-width: 0; border: 0; background: transparent; color: var(--text); font: 400 14px/20px var(--font-sans); height: 100%; outline: none; }
|
||||
.search input::placeholder { color: var(--text-3); }
|
||||
.listpane .kv-list { flex: 1; overflow: auto; }
|
||||
.empty { padding: var(--space-6) var(--space-4); text-align: center; }
|
||||
.detail { background: var(--surface); overflow: auto; padding: var(--space-5) var(--space-6) var(--space-12); min-width: 0; }
|
||||
.empty-detail { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-3); opacity: 0.9; }
|
||||
.detail-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-5); }
|
||||
.fields { overflow: hidden; }
|
||||
.notes { white-space: pre-wrap; }
|
||||
.editor { max-width: 520px; }
|
||||
.editor .row .kv-field { flex: 1; }
|
||||
|
||||
/* Settings */
|
||||
.settings { max-width: 640px; gap: var(--space-6); }
|
||||
.settings-section { display: flex; flex-direction: column; gap: var(--space-2\.5); }
|
||||
.rows { overflow: hidden; }
|
||||
.pad { padding: var(--space-4); }
|
||||
.badges { display: inline-flex; gap: var(--space-1); }
|
||||
.device-ico { color: var(--text-2); display: grid; }
|
||||
|
||||
/* Dialogs and browser prompts */
|
||||
#overlay, #modal { position: fixed; inset: 0; background: rgb(6 14 11 / 0.5); display: grid; place-content: center; gap: var(--space-3); padding: var(--space-4); }
|
||||
#modal { z-index: 2; }
|
||||
#overlay[hidden], #modal[hidden] { display: none; }
|
||||
.prompt { width: 440px; max-width: calc(100vw - 32px); max-height: calc(100vh - 48px); overflow: auto; padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.prompt { animation: pop 160ms var(--ease); }
|
||||
}
|
||||
@keyframes pop { from { opacity: 0; transform: translateY(6px) scale(0.985); } }
|
||||
|
||||
Reference in New Issue
Block a user