feat(web/ui): refactor component inline styles into utility classes

Pass 2 of the web UI spacing/typography/visual-rhythm rework.

- Add layout, spacing, typography, visual, card, and component utilities
- Add form-section, form-row, form-help, text-error, alert-success
- Unify .form-group and .form-field; add .status-badge family
- Alias legacy button classes to .btn primitives
- Refactor ToolTypeListSidebar and ConfigProfileListSidebar to use .sidebar
  and var(--sidebar-width) instead of hardcoded 280px
- Refactor ToolTypeEditorPanel, ConfigProfileEditorPanel,
  git-mount-editor, and manifest-editor to use utility classes

Quality gates: npm run typecheck, npm run lint, npm run build pass.
Inline style blocks in target components reduced from 198 to 11.
This commit is contained in:
Developer
2026-06-16 14:47:37 +00:00
parent 38d116dcf7
commit 5d5b39bec1
8 changed files with 957 additions and 571 deletions
+181 -24
View File
@@ -428,21 +428,71 @@
margin: 0 0 1rem;
}
.form-field {
.form-field,
.form-group {
display: flex;
flex-direction: column;
gap: 0.35rem;
gap: var(--space-2);
margin-bottom: var(--space-4);
}
.form-field:last-child,
.form-group:last-child {
margin-bottom: 0;
}
.form-field input,
.form-field textarea,
.form-field select {
padding: 0.55rem 0.7rem;
.form-field select,
.form-group input,
.form-group textarea,
.form-group select {
padding: var(--space-2) var(--space-3);
border: 1px solid var(--border);
border-radius: 10px;
border-radius: var(--radius-md);
font: inherit;
background: var(--panel);
color: var(--ink);
width: 100%;
}
.form-row {
display: flex;
align-items: flex-start;
gap: var(--space-3);
}
.form-row .form-field,
.form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.form-section {
padding: var(--space-4);
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-md);
}
.form-section > h4:first-child,
.form-section > h3:first-child {
margin-top: 0;
margin-bottom: var(--space-3);
}
.form-help {
display: block;
font-size: var(--font-size-sm);
color: var(--muted);
margin-top: var(--space-1);
line-height: var(--line-height-normal);
}
.text-error {
color: var(--danger);
font-size: var(--font-size-sm);
line-height: var(--line-height-normal);
}
.dialog-actions {
@@ -494,19 +544,6 @@
flex: 1;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.form-group input {
padding: 0.55rem 0.7rem;
border: 1px solid var(--border);
border-radius: 10px;
font: inherit;
}
.error {
color: #b91c1c;
padding: 0.75rem;
@@ -1205,7 +1242,11 @@ a .icon:last-child {
}
}
/* ─── Primitive Buttons ─── */
.btn {
.btn,
.primary-button,
.secondary-button,
.ghost-button,
.button-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -1224,29 +1265,41 @@ a .icon:last-child {
transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.btn:focus-visible {
.btn:focus-visible,
.primary-button:focus-visible,
.secondary-button:focus-visible,
.ghost-button:focus-visible,
.button-secondary:focus-visible {
outline: 2px solid var(--brand);
outline-offset: 2px;
}
.btn-primary {
.btn-primary,
.primary-button {
background: var(--brand);
color: var(--primary-fg);
border-color: var(--brand);
}
.btn-primary:hover {
.btn-primary:hover,
.primary-button:hover {
background: var(--brand-strong);
border-color: var(--brand-strong);
}
.btn-secondary {
.btn-secondary,
.secondary-button,
.ghost-button,
.button-secondary {
background: var(--panel);
color: var(--ink);
border-color: var(--border);
}
.btn-secondary:hover {
.btn-secondary:hover,
.secondary-button:hover,
.ghost-button:hover,
.button-secondary:hover {
background: var(--bg);
}
@@ -1411,6 +1464,12 @@ a .icon:last-child {
color: var(--info);
}
.alert-success {
background: var(--success-light);
border-color: color-mix(in srgb, var(--success) 30%, transparent);
color: var(--success);
}
/* ─── Loading State ─── */
.loading-state {
display: flex;
@@ -1454,6 +1513,104 @@ a .icon:last-child {
color: var(--muted);
}
/* ─── Status Badges ─── */
.status-badge {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: 600;
text-transform: capitalize;
line-height: var(--line-height-tight);
background: var(--bg);
color: var(--muted);
border: 1px solid var(--border);
}
.status-badge::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.status-badge--running,
.status-badge--success {
background: var(--success-light);
color: var(--success);
border-color: color-mix(in srgb, var(--success) 30%, transparent);
}
.status-badge--error,
.status-badge--failed {
background: var(--danger-light);
color: var(--danger);
border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}
.status-badge--warning {
background: var(--warning-light);
color: var(--warning);
border-color: color-mix(in srgb, var(--warning) 30%, transparent);
}
.status-badge--info,
.status-badge--pending,
.status-badge--idle {
background: var(--info-light);
color: var(--info);
border-color: color-mix(in srgb, var(--info) 30%, transparent);
}
/* Legacy instance-chip alias */
.instance-chip {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-full);
font-size: var(--font-size-xs);
font-weight: 600;
text-transform: capitalize;
line-height: var(--line-height-tight);
background: var(--bg);
color: var(--muted);
border: 1px solid var(--border);
}
.instance-chip::before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.instance-chip.running {
background: var(--success-light);
color: var(--success);
border-color: color-mix(in srgb, var(--success) 30%, transparent);
}
.instance-chip.starting,
.instance-chip.building,
.instance-chip.probing {
background: var(--warning-light);
color: var(--warning);
border-color: color-mix(in srgb, var(--warning) 30%, transparent);
}
.instance-chip.error,
.instance-chip.unhealthy,
.instance-chip.stopped {
background: var(--danger-light);
color: var(--danger);
border-color: color-mix(in srgb, var(--danger) 30%, transparent);
}
/* ─── Focus States ─── */
.nav-item:focus-visible,
.mobile-nav-item:focus-visible,
+570 -4
View File
@@ -13,7 +13,7 @@
.stack {
display: flex;
flex-direction: column;
gap: var(--space-4);
gap: var(--space-3);
}
.stack-sm {
@@ -21,20 +21,103 @@
}
.stack-md {
gap: var(--space-4);
gap: var(--space-3);
}
.stack-lg {
gap: var(--space-6);
gap: var(--space-4);
}
.stack-xl {
gap: var(--space-5);
}
.row {
display: flex;
flex-wrap: wrap;
gap: var(--space-4);
gap: var(--space-3);
align-items: center;
}
.row-sm {
gap: var(--space-2);
}
.row-md {
gap: var(--space-3);
}
.row-lg {
gap: var(--space-4);
}
.row.items-start,
.items-start {
align-items: flex-start;
}
.row.items-center,
.items-center {
align-items: center;
}
.row.items-end,
.items-end {
align-items: flex-end;
}
.justify-between {
justify-content: space-between;
}
.justify-center {
justify-content: center;
}
.justify-end {
justify-content: flex-end;
}
.flex-1 {
flex: 1;
}
.flex-2 {
flex: 2;
}
.flex-none {
flex: none;
}
.flex-wrap {
flex-wrap: wrap;
}
.min-h-0 {
min-height: 0;
}
.w-full {
width: 100%;
}
.w-fit {
width: fit-content;
}
.min-w-0 {
min-width: 0;
}
.max-w-prose {
max-width: 65ch;
}
.max-w-form {
max-width: 800px;
}
.grid {
display: grid;
gap: var(--space-4);
@@ -48,6 +131,286 @@
white-space: nowrap;
}
.text-xs {
font-size: var(--font-size-xs);
}
.text-sm {
font-size: var(--font-size-sm);
}
.text-base {
font-size: var(--font-size-base);
}
.text-lg {
font-size: var(--font-size-lg);
}
.text-xl {
font-size: var(--font-size-xl);
}
.font-mono {
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
.font-medium {
font-weight: 500;
}
.font-semibold {
font-weight: 600;
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
/* Visual Utilities */
.cursor-pointer {
cursor: pointer;
}
.overflow-auto {
overflow: auto;
}
.overflow-hidden {
overflow: hidden;
}
.rounded-sm {
border-radius: var(--radius-sm);
}
.rounded-md {
border-radius: var(--radius-md);
}
.rounded-lg {
border-radius: var(--radius-lg);
}
.rounded-xl {
border-radius: var(--radius-xl);
}
.rounded-full {
border-radius: var(--radius-full);
}
/* Spacing Utilities */
.m-0 {
margin: 0;
}
.mt-1 {
margin-top: var(--space-1);
}
.mt-2 {
margin-top: var(--space-2);
}
.mt-3 {
margin-top: var(--space-3);
}
.mt-4 {
margin-top: var(--space-4);
}
.mt-5 {
margin-top: var(--space-5);
}
.mt-6 {
margin-top: var(--space-6);
}
.mb-0 {
margin-bottom: 0;
}
.mb-1 {
margin-bottom: var(--space-1);
}
.mb-2 {
margin-bottom: var(--space-2);
}
.mb-3 {
margin-bottom: var(--space-3);
}
.mb-4 {
margin-bottom: var(--space-4);
}
.mb-5 {
margin-bottom: var(--space-5);
}
.mb-6 {
margin-bottom: var(--space-6);
}
.ml-auto {
margin-left: auto;
}
.mr-auto {
margin-right: auto;
}
.ml-1 {
margin-left: var(--space-1);
}
.ml-2 {
margin-left: var(--space-2);
}
.ml-3 {
margin-left: var(--space-3);
}
.mr-1 {
margin-right: var(--space-1);
}
.mr-2 {
margin-right: var(--space-2);
}
.mr-3 {
margin-right: var(--space-3);
}
.p-1 {
padding: var(--space-1);
}
.p-2 {
padding: var(--space-2);
}
.p-3 {
padding: var(--space-3);
}
.p-4 {
padding: var(--space-4);
}
.p-5 {
padding: var(--space-5);
}
.px-2 {
padding-left: var(--space-2);
padding-right: var(--space-2);
}
.px-3 {
padding-left: var(--space-3);
padding-right: var(--space-3);
}
.px-4 {
padding-left: var(--space-4);
padding-right: var(--space-4);
}
.py-1 {
padding-top: var(--space-1);
padding-bottom: var(--space-1);
}
.py-2 {
padding-top: var(--space-2);
padding-bottom: var(--space-2);
}
.py-3 {
padding-top: var(--space-3);
padding-bottom: var(--space-3);
}
.py-4 {
padding-top: var(--space-4);
padding-bottom: var(--space-4);
}
.pl-2 {
padding-left: var(--space-2);
}
.pl-3 {
padding-left: var(--space-3);
}
.pl-4 {
padding-left: var(--space-4);
}
.pr-2 {
padding-right: var(--space-2);
}
.pr-3 {
padding-right: var(--space-3);
}
.pr-4 {
padding-right: var(--space-4);
}
/* Border Utilities */
.border {
border: 1px solid var(--border);
}
.border-l {
border-left: 1px solid var(--border);
}
.border-l-2 {
border-left: 2px solid var(--border);
}
/* Card Utilities */
.card-sm {
padding: var(--space-3);
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-md);
}
.card-md {
padding: var(--space-4);
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-md);
}
.card-lg {
padding: var(--space-5);
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
}
.truncate-multiline {
display: -webkit-box;
-webkit-line-clamp: 3;
@@ -3777,6 +4140,209 @@ a:active,
gap: var(--space-3);
}
/* ─── Sticky Footer ─── */
.sticky-footer {
position: sticky;
bottom: var(--space-4);
z-index: 10;
background: var(--panel);
padding: var(--space-4);
border: 1px solid var(--border);
border-radius: var(--radius-md);
box-shadow: var(--shadow-md);
}
/* ─── Drag Items ─── */
.drag-item {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-md);
margin-bottom: var(--space-1);
cursor: grab;
transition: background 0.15s;
}
.drag-item:hover {
background: var(--bg);
}
.drag-item-active {
background: var(--brand-light, color-mix(in srgb, var(--brand) 10%, transparent));
border-color: var(--brand);
}
.drag-handle {
cursor: grab;
color: var(--muted);
}
/* ─── Code Block ─── */
.code-block {
background: var(--code-bg, #1e1e1e);
color: var(--code-fg, #d4d4d4);
padding: var(--space-4);
border-radius: var(--radius-md);
overflow: auto;
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: var(--font-size-sm);
line-height: var(--line-height-normal);
}
/* ─── Empty State ─── */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: var(--space-10);
color: var(--muted);
text-align: center;
}
.empty-state-icon {
opacity: 0.3;
margin-bottom: var(--space-4);
}
.empty-state h3 {
margin: 0 0 var(--space-2);
font-weight: 500;
}
.empty-state p {
margin: 0;
}
/* ─── Sidebar ─── */
.sidebar {
width: var(--sidebar-width);
min-width: var(--sidebar-width);
display: flex;
flex-direction: column;
border-right: 1px solid var(--border);
background: var(--panel);
}
.sidebar-header {
padding: var(--space-4);
border-bottom: 1px solid var(--border);
}
.sidebar-header h2 {
margin: 0;
font-size: var(--font-size-lg);
}
.sidebar-header p {
margin: var(--space-1) 0 0;
font-size: var(--font-size-sm);
}
.sidebar-list {
flex: 1;
overflow-y: auto;
padding: var(--space-2);
}
.sidebar-footer {
padding: var(--space-4);
border-top: 1px solid var(--border);
}
.sidebar-item {
width: 100%;
text-align: left;
padding: var(--space-3) var(--space-4);
margin-bottom: var(--space-1);
border-radius: var(--radius-md);
border: none;
background: transparent;
color: var(--ink);
cursor: pointer;
display: flex;
align-items: center;
gap: var(--space-3);
transition: background 0.15s;
}
.sidebar-item:hover {
background: var(--bg);
}
.sidebar-item-active,
.sidebar-item-active:hover {
background: var(--brand);
color: white;
}
.sidebar-item-active .delete-btn {
color: rgba(255, 255, 255, 0.8);
}
.sidebar-item-active .delete-btn:hover {
background: rgba(255, 255, 255, 0.15);
color: white;
}
.sidebar-item-active .badge {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3);
color: white;
}
.sidebar-item-content {
flex: 1;
min-width: 0;
}
.sidebar-item-title {
font-weight: 600;
font-size: var(--font-size-base);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar-item-subtitle {
font-size: var(--font-size-sm);
opacity: 0.8;
margin-top: var(--space-1);
}
.sidebar-item .delete-btn {
opacity: 0;
}
.sidebar-item:hover .delete-btn,
.sidebar-item:focus-within .delete-btn {
opacity: 1;
}
.sidebar-create-button {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: var(--space-3);
border-radius: var(--radius-md);
border: 2px dashed var(--border);
background: transparent;
color: var(--muted);
cursor: pointer;
font-weight: 600;
transition: border-color 0.15s, color 0.15s;
}
.sidebar-create-button:hover {
border-color: var(--brand);
color: var(--brand);
}
/* ─── Mobile FAB ─── */
.mobile-fab {
position: fixed;