Files
2meet-data-optimizer/admin/assets/wpdo-admin.css
T
wpdev 2203bc471c feat(admin): Settings 分區即時儲存(backport A v3.0.2)
7 個設定分區各自加「儲存此區塊」按鈕,經 wp_ajax_wpdo_save_settings_section
以 AJAX 存檔,不再需要整頁 reload。

- 新增 ajax_save_settings_section() + 7 個 private save_section_*()
- 新增 admin/assets/wpdo-settings.js(106 行)與 4 條 CSS 規則
- render_settings() 的 7 個 h3 各包上 .wpdo-settings-section[data-section]
- 全頁送出按鈕改標「儲存全部設定」,原本的 POST handler 保持不變(漸進增強)

hp-transient / wc-term-count 兩個分區的 toggle 仍由核心 admin 呈現(實作在
AddOn),只寫 wp_options,故不需 class_exists 守衛。
2026-07-31 10:01:20 +08:00

544 lines
15 KiB
CSS

/**
* WP Data Optimizer — Admin Styles (Morandi Design System)
*
* Morandi tokens are provided via the 'morandi-design-system' CSS dependency
* registered in WPDO_Admin::enqueue_assets(). No @import needed.
*/
/* ── KPI Hero Strip (v2.16.0) ────────────────────────────────────────── */
.wpdo-kpi-hero {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: var(--space-4);
margin: var(--space-5) 0;
}
.wpdo-kpi-card {
background: var(--color-bg-secondary, #FAF9F6);
border: 1px solid var(--color-bg-divider, #E8E3D9);
border-radius: var(--radius-lg);
padding: var(--space-4) var(--space-5);
box-shadow: var(--shadow-sm, 0 1px 3px rgba(58,53,48,0.08));
position: relative;
overflow: hidden;
transition: transform var(--duration-normal, 200ms) var(--ease-default, ease-out),
box-shadow var(--duration-normal, 200ms) var(--ease-default, ease-out);
}
.wpdo-kpi-card::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg,
var(--color-accent-warm, #c4a986),
var(--color-accent-warm, #c4a986));
opacity: 0.6;
}
.wpdo-kpi-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md, 0 4px 12px rgba(58,53,48,0.10));
}
.wpdo-kpi-label {
font-family: var(--font-family-body);
font-size: var(--text-sm, 13px);
color: var(--color-text-secondary, #6b6256);
font-weight: 500;
letter-spacing: 0.02em;
text-transform: uppercase;
margin-bottom: var(--space-2);
}
.wpdo-kpi-value {
font-family: var(--font-family-display);
font-size: 2.25rem;
line-height: 1.1;
font-weight: 700;
color: var(--color-text-primary, #3a3530);
margin-bottom: var(--space-2);
letter-spacing: -0.02em;
}
.wpdo-kpi-unit {
font-size: 1.1rem;
color: var(--color-text-secondary, #6b6256);
font-weight: 500;
margin-left: 2px;
}
.wpdo-kpi-empty {
color: var(--color-text-tertiary, #9a8e7e);
font-weight: 400;
}
.wpdo-kpi-sub {
font-family: var(--font-family-body);
font-size: var(--text-xs, 12px);
color: var(--color-text-secondary, #6b6256);
line-height: 1.4;
}
/* Per-KPI accent stripe color */
.wpdo-kpi--ratio::before { background: linear-gradient(90deg, #c4a986, #d4b89a); }
.wpdo-kpi--speedup::before { background: linear-gradient(90deg, #5a9a85, #7eb39e); }
.wpdo-kpi--coverage::before { background: linear-gradient(90deg, #8b7eb3, #a397c9); }
.wpdo-kpi--health-excellent::before { background: linear-gradient(90deg, #5a9a85, #7eb39e); }
.wpdo-kpi--health-good::before { background: linear-gradient(90deg, #c4a986, #d4b89a); }
.wpdo-kpi--health-warn::before { background: linear-gradient(90deg, #d4a574, #e0b888); }
.wpdo-kpi--health-crit::before { background: linear-gradient(90deg, #c47a7a, #d49090); }
.wpdo-kpi--health-excellent .wpdo-kpi-value { color: #4a8270; }
.wpdo-kpi--health-good .wpdo-kpi-value { color: #a8906f; }
.wpdo-kpi--health-warn .wpdo-kpi-value { color: #b88955; }
.wpdo-kpi--health-crit .wpdo-kpi-value { color: #a86060; }
@media (max-width: 600px) {
.wpdo-kpi-hero {
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.wpdo-kpi-value {
font-size: 1.75rem;
}
}
/* ── Grouped Tab Navigation (v2.16.0) ────────────────────────────────── */
.wpdo-tab-nav {
display: flex;
flex-wrap: wrap;
gap: var(--space-3) var(--space-4);
align-items: flex-end;
padding-bottom: 0;
border-bottom: 1px solid var(--color-bg-divider, #E8E3D9);
margin-bottom: var(--space-4);
}
.wpdo-tab-group {
display: inline-flex;
flex-direction: column;
gap: 4px;
margin-right: var(--space-2);
padding-right: var(--space-3);
position: relative;
}
.wpdo-tab-group:not(:last-child)::after {
content: "";
position: absolute;
right: 0;
top: 24px;
bottom: 4px;
width: 1px;
background: var(--color-bg-divider, #E8E3D9);
}
.wpdo-tab-group-label {
font-family: var(--font-family-body);
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-text-tertiary, #9a8e7e);
font-weight: 500;
padding-left: var(--space-2);
user-select: none;
pointer-events: none;
}
.wpdo-tab-group .nav-tab {
margin: 0 2px -1px 0;
transition: background-color var(--duration-fast, 120ms) ease,
color var(--duration-fast, 120ms) ease;
}
.wpdo-tab-group .nav-tab:hover {
background-color: var(--color-bg-tertiary, #F2EEE8);
color: var(--color-text-primary, #3a3530);
}
@media (max-width: 768px) {
.wpdo-tab-nav {
gap: var(--space-2);
}
.wpdo-tab-group {
margin-right: 0;
padding-right: var(--space-2);
}
.wpdo-tab-group-label {
font-size: 10px;
}
}
/* ── Layout ──────────────────────────────────────────────────────────── */
.wpdo-wrap .wpdo-tab-content {
margin-top: var(--space-5);
}
.wpdo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: var(--space-5);
margin-bottom: var(--space-5);
}
.wpdo-card {
background: var(--color-bg-secondary, #FAF9F6);
border: 1px solid var(--color-bg-divider, #E8E3D9);
border-radius: var(--radius-lg);
padding: var(--space-4) var(--space-5);
box-shadow: var(--shadow-sm, 0 1px 3px rgba(58,53,48,0.08));
overflow-x: auto; /* WCAG 1.4.10 Reflow: tables scroll within card, not page */
}
.wpdo-card h2 {
margin-top: 0;
padding-top: 0;
border-bottom: 1px solid var(--color-bg-divider, #E8E3D9);
padding-bottom: var(--space-3);
font-family: var(--font-family-display);
}
.wpdo-card h3,
.wpdo-card h4 {
margin-top: var(--space-3);
font-family: var(--font-family-display);
}
.wpdo-card-wide {
grid-column: 1 / -1;
}
/* Utility spacers replacing inline style="margin-top:N" */
.wpdo-mt-1 { margin-top: var(--space-2); }
.wpdo-mt-2 { margin-top: var(--space-3); }
.wpdo-mt-3 { margin-top: var(--space-5); }
.wpdo-mb-2 { margin-bottom: var(--space-3); }
.wpdo-table--narrow { max-width: 480px; }
.wpdo-table--medium { max-width: 700px; }
.wpdo-block--wide { max-width: 900px; }
.wpdo-col-param { width: 180px; }
/* Inline form row */
.wpdo-form-row {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
margin: var(--space-3) 0;
}
/* ── Tables ──────────────────────────────────────────────────────────── */
.wpdo-table {
font-size: var(--text-sm);
font-family: var(--font-family-body);
}
.wpdo-table code,
.wpdo-wrap code {
font-size: var(--text-xs);
background: var(--color-bg-tertiary, #F2EEE8);
padding: 2px var(--space-1);
border-radius: var(--radius-sm);
font-family: var(--font-family-mono);
}
.wpdo-log-msg {
max-width: 400px;
word-break: break-word;
}
/* ── Touch targets (CLAUDE.md: 44px minimum) ─────────────────────────── */
.wpdo-wrap .button,
.wpdo-wrap .button-secondary,
.wpdo-wrap .button-primary,
.wpdo-wrap input[type="number"],
.wpdo-wrap input[type="text"],
.wpdo-wrap input[type="search"],
.wpdo-wrap select {
min-height: 44px;
line-height: 1.4;
}
.wpdo-wrap .button-small {
min-height: 40px; /* WCAG 2.2 AA: 24px min; 40px exceeds 2.5.8 while remaining compact */
padding: 0 var(--space-2);
}
.wpdo-wrap input[type="number"] {
padding: 6px var(--space-2);
}
.wpdo-wrap input[type="number"].wpdo-input--xs {
width: 72px; /* was inline 60px — bumped to fit 44px height without clipping digits */
}
.wpdo-wrap .button:focus-visible,
.wpdo-wrap input:focus-visible,
.wpdo-wrap select:focus-visible {
outline: 2px solid var(--color-terra-primary, #C4897A);
outline-offset: 2px;
}
/* ── Method pill for REST API tab (was inline #0073aa) ───────────────── */
.wpdo-method-pill {
background: var(--color-blue-primary, #9EB3C2);
color: var(--color-text-primary, #3A3530);
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-weight-semibold);
font-family: var(--font-family-mono);
}
/* Code block for curl examples (was inline #EFEFEF) */
.wpdo-wrap .wpdo-code-block {
background: var(--color-bg-tertiary, #F2EEE8);
color: var(--color-text-primary, #3A3530);
padding: var(--space-3);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-family: var(--font-family-mono);
overflow: auto;
white-space: pre;
border: 1px solid var(--color-bg-divider, #E8E3D9);
}
/* ── Zone badges (icon + text, not colour alone — WCAG 1.4.1) ────────── */
.wpdo-zone {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary, #3A3530);
line-height: 1.4;
}
.wpdo-zone::before {
font-family: var(--font-family-mono);
font-weight: var(--font-weight-bold);
}
.wpdo-zone-hot {
background: var(--color-terra-primary, #C4897A);
}
.wpdo-zone-hot::before { content: "🔥"; }
.wpdo-zone-warm {
background: var(--color-apricot-medium, #C8B090);
}
.wpdo-zone-warm::before { content: "◐"; }
.wpdo-zone-cold {
background: var(--color-blue-medium, #8AA0B2);
}
.wpdo-zone-cold::before { content: "❄"; }
.wpdo-zone-archive {
background: var(--color-gray-medium, #AFA8A0);
}
.wpdo-zone-archive::before { content: "▦"; }
/* ── State badges (shape-prefixed for colour-blind legibility) ───────── */
.wpdo-state {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: var(--font-weight-semibold);
line-height: 1.4;
}
.wpdo-state::before {
font-family: var(--font-family-mono);
}
.wpdo-state-idle {
background: var(--color-bg-tertiary, #F2EEE8);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-idle::before { content: "○"; }
.wpdo-state-dual_write {
background: var(--color-apricot-lightest, #EDD9C0);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-dual_write::before { content: "⇄"; }
.wpdo-state-backfill {
background: var(--color-blue-lightest, #C8D6DE);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-backfill::before { content: "⟲"; }
.wpdo-state-verify {
background: var(--color-apricot-lightest, #EDD9C0);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-verify::before { content: "⚠"; }
.wpdo-state-cutover {
background: var(--color-green-lightest, #C8D5C4);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-cutover::before { content: "✓"; }
.wpdo-state-cleanup {
background: var(--color-blue-lightest, #C8D6DE);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-cleanup::before { content: "✂"; }
.wpdo-state-complete {
background: var(--color-green-primary, #A8B9A4);
color: var(--color-text-primary, #3A3530);
}
.wpdo-state-complete::before { content: "✔"; }
/* ── Generic badges ──────────────────────────────────────────────────── */
.wpdo-badge {
display: inline-block;
padding: 2px var(--space-2);
border-radius: var(--radius-sm);
font-size: var(--text-xs);
background: var(--color-bg-tertiary, #F2EEE8);
color: var(--color-text-primary, #3A3530);
}
.wpdo-badge-ok {
background: var(--color-green-lightest, #C8D5C4);
color: var(--color-text-primary, #3A3530);
}
.wpdo-badge-warn {
background: var(--color-apricot-lightest, #EDD9C0);
color: var(--color-text-primary, #3A3530);
}
/* ── Progress bars (transform-based — no layout-thrashing) ───────────── */
.wpdo-progress {
display: inline-block;
width: 120px;
height: 14px;
background: var(--color-bg-tertiary, #F2EEE8);
border-radius: var(--radius-full);
overflow: hidden;
vertical-align: middle;
margin-right: var(--space-2);
}
.wpdo-progress-bar {
height: 100%;
width: 100%;
background: var(--color-blue-primary, #9EB3C2);
border-radius: var(--radius-full);
transform-origin: left center;
transform: scaleX(var(--wpdo-progress, 0));
transition: transform var(--duration-slow, 0.4s) var(--ease-default, ease);
}
.wpdo-progress-text {
font-size: var(--text-xs);
color: var(--color-text-primary, #3A3530);
}
/* ── Confidence bar ──────────────────────────────────────────────────── */
.wpdo-confidence {
display: inline-block;
width: 60px;
height: 10px;
background: var(--color-bg-tertiary, #F2EEE8);
border-radius: var(--radius-full);
overflow: hidden;
vertical-align: middle;
margin-right: var(--space-1);
}
.wpdo-confidence-bar {
height: 100%;
width: 100%;
background: var(--color-green-primary, #A8B9A4);
border-radius: var(--radius-full);
transform-origin: left center;
transform: scaleX(var(--wpdo-confidence, 0));
}
/* ── Screen reader only ──────────────────────────────────────────────── */
.wpdo-sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
border: 0;
}
/* ── Tab red-dot badge (v2.8.1) ──────────────────────────────────────── */
.nav-tab .wpdo-tab-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: #dc3232;
margin-left: 6px;
vertical-align: middle;
box-shadow: 0 0 6px rgba(220, 50, 50, 0.6);
animation: wpdo-tab-dot-pulse 2s ease infinite;
}
@keyframes wpdo-tab-dot-pulse {
0% { box-shadow: 0 0 0 0 rgba(220, 50, 50, 0.7); }
70% { box-shadow: 0 0 0 6px rgba(220, 50, 50, 0); }
100% { box-shadow: 0 0 0 0 rgba(220, 50, 50, 0); }
}
/* ── Reduced motion ──────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
.wpdo-progress-bar {
transition-duration: 0.01ms;
}
.nav-tab .wpdo-tab-dot {
animation: none;
}
}
/* ── Settings per-section instant save (v3.0.2 backport) ────────────── */
.wpdo-settings-section {
background: var(--color-surface-card, #fbf7f0);
border: 1px solid var(--color-border-soft, #e5dccd);
border-radius: var(--radius-md, 12px);
padding: var(--space-4, 16px) var(--space-6, 24px);
margin-bottom: var(--space-5, 20px);
}
.wpdo-section-save-row {
display: flex;
align-items: center;
gap: var(--space-3, 12px);
padding-top: var(--space-3, 12px);
}
.wpdo-section-save-status {
font-size: 0.9rem;
color: var(--color-success, #5a8c5a);
opacity: 0;
transition: opacity var(--duration-normal, 200ms) var(--ease-default, ease);
}
.wpdo-section-save-status.visible {
opacity: 1;
}