feat(design): surface phase PR 6 — Settings polish

Settings is the densest button surface in the app. Per the surface-
phase spec, classify every button on a deliberate token.

Button reclassification per Hybrid rule
- btn-save (~13 instances across General/Account/Notifications/
  Integrations/Data/Briefing/Admin tabs): flat accent → Moss
  action-primary
- btn-primary (Groups tab "Save Retention", "+ New Group"): flat
  accent → Moss action-primary
- btn-secondary (Detect timezone, Test SMTP, Refresh, Export, Add
  slot, Preview voice, etc.): outline-on-bg-secondary +
  hover-to-accent → Bronze action-secondary, filled
- btn-danger filled (Reset VAPID, Revoke API key Yes): added the
  rule (was previously unstyled — relied on no-rule). Now Oxblood
  action-destructive.
- btn-danger-outline (Invalidate sessions, Clear observations,
  Delete personal data): generic --color-danger → Oxblood
  action-destructive
- btn-danger-sm hover (group + member delete): --color-danger →
  --color-action-destructive
- btn-delete row hover, btn-confirm-delete (user delete two-stage):
  --color-danger → Oxblood. Confirm filled, cancel ghost.
- btn-toggle-open ("Open registration"): flat accent → Moss
- model-delete-btn hover: --color-danger → Oxblood
- btn-warn hover: muted-border-warning → filled-warning

Two-weights-only
- Snapped every font-weight 600/700 to 500.

Out of scope for this PR
- Voice/tone copy audit across labels and help text — per spec,
  opportunistic only. Skipped.
- Settings row de-bordering — current section dividers are
  structural; left as-is.
- Validation-state colors (.input-error etc.) keep --color-danger
  (Error terracotta) per the doc — Error is for validation/error
  messages, Oxblood is for destructive actions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 23:13:38 -04:00
parent ff498ce1a4
commit 541e2ed713
+83 -55
View File
@@ -2771,7 +2771,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
.sidebar-group-label {
font-size: 0.65rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--color-text-muted);
@@ -2800,7 +2800,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
color: var(--color-primary);
background: rgba(91, 74, 138, 0.08);
border-left-color: var(--color-primary);
font-weight: 600;
font-weight: 500;
}
/* Content panel */
@@ -2828,7 +2828,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.settings-section h2 {
margin: 0 0 0.75rem;
font-size: 0.7rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--color-text-muted);
@@ -2857,7 +2857,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.model-row-info { display: flex; align-items: center; gap: 0.4rem; min-width: 0; flex: 1; }
.model-name { font-size: 0.88rem; font-weight: 500; font-family: monospace; }
.model-badge {
font-size: 0.68rem; padding: 0.1rem 0.4rem; border-radius: 3px; font-weight: 600; white-space: nowrap;
font-size: 0.68rem; padding: 0.1rem 0.4rem; border-radius: 3px; font-weight: 500; white-space: nowrap;
}
.model-badge--loaded { background: color-mix(in srgb, #22c55e 18%, transparent); color: #22c55e; }
.model-badge--default { background: color-mix(in srgb, var(--color-primary) 18%, transparent); color: var(--color-primary); }
@@ -2868,7 +2868,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
font-size: 0.8rem; padding: 0.2rem 0.35rem; border-radius: 3px; line-height: 1;
transition: color 0.15s, background 0.15s;
}
.model-delete-btn:hover:not(:disabled) { color: var(--color-danger, #ef4444); background: color-mix(in srgb, var(--color-danger, #ef4444) 10%, transparent); }
.model-delete-btn:hover:not(:disabled) { color: var(--color-action-destructive); background: color-mix(in srgb, var(--color-action-destructive) 10%, transparent); }
.model-delete-btn:disabled { opacity: 0.4; cursor: default; }
.model-pull-form { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
.model-pull-form .input { flex: 1; }
@@ -2922,7 +2922,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.field label {
display: block;
font-size: 0.875rem;
font-weight: 600;
font-weight: 500;
margin-bottom: 0.35rem;
color: var(--color-text);
}
@@ -2957,9 +2957,10 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
gap: 0.65rem;
flex-wrap: wrap;
}
/* Save: Moss action-primary per Hybrid */
.btn-save {
padding: 0.4rem 0.9rem;
background: var(--color-primary);
background: var(--color-action-primary);
color: #fff;
border: none;
border-radius: var(--radius-sm);
@@ -2967,52 +2968,72 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
font-size: 0.875rem;
font-family: inherit;
white-space: nowrap;
transition: background 0.15s;
}
.btn-save:disabled { opacity: 0.6; cursor: default; }
.btn-save:hover:not(:disabled) { opacity: 0.9; }
.btn-save:hover:not(:disabled) { background: var(--color-action-primary-hover); }
/* Danger outline (Invalidate sessions, Clear observations, etc.):
Oxblood action-destructive ghost — fills on hover */
.btn-danger-outline {
padding: 0.4rem 0.9rem;
background: none;
color: var(--color-danger, #e74c3c);
border: 1px solid var(--color-danger, #e74c3c);
color: var(--color-action-destructive);
border: 1px solid var(--color-action-destructive);
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.875rem;
font-family: inherit;
white-space: nowrap;
transition: background 0.15s, color 0.15s;
}
.btn-danger-outline:hover:not(:disabled) {
background: var(--color-danger, #e74c3c);
background: var(--color-action-destructive);
color: #fff;
}
.btn-danger-outline:disabled { opacity: 0.5; cursor: default; }
.btn-secondary {
/* Filled destructive: Oxblood action-destructive */
.btn-danger {
padding: 0.4rem 0.9rem;
background: var(--color-bg-secondary);
color: var(--color-text);
border: 1px solid var(--color-border);
background: var(--color-action-destructive);
color: #fff;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.875rem;
font-family: inherit;
white-space: nowrap;
transition: background 0.15s;
}
.btn-secondary:hover:not(:disabled) {
border-color: var(--color-primary);
color: var(--color-primary);
.btn-danger:hover:not(:disabled) { background: var(--color-action-destructive-hover); }
.btn-danger:disabled { opacity: 0.5; cursor: default; }
/* Secondary: Bronze action-secondary — alternate paths (Detect, Test,
Refresh, Add slot, etc.). Outline form for visual lightness. */
.btn-secondary {
padding: 0.4rem 0.9rem;
background: var(--color-action-secondary);
color: #fff;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.875rem;
font-family: inherit;
white-space: nowrap;
transition: background 0.15s;
}
.btn-secondary:hover:not(:disabled) { background: var(--color-action-secondary-hover); }
.btn-secondary:disabled { opacity: 0.6; cursor: default; }
.btn-warn:hover:not(:disabled) {
border-color: var(--color-warning);
color: var(--color-warning);
background: var(--color-warning);
color: #fff;
}
.saved-msg {
color: var(--color-success);
font-size: 0.875rem;
font-weight: 600;
font-weight: 500;
}
.input-error { border-color: var(--color-danger); }
.input-error:focus { border-color: var(--color-danger); }
@@ -3138,7 +3159,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
.result-title {
font-size: 0.9rem;
font-weight: 600;
font-weight: 500;
color: var(--color-primary);
text-decoration: none;
word-break: break-word;
@@ -3180,7 +3201,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.subsection-label {
margin: 0 0 0.5rem;
font-size: 0.875rem;
font-weight: 600;
font-weight: 500;
color: var(--color-text-secondary);
}
.test-email-section {
@@ -3214,7 +3235,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.push-permission-badge,
.push-sub-badge {
font-size: 0.72rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
padding: 0.1rem 0.4rem;
@@ -3303,14 +3324,14 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.subsection-label {
margin: 0 0 0.5rem;
font-size: 0.85rem;
font-weight: 600;
font-weight: 500;
color: var(--color-text-secondary);
}
.users-table { width: 100%; border-collapse: collapse; }
.users-table th {
text-align: left;
font-size: 0.8rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
@@ -3323,14 +3344,14 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
font-size: 0.9rem;
}
.users-table tbody tr:last-child td { border-bottom: none; }
.cell-username { font-weight: 600; }
.cell-username { font-weight: 500; }
.cell-email { color: var(--color-text-secondary); }
.cell-date { color: var(--color-text-muted); font-size: 0.85rem; }
.cell-actions { white-space: nowrap; }
.role-badge {
display: inline-block;
font-size: 0.7rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 0.15rem 0.4rem;
@@ -3345,6 +3366,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
background: var(--color-bg-secondary);
}
.you-label { font-size: 0.8rem; color: var(--color-text-muted); font-style: italic; }
/* Per-row delete (users / invitations / etc.): ghost → Oxblood on hover */
.btn-delete {
padding: 0.25rem 0.6rem;
background: transparent;
@@ -3354,20 +3376,22 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
cursor: pointer;
font-size: 0.8rem;
}
.btn-delete:hover:not(:disabled) { border-color: var(--color-danger); color: var(--color-danger); }
.btn-delete:hover:not(:disabled) { border-color: var(--color-action-destructive); color: var(--color-action-destructive); }
.btn-delete:disabled { opacity: 0.4; cursor: default; }
/* Two-stage destructive: Confirm = Oxblood filled, Cancel = Bronze ghost */
.btn-confirm-delete {
padding: 0.25rem 0.6rem;
background: var(--color-danger);
background: var(--color-action-destructive);
color: #fff;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
font-weight: 500;
margin-right: 0.25rem;
transition: background 0.15s;
}
.btn-confirm-delete:hover:not(:disabled) { filter: brightness(0.9); }
.btn-confirm-delete:hover:not(:disabled) { background: var(--color-action-destructive-hover); }
.btn-confirm-delete:disabled { opacity: 0.6; cursor: default; }
.btn-cancel-delete {
padding: 0.25rem 0.6rem;
@@ -3379,18 +3403,20 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
font-size: 0.8rem;
}
.btn-cancel-delete:hover { color: var(--color-text); border-color: var(--color-text-muted); }
/* Toggle (Open/Close registration, etc.): Open = Moss, Close = Pewter ghost */
.btn-toggle {
padding: 0.45rem 1rem;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.9rem;
font-weight: 600;
font-weight: 500;
white-space: nowrap;
transition: background 0.15s;
}
.btn-toggle:disabled { opacity: 0.6; cursor: default; }
.btn-toggle-open { background: var(--color-primary); color: #fff; }
.btn-toggle-open:hover:not(:disabled) { opacity: 0.9; }
.btn-toggle-open { background: var(--color-action-primary); color: #fff; }
.btn-toggle-open:hover:not(:disabled) { background: var(--color-action-primary-hover); }
.btn-toggle-close {
background: var(--color-bg-secondary);
color: var(--color-text);
@@ -3415,10 +3441,10 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
align-items: center;
gap: 0.15rem;
}
.stat-count { font-size: 1.5rem; font-weight: 700; color: var(--color-text); }
.stat-count { font-size: 1.5rem; font-weight: 500; color: var(--color-text); }
.stat-label {
font-size: 0.75rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
@@ -3436,7 +3462,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.logs-table th {
text-align: left;
font-size: 0.8rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
@@ -3468,7 +3494,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
.category-badge {
display: inline-block;
font-size: 0.65rem; font-weight: 700;
font-size: 0.65rem; font-weight: 500;
text-transform: uppercase; letter-spacing: 0.05em;
padding: 0.1rem 0.35rem; border-radius: var(--radius-sm);
}
@@ -3477,7 +3503,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.cat-error { color: var(--color-danger); background: color-mix(in srgb, var(--color-danger) 15%, transparent); }
.method-tag {
display: inline-block;
font-size: 0.65rem; font-weight: 700; font-family: monospace;
font-size: 0.65rem; font-weight: 500; font-family: monospace;
padding: 0.05rem 0.25rem; border-radius: 3px;
background: var(--color-bg-secondary); color: var(--color-text-muted);
margin-right: 0.25rem;
@@ -3503,9 +3529,10 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
/* ── Groups tab ──────────────────────────────────────────────── */
/* Moss action-primary per Hybrid */
.btn-primary {
padding: 0.4rem 0.9rem;
background: var(--color-primary);
background: var(--color-action-primary);
color: #fff;
border: none;
border-radius: var(--radius-sm);
@@ -3513,9 +3540,10 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
font-size: 0.875rem;
font-family: inherit;
white-space: nowrap;
transition: background 0.15s;
}
.btn-primary:disabled { opacity: 0.6; cursor: default; }
.btn-primary:hover:not(:disabled) { opacity: 0.9; }
.btn-primary:hover:not(:disabled) { background: var(--color-action-primary-hover); }
.input-field {
width: 100%;
@@ -3576,7 +3604,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
.group-name {
font-weight: 600;
font-weight: 500;
font-size: 0.9rem;
color: var(--color-text);
}
@@ -3611,7 +3639,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
transition: border-color 0.15s, color 0.15s;
}
.btn-sm:hover { border-color: var(--color-primary); color: var(--color-primary); }
.btn-danger-sm:hover { border-color: var(--color-danger, #e74c3c); color: var(--color-danger, #e74c3c); }
.btn-danger-sm:hover { border-color: var(--color-action-destructive); color: var(--color-action-destructive); }
.group-members-panel {
padding: 0.75rem 1rem 1rem;
@@ -3657,7 +3685,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
transition: background 0.1s;
}
.member-result-item:hover { background: var(--color-hover); }
.member-result-name { font-weight: 600; font-size: 0.85rem; }
.member-result-name { font-weight: 500; font-size: 0.85rem; }
.member-result-email { color: var(--color-text-muted); font-size: 0.78rem; }
.role-select {
@@ -3693,7 +3721,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.member-role-badge {
font-size: 0.7rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
padding: 0.15rem 0.4rem;
@@ -3948,13 +3976,13 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
border-bottom: 1px solid var(--color-border);
font-size: 0.9rem;
}
.api-keys-table th { font-weight: 600; opacity: 0.7; }
.api-keys-table th { font-weight: 500; opacity: 0.7; }
.scope-badge {
display: inline-block;
padding: 0.1rem 0.5rem;
border-radius: 9999px;
font-size: 0.78rem;
font-weight: 600;
font-weight: 500;
}
.scope-badge.read { background: color-mix(in srgb, #3b82f6 15%, transparent); color: #3b82f6; }
.scope-badge.write { background: color-mix(in srgb, #10b981 15%, transparent); color: #10b981; }
@@ -3975,7 +4003,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
border-radius: 8px;
}
.mcp-pkg-name { font-family: monospace; font-size: 0.9rem; flex: 1; }
.mcp-install-steps h3 { font-size: 0.95rem; font-weight: 600; margin-bottom: 0.75rem; }
.mcp-install-steps h3 { font-size: 0.95rem; font-weight: 500; margin-bottom: 0.75rem; }
.mcp-install-steps ol { padding-left: 1.25rem; display: flex; flex-direction: column; gap: 0.75rem; }
.mcp-install-steps li { line-height: 1.6; font-size: 0.9rem; }
.mcp-code {
@@ -4012,7 +4040,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.mcp-client-tab.active {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
font-weight: 600;
font-weight: 500;
}
.mcp-code-row {
display: flex;
@@ -4049,7 +4077,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
}
.status-badge {
font-size: 0.75rem;
font-weight: 600;
font-weight: 500;
padding: 0.15rem 0.55rem;
border-radius: 999px;
}
@@ -4196,7 +4224,7 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
background: color-mix(in srgb, var(--color-primary) 15%, transparent);
border-color: var(--color-primary);
color: var(--color-primary);
font-weight: 600;
font-weight: 500;
}
.learned-summary {
background: var(--color-bg-secondary);
@@ -4219,16 +4247,16 @@ FABLE_API_KEY={{ effectiveApiKey }}</pre>
.btn-danger-outline {
padding: 0.45rem 1rem;
background: none;
border: 1px solid var(--color-danger, #e74c3c);
border: 1px solid var(--color-action-destructive);
border-radius: var(--radius-sm);
color: var(--color-danger, #e74c3c);
color: var(--color-action-destructive);
font-size: 0.9rem;
cursor: pointer;
font-family: inherit;
transition: background 0.15s, color 0.15s;
}
.btn-danger-outline:hover:not(:disabled) {
background: var(--color-danger, #e74c3c);
background: var(--color-action-destructive);
color: #fff;
}
.btn-danger-outline:disabled { opacity: 0.5; cursor: default; }