diff --git a/website/css/style.css b/website/css/style.css index c81c97d..7e10fbf 100644 --- a/website/css/style.css +++ b/website/css/style.css @@ -1,26 +1,89 @@ :root { - --bg: #f4f6f9; - --surface: #ffffff; - --primary: #2563eb; - --primary-hover: #1d4ed8; - --danger: #dc2626; - --danger-hover: #b91c1c; - --success: #16a34a; - --warning: #d97706; - --text: #1e293b; - --text-secondary: #64748b; - --border: #e2e8f0; + color-scheme: dark; + --bg: #0f1218; + --surface: #1a1f2a; + --surface-raised: #222836; + --surface-hover: #2a3140; + --surface-muted: #161b24; + --primary: #3b82f6; + --primary-hover: #60a5fa; + --primary-subtle: rgba(59, 130, 246, 0.14); + --primary-border: rgba(59, 130, 246, 0.35); + --primary-text: #93c5fd; + --danger: #f87171; + --danger-hover: #fca5a5; + --danger-subtle: rgba(248, 113, 113, 0.12); + --danger-subtle-hover: rgba(248, 113, 113, 0.22); + --danger-border: rgba(248, 113, 113, 0.45); + --danger-text: #fca5a5; + --danger-focus-ring: rgba(248, 113, 113, 0.35); + --success: #4ade80; + --warning: #fbbf24; + --text: #e8edf4; + --text-secondary: #94a3b8; + --text-on-primary: #fff; + --border: #2e3648; --radius: 8px; - --shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06); - --shadow-lg: 0 4px 12px rgba(0,0,0,.1); + --shadow: 0 1px 3px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.25); + --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.45); + --focus-ring: rgba(59, 130, 246, 0.28); --sidebar-width: 240px; --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; - /* Incomplete translation highlight (neutral, not yellow) */ - --trans-missing-bg: #f8fafc; - --trans-missing-bg-hover: #f1f5f9; - --trans-missing-border: #cbd5e1; + --table-header-bg: #1e2430; + --row-hover-bg: #252c3a; + --test-row-bg: #2a2618; + --test-row-hover-bg: #3a321f; + --sticky-shadow: rgba(0, 0, 0, 0.35); + /* Incomplete translation highlight */ + --trans-missing-bg: #1c222d; + --trans-missing-bg-hover: #242b38; + --trans-missing-border: #3d4a5c; --trans-missing-accent: #64748b; - --trans-missing-placeholder: #94a3b8; + --trans-missing-placeholder: #64748b; + /* Status badges */ + --badge-draft-bg: #2a3140; + --badge-draft-fg: #94a3b8; + --badge-active-bg: rgba(74, 222, 128, 0.15); + --badge-active-fg: #4ade80; + --badge-archived-bg: rgba(251, 191, 36, 0.15); + --badge-archived-fg: #fbbf24; + --badge-in-progress-bg: var(--primary-subtle); + --badge-in-progress-fg: var(--primary-text); + --badge-pending-bg: var(--badge-draft-bg); + --badge-pending-fg: var(--badge-draft-fg); + --badge-admin-bg: rgba(167, 139, 250, 0.18); + --badge-admin-fg: #c4b5fd; + --badge-supervisor-bg: rgba(244, 114, 182, 0.15); + --badge-supervisor-fg: #f9a8d4; + --badge-coach-bg: rgba(74, 222, 128, 0.12); + --badge-coach-fg: #86efac; + --badge-you-bg: var(--primary-subtle); + --badge-you-fg: var(--primary-text); + --badge-warn-bg: rgba(251, 191, 36, 0.15); + --badge-warn-fg: #fcd34d; + --badge-q-bg: var(--primary-subtle); + --badge-q-fg: var(--primary-text); + --badge-opt-bg: #2a3140; + --badge-opt-fg: #cbd5e1; + --badge-app-bg: rgba(74, 222, 128, 0.12); + --badge-app-fg: #86efac; + --badge-str-bg: rgba(167, 139, 250, 0.15); + --badge-str-fg: #c4b5fd; + --badge-branch-bg: rgba(167, 139, 250, 0.18); + --badge-branch-fg: #c4b5fd; + --lang-chip-fixed-bg: var(--primary-subtle); + --lang-chip-fixed-border: var(--primary-border); + --toast-success-bg: rgba(74, 222, 128, 0.12); + --toast-success-fg: #86efac; + --toast-success-border: rgba(74, 222, 128, 0.3); + --toast-error-bg: rgba(248, 113, 113, 0.12); + --toast-error-fg: #fca5a5; + --toast-error-border: rgba(248, 113, 113, 0.3); + --toast-info-bg: var(--primary-subtle); + --toast-info-fg: var(--primary-text); + --toast-info-border: var(--primary-border); + --save-flash-bg: var(--primary-subtle); + --progress-track: #2e3648; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } @@ -33,6 +96,29 @@ body { min-height: 100vh; } +hr { + border: none; + border-top: 1px solid var(--border); + margin: 20px 0; +} + +pre, code { + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace; +} +pre { + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px; + color: var(--text); +} +code { + background: var(--surface-raised); + padding: 1px 5px; + border-radius: 4px; + font-size: 0.9em; +} + /* Layout */ .layout { display: flex; @@ -81,7 +167,7 @@ body { } .sidebar-nav a:hover, .sidebar-nav a.active { - background: #f1f5f9; + background: var(--surface-hover); color: var(--primary); } .sidebar-nav a svg { @@ -133,15 +219,56 @@ body { text-decoration: none; line-height: 1.4; } -.btn:hover { background: #f8fafc; box-shadow: var(--shadow); } -.btn-primary { background: var(--primary); color: #fff; border-color: var(--primary); } -.btn-primary:hover { background: var(--primary-hover); } -.btn-danger { background: var(--danger); color: #fff; border-color: var(--danger); } -.btn-danger:hover { background: var(--danger-hover); } +.btn:hover:not(.btn-primary):not(.btn-danger) { + background: var(--surface-hover); + box-shadow: var(--shadow); +} +.btn-primary { + background: var(--primary); + color: var(--text-on-primary); + border-color: var(--primary); +} +.btn-primary:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); + box-shadow: var(--shadow); +} +.btn-danger { + background: var(--danger-subtle); + color: var(--danger-text); + border-color: var(--danger-border); +} +.btn-danger:hover { + background: var(--danger-subtle-hover); + border-color: var(--danger); + color: var(--danger-hover); + box-shadow: 0 0 0 1px var(--danger-border); +} +.btn-danger:focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--danger-focus-ring); +} .btn-sm { padding: 4px 10px; font-size: .8rem; } .btn-block { width: 100%; justify-content: center; } -.btn-icon { padding: 6px; border: none; background: transparent; } -.btn-icon:hover { background: #f1f5f9; border-radius: 4px; } +.btn-icon { + padding: 6px; + border: none; + background: transparent; + color: var(--text-secondary); +} +.btn-icon:hover:not(.btn-icon-danger) { + background: var(--surface-hover); + border-radius: 4px; + color: var(--text); +} +.btn-icon-danger { + color: var(--danger-text); +} +.btn-icon-danger:hover { + background: var(--danger-subtle); + color: var(--danger-hover); + border-radius: 4px; +} /* Forms */ .form-group { @@ -172,7 +299,7 @@ body { .form-group textarea:focus { outline: none; border-color: var(--primary); - box-shadow: 0 0 0 3px rgba(37,99,235,.12); + box-shadow: 0 0 0 3px var(--focus-ring); } .form-row { display: flex; @@ -190,12 +317,12 @@ body { text-transform: uppercase; letter-spacing: .03em; } -.badge-draft { background: #f1f5f9; color: #64748b; } -.badge-active { background: #dcfce7; color: #16a34a; } -.badge-archived { background: #fef3c7; color: #d97706; } -.badge-completed { background: #dcfce7; color: #16a34a; } -.badge-in_progress { background: #dbeafe; color: #2563eb; } -.badge-pending { background: #f1f5f9; color: #64748b; } +.badge-draft { background: var(--badge-draft-bg); color: var(--badge-draft-fg); } +.badge-active { background: var(--badge-active-bg); color: var(--badge-active-fg); } +.badge-archived { background: var(--badge-archived-bg); color: var(--badge-archived-fg); } +.badge-completed { background: var(--badge-active-bg); color: var(--badge-active-fg); } +.badge-in_progress { background: var(--badge-in-progress-bg); color: var(--badge-in-progress-fg); } +.badge-pending { background: var(--badge-pending-bg); color: var(--badge-pending-fg); } /* Page header */ .page-header { @@ -282,44 +409,44 @@ table.data-table th { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; - background: #f8fafc; + background: var(--table-header-bg); position: sticky; top: 0; z-index: 2; box-shadow: 0 1px 0 var(--border); } table.data-table tr:hover td { - background: #f8fafc; + background: var(--row-hover-bg); } table.data-table tbody tr.row-test-data td { - background: #fffde7; + background: var(--test-row-bg); } table.data-table tbody tr.row-test-data:hover td { - background: #fff9c4; + background: var(--test-row-hover-bg); } #qdb-table tbody tr.row-test-data td { - background: #fffde7; + background: var(--test-row-bg); } #qdb-table tbody tr.row-test-data:hover td { - background: #fff9c4; + background: var(--test-row-hover-bg); } table.data-table .sticky-col { position: sticky; left: 0; z-index: 1; background: var(--surface); - box-shadow: 2px 0 4px rgba(0, 0, 0, 0.06); + box-shadow: 2px 0 4px var(--sticky-shadow); } table.data-table thead th.sticky-col { z-index: 5; top: 0; - background: #f8fafc; + background: var(--table-header-bg); } table.data-table tbody tr.row-test-data td.sticky-col { - background: #fffde7; + background: var(--test-row-bg); } table.data-table tbody tr.row-test-data:hover td.sticky-col { - background: #fff9c4; + background: var(--test-row-hover-bg); } th.sortable { cursor: pointer; user-select: none; } th.sortable::after { content: ' \2195'; opacity: .4; } @@ -344,7 +471,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } cursor: pointer; user-select: none; } -.question-header:hover { background: #f8fafc; } +.question-header:hover { background: var(--row-hover-bg); } .drag-handle { cursor: grab; color: var(--text-secondary); @@ -359,7 +486,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .question-header .q-type { font-size: .8rem; color: var(--text-secondary); - background: #f1f5f9; + background: var(--surface-raised); padding: 2px 8px; border-radius: 4px; } @@ -392,15 +519,15 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } border: 1px solid var(--border); border-radius: 6px; margin-bottom: 4px; - background: #fafbfc; + background: var(--surface-muted); font-size: .875rem; } .option-item .opt-text { flex: 1; } .option-item .opt-points { font-weight: 600; - color: var(--primary); + color: var(--primary-text); font-size: .8rem; - background: #eff6ff; + background: var(--primary-subtle); padding: 1px 6px; border-radius: 4px; } @@ -409,7 +536,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .translations-panel { margin-top: 12px; padding: 12px; - background: #f8fafc; + background: var(--surface-muted); border-radius: 6px; border: 1px solid var(--border); } @@ -454,7 +581,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } outline: none; border-color: var(--primary); background: var(--surface); - box-shadow: 0 0 0 3px rgba(37,99,235,.1); + box-shadow: 0 0 0 3px var(--focus-ring); } /* Login */ @@ -498,9 +625,9 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } max-width: 360px; } .toast.show { opacity: 1; transform: translateX(0); } -.toast-success { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0; } -.toast-error { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; } -.toast-info { background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe; } +.toast-success { background: var(--toast-success-bg); color: var(--toast-success-fg); border: 1px solid var(--toast-success-border); } +.toast-error { background: var(--toast-error-bg); color: var(--toast-error-fg); border: 1px solid var(--toast-error-border); } +.toast-info { background: var(--toast-info-bg); color: var(--toast-info-fg); border: 1px solid var(--toast-info-border); } /* Filter bar */ .filter-bar { @@ -598,10 +725,10 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } /* Role badges */ -.badge-admin { background: #ede9fe; color: #6d28d9; } -.badge-supervisor { background: #fce7f3; color: #be185d; } -.badge-coach { background: #d1fae5; color: #065f46; } -.badge-you { background: #dbeafe; color: #1d4ed8; font-size: .7rem; } +.badge-admin { background: var(--badge-admin-bg); color: var(--badge-admin-fg); } +.badge-supervisor { background: var(--badge-supervisor-bg); color: var(--badge-supervisor-fg); } +.badge-coach { background: var(--badge-coach-bg); color: var(--badge-coach-fg); } +.badge-you { background: var(--badge-you-bg); color: var(--badge-you-fg); font-size: .7rem; } /* Assignment page layout */ .assign-layout { @@ -632,9 +759,9 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } border: 1px solid transparent; margin-bottom: 4px; } -.coach-list-item:hover { background: #f1f5f9; } +.coach-list-item:hover { background: var(--surface-hover); } .coach-list-item.active { - background: #eff6ff; + background: var(--primary-subtle); border-color: var(--primary); } .coach-name { font-weight: 600; font-size: .9rem; } @@ -650,11 +777,11 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .selection-info { margin-top: 10px; padding: 8px 12px; - background: #eff6ff; - border: 1px solid #bfdbfe; + background: var(--primary-subtle); + border: 1px solid var(--primary-border); border-radius: 6px; font-size: .875rem; - color: #1e40af; + color: var(--primary-text); } /* Tabs */ @@ -695,7 +822,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } /* Inline form cards (add/edit question, add/edit option) */ .inline-form-card { - background: #f8fafc; + background: var(--surface-muted); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; @@ -722,7 +849,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .type-select:focus { outline: none; border-color: var(--primary); - box-shadow: 0 0 0 3px rgba(37,99,235,.12); + box-shadow: 0 0 0 3px var(--focus-ring); } /* Checkbox label */ @@ -776,8 +903,8 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .opt-branch { font-size: .75rem; font-weight: 600; - color: #7c3aed; - background: #ede9fe; + color: var(--badge-branch-fg); + background: var(--badge-branch-bg); padding: 1px 6px; border-radius: 4px; white-space: nowrap; @@ -846,13 +973,13 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .trans-select:focus { outline: none; border-color: var(--primary); - box-shadow: 0 0 0 3px rgba(37,99,235,.12); + box-shadow: 0 0 0 3px var(--focus-ring); } .trans-type-select { width: auto; min-width: 140px; flex-shrink: 0; } .trans-lang-panel { margin-bottom: 16px; padding: 12px 14px; - background: #f8fafc; + background: var(--surface-muted); border: 1px solid var(--border); border-radius: var(--radius); } @@ -878,7 +1005,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); - background: #f1f5f9; + background: var(--table-header-bg); border: 1px solid var(--border); border-bottom: none; border-radius: var(--radius) var(--radius) 0 0; @@ -908,7 +1035,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } color: var(--text-secondary); margin: 0 0 8px; padding-bottom: 6px; - border-bottom: 1px solid #e2e8f0; + border-bottom: 1px solid var(--border); } .trans-groups-wrap .trans-list-header { margin-top: 0; } .trans-list-in-group { @@ -932,10 +1059,10 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } gap: 12px; align-items: center; padding: 10px 14px; - border-bottom: 1px solid #f1f5f9; + border-bottom: 1px solid var(--border); } .trans-list-item:last-child { border-bottom: none; } -.trans-list-item:hover { background: #f8fafc; } +.trans-list-item:hover { background: var(--row-hover-bg); } .trans-list-item-missing { background: var(--trans-missing-bg); box-shadow: inset 3px 0 0 var(--trans-missing-accent); @@ -965,8 +1092,8 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } word-break: break-word; } .lang-chip-fixed { - background: #dbeafe; - border-color: #93c5fd; + background: var(--lang-chip-fixed-bg); + border-color: var(--lang-chip-fixed-border); } .trans-list-item .trans-cell-input { border: 1px solid var(--border); @@ -1016,7 +1143,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } /* ── Language manager ── */ .lang-manager { - background: #f8fafc; + background: var(--surface-muted); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; @@ -1090,7 +1217,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .trans-search-input:focus { outline: none; border-color: var(--primary); - box-shadow: 0 0 0 3px rgba(37,99,235,.12); + box-shadow: 0 0 0 3px var(--focus-ring); } .trans-filter-types { display: flex; @@ -1112,7 +1239,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .trans-table th { position: sticky; top: 0; - background: #f1f5f9; + background: var(--table-header-bg); font-weight: 600; text-align: left; padding: 8px 10px; @@ -1121,8 +1248,8 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } z-index: 1; } .trans-table td { padding: 2px 4px; } -.trans-table tbody tr { border-bottom: 1px solid #f1f5f9; } -.trans-table tbody tr:hover { background: #f8fafc; } +.trans-table tbody tr { border-bottom: 1px solid var(--border); } +.trans-table tbody tr:hover { background: var(--row-hover-bg); } .trans-row-hidden { display: none !important; } #tabContent-translations .trans-list, #tabContent-translations .trans-list-header { @@ -1153,10 +1280,10 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } text-transform: uppercase; letter-spacing: .3px; } -.badge-q { background: #dbeafe; color: #1e40af; } -.badge-opt { background: #e2e8f0; color: #475569; } -.badge-app { background: #d1fae5; color: #065f46; } -.badge-str { background: #ede9fe; color: #5b21b6; } +.badge-q { background: var(--badge-q-bg); color: var(--badge-q-fg); } +.badge-opt { background: var(--badge-opt-bg); color: var(--badge-opt-fg); } +.badge-app { background: var(--badge-app-bg); color: var(--badge-app-fg); } +.badge-str { background: var(--badge-str-bg); color: var(--badge-str-fg); } .cell-trans { position: relative; } .cell-missing { background: var(--trans-missing-bg); } @@ -1179,7 +1306,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } outline: none; border-color: var(--primary); background: var(--surface); - box-shadow: 0 0 0 2px rgba(37,99,235,.1); + box-shadow: 0 0 0 2px var(--focus-ring); } .trans-cell-input::placeholder { color: var(--trans-missing-placeholder); @@ -1193,7 +1320,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } animation: saveFlash .4s ease; } @keyframes saveFlash { - 0% { background: #dbeafe; } + 0% { background: var(--save-flash-bg); } 100% { background: var(--surface); } } .trans-list-item .trans-cell-input.save-flash { @@ -1217,7 +1344,7 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } .trans-progress-bar { width: 120px; height: 6px; - background: #e2e8f0; + background: var(--progress-track); border-radius: 3px; overflow: hidden; } @@ -1240,8 +1367,8 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } } .badge-warn { - background: #fef3c7; - color: #92400e; + background: var(--badge-warn-bg); + color: var(--badge-warn-fg); } .opt-key { @@ -1249,3 +1376,25 @@ th.sort-desc::after { content: ' \2193'; opacity: 1; } margin-right: 8px; color: var(--text-secondary); } + +/* Callouts */ +.callout-warn { + margin-bottom: 16px; + padding: 12px; + background: var(--badge-warn-bg); + border-radius: 8px; + color: var(--badge-warn-fg); +} +.callout-danger { + margin-bottom: 12px; + padding: 12px; + background: var(--toast-error-bg); + border-radius: 8px; + color: var(--toast-error-fg); +} +input:disabled, +select:disabled { + background: var(--surface-muted); + color: var(--text-secondary); + cursor: not-allowed; +} diff --git a/website/index.html b/website/index.html index 3463926..3406215 100644 --- a/website/index.html +++ b/website/index.html @@ -3,6 +3,8 @@
+ ++
Local / test only. Imports prefixed dev users (dev_*),
clients (DEV-CL-*), and completed questionnaire runs.
Existing real users are not modified. Conflicts are skipped (usernames, client codes, completions).
@@ -34,7 +34,7 @@ export function devPage() {
+
Destructive. Deletes every client, completion, coach, and supervisor (including non-dev accounts). Admin logins and questionnaire definitions are kept.
diff --git a/website/js/pages/editor.js b/website/js/pages/editor.js index cdf9288..b159499 100644 --- a/website/js/pages/editor.js +++ b/website/js/pages/editor.js @@ -745,7 +745,7 @@ function showAddQuestionForm() { ${esc(opt.text)} ${opt.points} pts ${opt.nextQuestionId ? `→ ${esc(opt.nextQuestionId)}` : ''} - + `).join(''); list.querySelectorAll('.remove-pending-opt').forEach(btn => { @@ -1063,7 +1063,7 @@ function optionItemHTML(q, o, editable) { ${o.nextQuestionId ? `→ ${esc(o.nextQuestionId)}` : ''} ${editable ? ` - + ` : ''} `; diff --git a/website/js/pages/users.js b/website/js/pages/users.js index 196064b..694ab32 100644 --- a/website/js/pages/users.js +++ b/website/js/pages/users.js @@ -224,7 +224,7 @@ function showCreateForm() {