From 2051880d7346d2682fe8471115a217fa0235c097 Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Mon, 18 May 2026 13:37:08 -0700 Subject: [PATCH] fix: dark mode CSS token gaps, interview score display, undefined CSS vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InterviewCard: remove erroneous *100 multiplier from scoreClass and scoreLabel — match_score is stored as 0-100 in the DB, not 0-1. This was producing scores like '1490%' for jobs with a 14.9 raw score. peregrine.css: define --color-hover token for light (rgba(0,0,0,0.06)) and dark (rgba(255,255,255,0.07)). Was undefined, leaving hover states on InterviewCard, InterviewsView, ReferencesView, ContactsView silent. InterviewCard + InterviewsView: replace var(--color-primary-muted,#e8f0ff) with var(--app-primary-light). The hardcoded #e8f0ff fallback is a bright light-blue that renders on dark backgrounds when the variable is undefined. WizardTrainingStep: --font-sans → --font-body (correct token name). ResumeSyncConfirmModal, ResumeLibraryCard, ResumeOptimizerPanel, resume-review sub-pages: --font-sm → --text-sm across all occurrences. --font-sm was never defined; most had a 0.875rem fallback (which matches --text-sm) but the correct token should be referenced directly. --- web/src/assets/peregrine.css | 7 +++ web/src/components/InterviewCard.vue | 6 +-- web/src/components/ResumeLibraryCard.vue | 10 ++-- web/src/components/ResumeOptimizerPanel.vue | 48 +++++++++---------- web/src/components/ResumeSyncConfirmModal.vue | 4 +- .../components/resume-review/ConfirmPage.vue | 10 ++-- .../resume-review/ExperiencePage.vue | 8 ++-- .../components/resume-review/SkillsPage.vue | 8 ++-- .../components/resume-review/SummaryPage.vue | 6 +-- web/src/views/InterviewsView.vue | 2 +- web/src/views/ResumesView.vue | 18 +++---- web/src/views/wizard/WizardTrainingStep.vue | 2 +- 12 files changed, 68 insertions(+), 61 deletions(-) diff --git a/web/src/assets/peregrine.css b/web/src/assets/peregrine.css index 3ce6817..1b70936 100644 --- a/web/src/assets/peregrine.css +++ b/web/src/assets/peregrine.css @@ -58,6 +58,9 @@ body { --score-low: var(--color-error); /* < 30% */ --score-none: var(--color-text-muted); + /* ── Hover overlay ── */ + --color-hover: rgba(0, 0, 0, 0.06); /* subtle darkening on light surfaces */ + /* ── Motion tokens ── */ --swipe-exit: 300ms; --swipe-spring: 400ms cubic-bezier(0.34, 1.56, 0.64, 1); @@ -91,6 +94,8 @@ body { --score-mid-high: #5ba3d9; /* lighter blue for dark bg */ + --color-hover: rgba(255, 255, 255, 0.07); /* subtle lightening on dark surfaces */ + --status-synced: #9b8fea; --status-survey: #b08fea; --status-phone: #4ec9be; @@ -112,6 +117,8 @@ body { --score-mid-high: #5ba3d9; + --color-hover: rgba(255, 255, 255, 0.07); + --status-synced: #9b8fea; --status-survey: #b08fea; --status-phone: #4ec9be; diff --git a/web/src/components/InterviewCard.vue b/web/src/components/InterviewCard.vue index 9566f0c..7cc7814 100644 --- a/web/src/components/InterviewCard.vue +++ b/web/src/components/InterviewCard.vue @@ -151,7 +151,7 @@ async function reclassifySignal(sig: StageSignal, newLabel: StageSignal['stage_s } const scoreClass = computed(() => { - const s = (props.job.match_score ?? 0) * 100 + const s = props.job.match_score ?? 0 if (s >= 85) return 'score--high' if (s >= 65) return 'score--mid' return 'score--low' @@ -159,7 +159,7 @@ const scoreClass = computed(() => { const scoreLabel = computed(() => props.job.match_score != null - ? `${Math.round(props.job.match_score * 100)}%` + ? `${Math.round(props.job.match_score)}%` : '—' ) @@ -588,7 +588,7 @@ async function saveFeedback() { background: var(--color-hover); } .btn-chip-active { - background: var(--color-primary-muted, #e8f0ff); + background: var(--app-primary-light); color: var(--color-primary); border-color: var(--color-primary); font-weight: 600; } diff --git a/web/src/components/ResumeLibraryCard.vue b/web/src/components/ResumeLibraryCard.vue index 00dc33d..2941ec4 100644 --- a/web/src/components/ResumeLibraryCard.vue +++ b/web/src/components/ResumeLibraryCard.vue @@ -106,7 +106,7 @@ onMounted(load) } .rlc__title { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-weight: 600; margin: 0; display: flex; @@ -128,7 +128,7 @@ onMounted(load) .rlc__name { font-weight: 500; - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); } .rlc__meta { @@ -143,7 +143,7 @@ onMounted(load) } .rlc__empty { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--color-text-muted, #64748b); } @@ -153,7 +153,7 @@ onMounted(load) } .rlc__loading { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--color-text-muted, #64748b); } @@ -183,7 +183,7 @@ onMounted(load) padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); cursor: pointer; border-radius: var(--radius-sm, 0.25rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); } .rlc__picker-item:hover, diff --git a/web/src/components/ResumeOptimizerPanel.vue b/web/src/components/ResumeOptimizerPanel.vue index 41b84b3..60f3a30 100644 --- a/web/src/components/ResumeOptimizerPanel.vue +++ b/web/src/components/ResumeOptimizerPanel.vue @@ -578,7 +578,7 @@ onUnmounted(stopPolling) } .rop__tier-note { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); background: var(--app-surface-alt, #f8fafc); border: 1px solid var(--app-border, #e2e8f0); @@ -603,13 +603,13 @@ onUnmounted(stopPolling) .rop__hint, .rop__empty { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); margin: 0; } .rop__error { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-danger, #dc2626); margin: 0; } @@ -618,7 +618,7 @@ onUnmounted(stopPolling) display: flex; align-items: center; gap: var(--space-2, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); } @@ -643,7 +643,7 @@ onUnmounted(stopPolling) border-radius: var(--radius-sm, 0.25rem); border-left: 3px solid transparent; background: var(--app-surface-alt, #f8fafc); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); cursor: pointer; user-select: none; } @@ -706,7 +706,7 @@ onUnmounted(stopPolling) } .rop__wordcount { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); } @@ -727,7 +727,7 @@ onUnmounted(stopPolling) background: color-mix(in srgb, var(--app-danger, #dc2626) 8%, transparent); border: 1px solid color-mix(in srgb, var(--app-danger, #dc2626) 30%, transparent); border-radius: var(--radius-md, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-danger, #dc2626); } @@ -736,7 +736,7 @@ onUnmounted(stopPolling) min-height: 20rem; padding: var(--space-3, 0.75rem); font-family: var(--font-mono, monospace); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); line-height: 1.6; border: 1px solid var(--app-border, #e2e8f0); border-radius: var(--radius-md, 0.5rem); @@ -762,7 +762,7 @@ onUnmounted(stopPolling) color: #fff; border: none; border-radius: var(--radius-md, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-weight: 500; cursor: pointer; transition: background 0.15s; @@ -781,7 +781,7 @@ onUnmounted(stopPolling) color: var(--app-text, #1e293b); border: 1px solid var(--app-border, #e2e8f0); border-radius: var(--radius-md, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-weight: 500; cursor: pointer; transition: background 0.15s; @@ -799,7 +799,7 @@ onUnmounted(stopPolling) } .rop__review-intro { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); margin: 0; padding: var(--space-3, 0.75rem) var(--space-4, 1rem); @@ -819,7 +819,7 @@ onUnmounted(stopPolling) } .rop__review-section-title { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; @@ -852,7 +852,7 @@ onUnmounted(stopPolling) gap: var(--space-1, 0.25rem); padding: 0.3em 0.75em; border-radius: var(--radius-full, 9999px); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); border: 1.5px solid var(--app-border, #e2e8f0); background: var(--app-surface, #fff); cursor: pointer; @@ -887,7 +887,7 @@ onUnmounted(stopPolling) gap: var(--space-1, 0.25rem); padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); border-radius: var(--radius-sm, 0.25rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); } .rop__diff-col--original { @@ -936,7 +936,7 @@ onUnmounted(stopPolling) } .rop__exp-company { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); } @@ -945,7 +945,7 @@ onUnmounted(stopPolling) display: inline-flex; align-items: center; gap: var(--space-1, 0.25rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); cursor: pointer; color: var(--app-text, #1e293b); } @@ -975,7 +975,7 @@ onUnmounted(stopPolling) background: none; border: none; color: var(--app-accent, #6366f1); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); cursor: pointer; padding: 0; } @@ -994,7 +994,7 @@ onUnmounted(stopPolling) background: var(--app-surface-alt, #f8fafc); border: 1px solid var(--app-border, #e2e8f0); border-radius: var(--radius-sm, 0.25rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); } .rop__history-date { @@ -1060,7 +1060,7 @@ onUnmounted(stopPolling) display: inline-flex; align-items: center; gap: var(--space-1, 0.25rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); cursor: pointer; color: var(--app-text, #1e293b); } @@ -1068,7 +1068,7 @@ onUnmounted(stopPolling) .rop__framing-context { width: 100%; padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-family: inherit; line-height: 1.5; border: 1px solid var(--app-border, #e2e8f0); @@ -1103,7 +1103,7 @@ onUnmounted(stopPolling) .rop__preview-hint { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text-muted, #64748b); margin: 0; } @@ -1132,7 +1132,7 @@ onUnmounted(stopPolling) color: var(--app-text-muted, #64748b); border: 1px solid var(--app-border, #e2e8f0); border-radius: var(--radius-md, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); cursor: pointer; transition: background 0.15s; } @@ -1163,7 +1163,7 @@ onUnmounted(stopPolling) display: inline-flex; align-items: center; gap: var(--space-2, 0.5rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); color: var(--app-text, #1e293b); cursor: pointer; user-select: none; @@ -1171,7 +1171,7 @@ onUnmounted(stopPolling) .rop__resume-name-input { padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-family: inherit; border: 1px solid var(--app-border, #e2e8f0); border-radius: var(--radius-sm, 0.25rem); diff --git a/web/src/components/ResumeSyncConfirmModal.vue b/web/src/components/ResumeSyncConfirmModal.vue index 3739b77..899bedb 100644 --- a/web/src/components/ResumeSyncConfirmModal.vue +++ b/web/src/components/ResumeSyncConfirmModal.vue @@ -131,7 +131,7 @@ defineEmits<{ background: var(--color-error, #dc2626); color: #fff; border: none; border-radius: var(--radius-md); cursor: pointer; - font-size: var(--font-sm); font-weight: 600; + font-size: var(--text-sm); font-weight: 600; } .btn-danger:hover { filter: brightness(1.1); } .btn-secondary { @@ -140,7 +140,7 @@ defineEmits<{ color: var(--color-text); border: 1px solid var(--color-border); border-radius: var(--radius-md); cursor: pointer; - font-size: var(--font-sm); + font-size: var(--text-sm); } .btn-secondary:hover { background: var(--color-surface-alt); } diff --git a/web/src/components/resume-review/ConfirmPage.vue b/web/src/components/resume-review/ConfirmPage.vue index 46e37a8..5075ad6 100644 --- a/web/src/components/resume-review/ConfirmPage.vue +++ b/web/src/components/resume-review/ConfirmPage.vue @@ -46,11 +46,11 @@ const emit = defineEmits<{ diff --git a/web/src/components/resume-review/ExperiencePage.vue b/web/src/components/resume-review/ExperiencePage.vue index 00ed8dd..8710543 100644 --- a/web/src/components/resume-review/ExperiencePage.vue +++ b/web/src/components/resume-review/ExperiencePage.vue @@ -67,18 +67,18 @@ function updateBullet(idx: number, value: string) { diff --git a/web/src/components/resume-review/SkillsPage.vue b/web/src/components/resume-review/SkillsPage.vue index af13b24..9407b10 100644 --- a/web/src/components/resume-review/SkillsPage.vue +++ b/web/src/components/resume-review/SkillsPage.vue @@ -58,7 +58,7 @@ const emit = defineEmits<{ diff --git a/web/src/components/resume-review/SummaryPage.vue b/web/src/components/resume-review/SummaryPage.vue index 14886a4..37f8a4c 100644 --- a/web/src/components/resume-review/SummaryPage.vue +++ b/web/src/components/resume-review/SummaryPage.vue @@ -51,9 +51,9 @@ const emit = defineEmits<{ .rp__diff-col { display: flex; flex-direction: column; gap: var(--space-2, 0.5rem); } .rp__diff-col--editable { gap: var(--space-2, 0.5rem); } .rp__diff-label { font-size: var(--font-xs, 0.75rem); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-muted, #4a5c7a); } -.rp__diff-text { font-size: var(--font-sm, 0.875rem); line-height: 1.6; padding: var(--space-3, 0.75rem); background: var(--color-surface-alt, #dde4f0); border-radius: var(--radius-sm, 0.25rem); margin: 0; } +.rp__diff-text { font-size: var(--text-sm); line-height: 1.6; padding: var(--space-3, 0.75rem); background: var(--color-surface-alt, #dde4f0); border-radius: var(--radius-sm, 0.25rem); margin: 0; } .rp__edit-textarea { - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); line-height: 1.6; padding: var(--space-3, 0.75rem); background: var(--color-surface, #eaeff8); @@ -67,5 +67,5 @@ const emit = defineEmits<{ font-family: inherit; } .rp__edit-textarea:focus { outline: 2px solid var(--color-accent, #c4732a); outline-offset: 2px; } -.rp__accept-toggle { display: inline-flex; align-items: center; gap: var(--space-2, 0.5rem); cursor: pointer; font-size: var(--font-sm, 0.875rem); } +.rp__accept-toggle { display: inline-flex; align-items: center; gap: var(--space-2, 0.5rem); cursor: pointer; font-size: var(--text-sm); } diff --git a/web/src/views/InterviewsView.vue b/web/src/views/InterviewsView.vue index a54b375..516d611 100644 --- a/web/src/views/InterviewsView.vue +++ b/web/src/views/InterviewsView.vue @@ -767,7 +767,7 @@ function formatRejectionDate(job: PipelineJob): string { background: var(--color-hover); } .btn-chip-active { - background: var(--color-primary-muted, #e8f0ff); + background: var(--app-primary-light); color: var(--color-primary); border-color: var(--color-primary); font-weight: 600; } diff --git a/web/src/views/ResumesView.vue b/web/src/views/ResumesView.vue index 3e0262f..f17b6c4 100644 --- a/web/src/views/ResumesView.vue +++ b/web/src/views/ResumesView.vue @@ -332,7 +332,7 @@ onBeforeRouteLeave(() => { .rv__item-star { color: var(--color-warning, #f59e0b); font-size: 1rem; flex-shrink: 0; margin-top: 2px; } .rv__item-info { display: flex; flex-direction: column; gap: 2px; } -.rv__item-name { font-weight: 500; font-size: var(--font-sm, 0.875rem); } +.rv__item-name { font-weight: 500; font-size: var(--text-sm); } .rv__item-meta { font-size: var(--font-xs, 0.75rem); color: var(--color-text-muted, #64748b); } .rv__item-source { font-size: var(--font-xs, 0.75rem); color: var(--color-accent, #6366f1); } @@ -340,7 +340,7 @@ onBeforeRouteLeave(() => { .rv__preview-header { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: var(--space-3, 0.75rem); } .rv__preview-meta { display: flex; align-items: center; gap: var(--space-2, 0.5rem); flex-wrap: wrap; } .rv__preview-name { font-size: var(--font-lg, 1.125rem); font-weight: 600; margin: 0; } -.rv__preview-words { font-size: var(--font-sm, 0.875rem); color: var(--color-text-muted, #64748b); } +.rv__preview-words { font-size: var(--text-sm); color: var(--color-text-muted, #64748b); } .rv__default-badge { font-size: var(--font-xs, 0.75rem); font-weight: 600; background: var(--color-success, #16a34a); color: #fff; @@ -352,7 +352,7 @@ onBeforeRouteLeave(() => { border: 1px solid var(--color-error, #dc2626); border-radius: var(--radius-md, 0.5rem); padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); - cursor: pointer; font-size: var(--font-sm, 0.875rem); + cursor: pointer; font-size: var(--text-sm); } .rv__delete-btn:disabled { opacity: 0.4; cursor: not-allowed; } @@ -364,13 +364,13 @@ onBeforeRouteLeave(() => { .rv__textarea { flex: 1; min-height: 400px; padding: var(--space-3, 0.75rem); border: 1px solid var(--color-border, #e2e8f0); border-radius: var(--radius-md, 0.5rem); - font-family: monospace; font-size: var(--font-sm, 0.875rem); resize: vertical; + font-family: monospace; font-size: var(--text-sm); resize: vertical; background: var(--color-surface-alt, #f8fafc); color: var(--color-text); } .rv__textarea:not([readonly]) { background: var(--color-surface); } .rv__edit-actions { display: flex; gap: var(--space-2, 0.5rem); } -.rv__error { color: var(--color-error, #dc2626); font-size: var(--font-sm, 0.875rem); } +.rv__error { color: var(--color-error, #dc2626); font-size: var(--text-sm); } .rv__download-menu { position: relative; } .rv__download-dropdown { @@ -382,11 +382,11 @@ onBeforeRouteLeave(() => { .rv__download-dropdown button { width: 100%; text-align: left; background: none; border: none; padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem); - cursor: pointer; font-size: var(--font-sm, 0.875rem); border-radius: var(--radius-sm, 0.25rem); + cursor: pointer; font-size: var(--text-sm); border-radius: var(--radius-sm, 0.25rem); } .rv__download-dropdown button:hover { background: var(--color-surface-alt, #f8fafc); } -.rv__loading, .rv__empty { color: var(--color-text-muted, #64748b); font-size: var(--font-sm, 0.875rem); } +.rv__loading, .rv__empty { color: var(--color-text-muted, #64748b); font-size: var(--text-sm); } /* Button styles — defined locally since no global button sheet exists yet */ .btn-secondary { @@ -396,7 +396,7 @@ onBeforeRouteLeave(() => { border-radius: var(--radius-md, 0.5rem); color: var(--color-text-muted); cursor: pointer; - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); white-space: nowrap; } .btn-secondary:hover:not(:disabled) { @@ -412,7 +412,7 @@ onBeforeRouteLeave(() => { border: none; border-radius: var(--radius-md, 0.5rem); cursor: pointer; - font-size: var(--font-sm, 0.875rem); + font-size: var(--text-sm); font-weight: 600; white-space: nowrap; display: inline-flex; diff --git a/web/src/views/wizard/WizardTrainingStep.vue b/web/src/views/wizard/WizardTrainingStep.vue index 994e5b8..87c32ed 100644 --- a/web/src/views/wizard/WizardTrainingStep.vue +++ b/web/src/views/wizard/WizardTrainingStep.vue @@ -65,7 +65,7 @@ function back() { router.push('/setup/resume') }