Frontend improvements: CSS foundations, accessibility, UX enhancements

CSS: Add design token variables (spacing, timing, weights, z-index layers),
migrate all hardcoded z-index to named vars, fix light theme contrast for
WCAG AA, add skeleton loading cards, mask-composite fallback, card padding.

Accessibility: aria-live on toast, aria-label on health dots, sr-only class,
graph container keyboard focusable, MQTT password wrapped in form element.

UX: Modal auto-focus on open, inline field validation with blur, undo toast
with countdown, bulk action progress indicator, API error toast on failure.

i18n: Add common.undo, validation.required, bulk.processing, api.error.*
keys in EN/RU/ZH.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 01:51:22 +03:00
parent 43fbc1eff5
commit 47c696bae3
21 changed files with 397 additions and 38 deletions

View File

@@ -193,6 +193,21 @@ select:focus {
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.15);
}
/* Inline validation states */
input.field-invalid,
select.field-invalid {
border-color: var(--danger-color);
box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger-color) 15%, transparent);
}
.field-error-msg {
display: block;
color: var(--danger-color);
font-size: 0.78rem;
margin-top: 4px;
line-height: 1.3;
}
/* Remove browser autofill styling */
input:-webkit-autofill,
input:-webkit-autofill:hover,
@@ -260,7 +275,7 @@ input:-webkit-autofill:focus {
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 9999;
z-index: var(--z-overlay-spinner);
backdrop-filter: blur(4px);
}
@@ -353,7 +368,7 @@ input:-webkit-autofill:focus {
font-size: 15px;
opacity: 0;
transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1), transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
z-index: 3000;
z-index: var(--z-toast);
box-shadow: 0 4px 20px var(--shadow-color);
min-width: 300px;
text-align: center;
@@ -384,6 +399,52 @@ input:-webkit-autofill:focus {
background: var(--info-color);
}
/* Toast with undo action */
.toast-with-action {
display: flex;
align-items: center;
gap: 12px;
}
.toast-message {
flex: 1;
}
.toast-undo-btn {
background: rgba(255, 255, 255, 0.25);
border: 1px solid rgba(255, 255, 255, 0.4);
color: white;
padding: 4px 12px;
border-radius: var(--radius-sm);
font-weight: var(--weight-semibold, 600);
font-size: 0.85rem;
cursor: pointer;
transition: background var(--duration-fast, 0.15s);
white-space: nowrap;
flex-shrink: 0;
}
.toast-undo-btn:hover {
background: rgba(255, 255, 255, 0.4);
}
.toast-timer {
width: 100%;
height: 3px;
position: absolute;
bottom: 0;
left: 0;
border-radius: 0 0 var(--radius-md) var(--radius-md);
background: rgba(255, 255, 255, 0.3);
transform-origin: left;
animation: toastTimer var(--toast-duration, 5s) linear forwards;
}
@keyframes toastTimer {
from { transform: scaleX(1); }
to { transform: scaleX(0); }
}
/* ── Card Tags ──────────────────────────────────────────── */
.card-tags {
@@ -604,7 +665,7 @@ textarea:focus-visible {
.icon-select-popup {
position: fixed;
z-index: 10000;
z-index: var(--z-lightbox);
overflow: hidden;
opacity: 0;
transition: opacity 0.15s ease;
@@ -683,7 +744,7 @@ textarea:focus-visible {
.type-picker-overlay {
position: fixed;
inset: 0;
z-index: 3000;
z-index: var(--z-command-palette);
display: flex;
justify-content: center;
padding-top: 15vh;
@@ -758,7 +819,7 @@ textarea:focus-visible {
display: none;
position: fixed;
inset: 0;
z-index: 10000;
z-index: var(--z-lightbox);
background: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: flex-start;