From 7e81e50e3e53f445cd67d54b0e447aa6a2a25841 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 8 Apr 2026 12:12:46 -0400 Subject: [PATCH 01/11] feat(theme): sweep indigo to violet across all remaining views and components Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/BriefingSetupWizard.vue | 4 ++-- frontend/src/components/EventSlideOver.vue | 10 +++++----- frontend/src/components/InlineAssistPanel.vue | 10 +++++----- frontend/src/components/NoteCard.vue | 6 +++--- frontend/src/components/ShareDialog.vue | 2 +- frontend/src/components/TagPill.vue | 2 +- frontend/src/components/TaskCard.vue | 4 ++-- frontend/src/components/ToolCallCard.vue | 8 ++++---- frontend/src/views/CalendarView.vue | 20 +++++++++---------- frontend/src/views/HomeView.vue | 18 ++++++++--------- frontend/src/views/NoteEditorView.vue | 2 +- frontend/src/views/NoteViewerView.vue | 6 +++--- frontend/src/views/ProjectView.vue | 12 +++++------ frontend/src/views/SettingsView.vue | 4 ++-- frontend/src/views/TaskViewerView.vue | 8 ++++---- 15 files changed, 58 insertions(+), 58 deletions(-) diff --git a/frontend/src/components/BriefingSetupWizard.vue b/frontend/src/components/BriefingSetupWizard.vue index d7e5c54..b7a3e57 100644 --- a/frontend/src/components/BriefingSetupWizard.vue +++ b/frontend/src/components/BriefingSetupWizard.vue @@ -247,7 +247,7 @@ async function finish() { } .wizard-progress-fill { height: 100%; - background: linear-gradient(90deg, #6366f1, #4f46e5); + background: linear-gradient(90deg, #7c3aed, #5b21b6); transition: width 0.3s ease; } .wizard-step-label { @@ -375,7 +375,7 @@ async function finish() { } .btn-wizard-primary { padding: 0.5rem 1.25rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: 8px; diff --git a/frontend/src/components/EventSlideOver.vue b/frontend/src/components/EventSlideOver.vue index 0eb2c39..854a0fb 100644 --- a/frontend/src/components/EventSlideOver.vue +++ b/frontend/src/components/EventSlideOver.vue @@ -232,7 +232,7 @@ async function doDelete() {
- +
@@ -357,7 +357,7 @@ async function doDelete() { box-sizing: border-box; transition: border-color 0.15s; } -.so-input:focus { outline: none; border-color: var(--color-primary, #6366f1); } +.so-input:focus { outline: none; border-color: var(--color-primary, #7c3aed); } .so-textarea { resize: vertical; min-height: 5rem; font-family: inherit; } @@ -376,8 +376,8 @@ async function doDelete() { transition: all 0.15s; } .toggle-btn.active { - background: var(--color-primary, #6366f1); - border-color: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); + border-color: var(--color-primary, #7c3aed); color: #fff; } @@ -403,7 +403,7 @@ async function doDelete() { } .btn-primary { - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: 8px; diff --git a/frontend/src/components/InlineAssistPanel.vue b/frontend/src/components/InlineAssistPanel.vue index 33956be..2b2d545 100644 --- a/frontend/src/components/InlineAssistPanel.vue +++ b/frontend/src/components/InlineAssistPanel.vue @@ -94,10 +94,10 @@ const markers: Record = { /* ── Streaming ── */ .iap-streaming { - border-color: var(--color-primary, #6366f1); + border-color: var(--color-primary, #7c3aed); } .iap-streaming .iap-header { - background: color-mix(in srgb, var(--color-primary, #6366f1) 8%, var(--color-bg-secondary)); + background: color-mix(in srgb, var(--color-primary, #7c3aed) 8%, var(--color-bg-secondary)); } .iap-pulse { @@ -105,7 +105,7 @@ const markers: Record = { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); flex-shrink: 0; animation: iap-pulse 1.2s ease-in-out infinite; } @@ -172,8 +172,8 @@ const markers: Record = { font-family: inherit; } .iap-btn-toggle:hover { - border-color: var(--color-primary, #6366f1); - color: var(--color-primary, #6366f1); + border-color: var(--color-primary, #7c3aed); + color: var(--color-primary, #7c3aed); } .iap-actions { diff --git a/frontend/src/components/NoteCard.vue b/frontend/src/components/NoteCard.vue index eacdcf8..3408fee 100644 --- a/frontend/src/components/NoteCard.vue +++ b/frontend/src/components/NoteCard.vue @@ -64,11 +64,11 @@ function goEdit() { text-decoration: none; color: inherit; background: var(--color-bg-card); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(99, 102, 241, 0.06); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(124, 58, 237, 0.06); transition: box-shadow 0.2s, transform 0.18s ease; } .note-card:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10), 0 0 0 1px rgba(99, 102, 241, 0.14); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10), 0 0 0 1px rgba(124, 58, 237, 0.14); transform: translateY(-2px); } @@ -89,7 +89,7 @@ function goEdit() { } .note-card.compact:hover { box-shadow: none; - background: rgba(99, 102, 241, 0.04); + background: rgba(124, 58, 237, 0.04); transform: none; } .note-title-compact { diff --git a/frontend/src/components/ShareDialog.vue b/frontend/src/components/ShareDialog.vue index 78ebf92..7edc1f9 100644 --- a/frontend/src/components/ShareDialog.vue +++ b/frontend/src/components/ShareDialog.vue @@ -337,7 +337,7 @@ onMounted(async () => { .btn-add-share { padding: 0.45rem 1rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: 6px; diff --git a/frontend/src/components/TagPill.vue b/frontend/src/components/TagPill.vue index 687ee9b..2c0d797 100644 --- a/frontend/src/components/TagPill.vue +++ b/frontend/src/components/TagPill.vue @@ -40,7 +40,7 @@ defineEmits<{ } .tag-pill:hover { color: var(--color-primary); - background: rgba(99, 102, 241, 0.08); + background: rgba(124, 58, 237, 0.08); } .dismiss { background: none; diff --git a/frontend/src/components/TaskCard.vue b/frontend/src/components/TaskCard.vue index f109e7e..224143d 100644 --- a/frontend/src/components/TaskCard.vue +++ b/frontend/src/components/TaskCard.vue @@ -112,11 +112,11 @@ function isOverdue(): boolean { text-decoration: none; color: inherit; background: var(--color-bg-card); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(99, 102, 241, 0.06); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(124, 58, 237, 0.06); transition: box-shadow 0.2s, transform 0.18s ease; } .task-card:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10), 0 0 0 1px rgba(99, 102, 241, 0.14); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.10), 0 0 0 1px rgba(124, 58, 237, 0.14); transform: translateY(-2px); } diff --git a/frontend/src/components/ToolCallCard.vue b/frontend/src/components/ToolCallCard.vue index 54c0fdb..e21228e 100644 --- a/frontend/src/components/ToolCallCard.vue +++ b/frontend/src/components/ToolCallCard.vue @@ -751,7 +751,7 @@ function closeEventSlideOver(changed = false) { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); flex-shrink: 0; } @@ -773,14 +773,14 @@ function closeEventSlideOver(changed = false) { } .tool-event-card.clickable { cursor: pointer; } .tool-event-card.clickable:hover { - border-color: var(--color-primary, #6366f1); - background: color-mix(in srgb, var(--color-primary, #6366f1) 6%, var(--color-bg-card, #16161a)); + border-color: var(--color-primary, #7c3aed); + background: color-mix(in srgb, var(--color-primary, #7c3aed) 6%, var(--color-bg-card, #16161a)); } .tool-event-card-dot { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); flex-shrink: 0; margin-top: 3px; } diff --git a/frontend/src/views/CalendarView.vue b/frontend/src/views/CalendarView.vue index 590ac3f..fd5f3cf 100644 --- a/frontend/src/views/CalendarView.vue +++ b/frontend/src/views/CalendarView.vue @@ -435,7 +435,7 @@ const upcomingGrouped = computed(() => { } .btn-new-event { - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: 8px; @@ -482,8 +482,8 @@ const upcomingGrouped = computed(() => { } :deep(.fc-button:hover), :deep(.fc-button-active) { - background: var(--color-primary, #6366f1) !important; - border-color: var(--color-primary, #6366f1) !important; + background: var(--color-primary, #7c3aed) !important; + border-color: var(--color-primary, #7c3aed) !important; color: #fff !important; } :deep(.fc-button:focus) { box-shadow: none !important; } @@ -494,7 +494,7 @@ const upcomingGrouped = computed(() => { font-size: 0.82rem; } :deep(.fc-daygrid-day.fc-day-today) { - background: rgba(99, 102, 241, 0.08); + background: rgba(124, 58, 237, 0.08); } :deep(.fc-event) { border-radius: 4px; @@ -505,7 +505,7 @@ const upcomingGrouped = computed(() => { } :deep(.fc-event-main) { color: #fff; } :deep(.fc-event:not([style*="background"])) { - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); } :deep(.fc-daygrid-day-frame) { min-height: 5rem; } :deep(.fc-scrollgrid) { border-color: var(--color-border, #2a2b30); } @@ -574,8 +574,8 @@ const upcomingGrouped = computed(() => { background: rgba(255,255,255,0.08); } .picker-month.active { - background: rgba(99,102,241,0.22); - color: var(--color-primary, #818cf8); + background: rgba(124,58,237,0.22); + color: var(--color-primary, #a78bfa); font-weight: 700; } @@ -634,7 +634,7 @@ const upcomingGrouped = computed(() => { .upcoming-card-accent { width: 4px; flex-shrink: 0; - background: var(--ev-color, #6366f1); + background: var(--ev-color, #7c3aed); } .upcoming-card-body { @@ -691,7 +691,7 @@ const upcomingGrouped = computed(() => { .popover-accent { height: 4px; - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); } .popover-content { @@ -750,7 +750,7 @@ const upcomingGrouped = computed(() => { } .popover-btn:hover { opacity: 0.85; } .popover-btn--edit { - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); color: #fff; } .popover-btn--close { diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index c940f8e..8300a0d 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -569,7 +569,7 @@ function formatUpcomingTime(event: EventEntry): string { border-radius: var(--radius-lg); padding: 1.25rem 1.5rem; margin-bottom: 1.75rem; - box-shadow: 0 2px 16px rgba(99, 102, 241, 0.08), 0 1px 4px rgba(0, 0, 0, 0.06); + box-shadow: 0 2px 16px rgba(124, 58, 237, 0.08), 0 1px 4px rgba(0, 0, 0, 0.06); } .hero-top { display: flex; @@ -605,20 +605,20 @@ function formatUpcomingTime(event: EventEntry): string { align-items: center; gap: 0.4rem; padding: 0.55rem 1.1rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border-radius: var(--radius-sm); text-decoration: none; font-size: 0.9rem; font-weight: 600; white-space: nowrap; - box-shadow: 0 1px 6px rgba(99, 102, 241, 0.3); + box-shadow: 0 1px 6px rgba(124, 58, 237, 0.3); transition: opacity 0.15s, box-shadow 0.15s; flex-shrink: 0; } .btn-workspace-hero:hover { opacity: 0.9; - box-shadow: 0 3px 14px rgba(99, 102, 241, 0.45); + box-shadow: 0 3px 14px rgba(124, 58, 237, 0.45); } .hero-milestones { margin-bottom: 0.75rem; } @@ -760,8 +760,8 @@ function formatUpcomingTime(event: EventEntry): string { font-weight: 500; } .urgency-in-progress { - background: color-mix(in srgb, #6366f1 15%, transparent); - color: #6366f1; + background: color-mix(in srgb, #7c3aed 15%, transparent); + color: #7c3aed; } .urgency-todo { background: color-mix(in srgb, var(--color-text-muted) 12%, transparent); @@ -892,14 +892,14 @@ function formatUpcomingTime(event: EventEntry): string { width: 100%; } .upcoming-event-card:hover { - border-color: var(--color-primary, #6366f1); - background: color-mix(in srgb, var(--color-primary, #6366f1) 6%, var(--color-bg-card)); + border-color: var(--color-primary, #7c3aed); + background: color-mix(in srgb, var(--color-primary, #7c3aed) 6%, var(--color-bg-card)); } .upcoming-event-dot { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #6366f1); + background: var(--color-primary, #7c3aed); flex-shrink: 0; margin-top: 4px; } diff --git a/frontend/src/views/NoteEditorView.vue b/frontend/src/views/NoteEditorView.vue index c364974..488fad5 100644 --- a/frontend/src/views/NoteEditorView.vue +++ b/frontend/src/views/NoteEditorView.vue @@ -731,7 +731,7 @@ onUnmounted(() => assist.clearSelection()); transition: border-color 0.15s; } .sb-select:focus, .sb-input:focus { - border-color: var(--color-primary, #6366f1); + border-color: var(--color-primary, #7c3aed); } /* Tag suggest row inside sidebar */ diff --git a/frontend/src/views/NoteViewerView.vue b/frontend/src/views/NoteViewerView.vue index 8085785..7bf4705 100644 --- a/frontend/src/views/NoteViewerView.vue +++ b/frontend/src/views/NoteViewerView.vue @@ -350,17 +350,17 @@ async function convertToTask() { padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; font-weight: 600; - box-shadow: 0 2px 8px rgba(99, 102, 241, 0.28); + box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); transition: box-shadow 0.15s, opacity 0.15s; } .btn-edit:hover { - box-shadow: 0 4px 14px rgba(99, 102, 241, 0.42); + box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); opacity: 0.95; color: #fff; } diff --git a/frontend/src/views/ProjectView.vue b/frontend/src/views/ProjectView.vue index 299599b..8b13068 100644 --- a/frontend/src/views/ProjectView.vue +++ b/frontend/src/views/ProjectView.vue @@ -676,17 +676,17 @@ async function confirmDelete() { align-items: center; gap: 0.35rem; padding: 0.45rem 1rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 0.875rem; font-weight: 600; text-decoration: none; - box-shadow: 0 2px 8px rgba(99, 102, 241, 0.28); + box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); transition: box-shadow 0.15s, opacity 0.15s; } -.btn-workspace:hover { box-shadow: 0 4px 14px rgba(99, 102, 241, 0.42); opacity: 0.95; color: #fff; } +.btn-workspace:hover { box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); opacity: 0.95; color: #fff; } .btn-share { padding: 0.4rem 0.8rem; @@ -857,7 +857,7 @@ async function confirmDelete() { .btn-save-panel { padding: 0.45rem 0.9rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: var(--radius-sm); @@ -866,10 +866,10 @@ async function confirmDelete() { font-weight: 600; font-family: inherit; width: 100%; - box-shadow: 0 2px 8px rgba(99, 102, 241, 0.25); + box-shadow: 0 2px 8px rgba(124, 58, 237, 0.25); transition: box-shadow 0.15s, opacity 0.15s; } -.btn-save-panel:hover:not(:disabled) { box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4); opacity: 0.95; } +.btn-save-panel:hover:not(:disabled) { box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4); opacity: 0.95; } .btn-save-panel:disabled { opacity: 0.45; cursor: default; } /* ── Content area ────────────────────────────────────────────── */ diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index ba1fffc..40d8b12 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -3024,7 +3024,7 @@ FABLE_API_KEY=<your-api-key> } .sidebar-item.active { color: var(--color-primary); - background: rgba(99, 102, 241, 0.08); + background: rgba(124, 58, 237, 0.08); border-left-color: var(--color-primary); font-weight: 600; } @@ -3489,7 +3489,7 @@ FABLE_API_KEY=<your-api-key> } .sidebar-item.active { border-bottom-color: var(--color-primary); - background: rgba(99, 102, 241, 0.08); + background: rgba(124, 58, 237, 0.08); } .settings-grid { grid-template-columns: 1fr; diff --git a/frontend/src/views/TaskViewerView.vue b/frontend/src/views/TaskViewerView.vue index 7295be0..754152d 100644 --- a/frontend/src/views/TaskViewerView.vue +++ b/frontend/src/views/TaskViewerView.vue @@ -480,23 +480,23 @@ const subTaskProgress = computed(() => { padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; font-weight: 600; - box-shadow: 0 2px 8px rgba(99, 102, 241, 0.28); + box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); transition: box-shadow 0.15s, opacity 0.15s; } .btn-edit:hover { - box-shadow: 0 4px 14px rgba(99, 102, 241, 0.42); + box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); opacity: 0.95; color: #fff; } .btn-advance { padding: 0.45rem 1rem; - background: linear-gradient(135deg, #6366f1, #4f46e5); + background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: var(--radius-sm); From 2ac894b5d16b38af62ccaa9b94ed5b56f398eb97 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 8 Apr 2026 12:19:38 -0400 Subject: [PATCH 02/11] refactor(theme): DRY hardcoded violet values into CSS custom properties Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/AppHeader.vue | 6 +++--- frontend/src/components/BriefingSetupWizard.vue | 4 ++-- frontend/src/components/ChatInputBar.vue | 2 +- frontend/src/components/EventSlideOver.vue | 8 ++++---- frontend/src/components/InlineAssistPanel.vue | 10 +++++----- frontend/src/components/ShareDialog.vue | 2 +- frontend/src/components/TagPill.vue | 2 +- frontend/src/components/ToolCallCard.vue | 8 ++++---- frontend/src/views/CalendarView.vue | 16 ++++++++-------- frontend/src/views/HomeView.vue | 8 ++++---- frontend/src/views/KnowledgeView.vue | 16 ++++++++-------- frontend/src/views/NoteEditorView.vue | 2 +- frontend/src/views/NoteViewerView.vue | 6 +++--- frontend/src/views/ProjectView.vue | 10 +++++----- frontend/src/views/SettingsView.vue | 2 +- frontend/src/views/TaskViewerView.vue | 8 ++++---- 16 files changed, 55 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index 2db3cf9..3c285e6 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -194,7 +194,7 @@ router.afterEach(() => { display: flex; align-items: center; gap: 2px; - background: rgba(124, 58, 237, 0.06); + background: var(--color-primary-faint); border-radius: 10px; padding: 3px; } @@ -217,7 +217,7 @@ router.afterEach(() => { } .nav-link:hover { color: var(--color-text-secondary); - background: rgba(124, 58, 237, 0.08); + background: var(--color-primary-tint); } .nav-link.router-link-active { color: #c4b5fd; @@ -387,7 +387,7 @@ router.afterEach(() => { border-radius: 8px; } .mobile-menu .nav-link.router-link-active { - background: rgba(124, 58, 237, 0.15); + background: var(--color-primary-wash); box-shadow: none; } .mobile-user .btn-logout { diff --git a/frontend/src/components/BriefingSetupWizard.vue b/frontend/src/components/BriefingSetupWizard.vue index b7a3e57..6f32449 100644 --- a/frontend/src/components/BriefingSetupWizard.vue +++ b/frontend/src/components/BriefingSetupWizard.vue @@ -247,7 +247,7 @@ async function finish() { } .wizard-progress-fill { height: 100%; - background: linear-gradient(90deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); transition: width 0.3s ease; } .wizard-step-label { @@ -375,7 +375,7 @@ async function finish() { } .btn-wizard-primary { padding: 0.5rem 1.25rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: 8px; diff --git a/frontend/src/components/ChatInputBar.vue b/frontend/src/components/ChatInputBar.vue index 96a8927..c8742c1 100644 --- a/frontend/src/components/ChatInputBar.vue +++ b/frontend/src/components/ChatInputBar.vue @@ -391,7 +391,7 @@ defineExpose({ focus, prefill }) display: flex; align-items: center; justify-content: center; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: 50%; diff --git a/frontend/src/components/EventSlideOver.vue b/frontend/src/components/EventSlideOver.vue index 854a0fb..50bf92b 100644 --- a/frontend/src/components/EventSlideOver.vue +++ b/frontend/src/components/EventSlideOver.vue @@ -357,7 +357,7 @@ async function doDelete() { box-sizing: border-box; transition: border-color 0.15s; } -.so-input:focus { outline: none; border-color: var(--color-primary, #7c3aed); } +.so-input:focus { outline: none; border-color: var(--color-primary); } .so-textarea { resize: vertical; min-height: 5rem; font-family: inherit; } @@ -376,8 +376,8 @@ async function doDelete() { transition: all 0.15s; } .toggle-btn.active { - background: var(--color-primary, #7c3aed); - border-color: var(--color-primary, #7c3aed); + background: var(--color-primary); + border-color: var(--color-primary); color: #fff; } @@ -403,7 +403,7 @@ async function doDelete() { } .btn-primary { - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: 8px; diff --git a/frontend/src/components/InlineAssistPanel.vue b/frontend/src/components/InlineAssistPanel.vue index 2b2d545..45c15c9 100644 --- a/frontend/src/components/InlineAssistPanel.vue +++ b/frontend/src/components/InlineAssistPanel.vue @@ -94,10 +94,10 @@ const markers: Record = { /* ── Streaming ── */ .iap-streaming { - border-color: var(--color-primary, #7c3aed); + border-color: var(--color-primary); } .iap-streaming .iap-header { - background: color-mix(in srgb, var(--color-primary, #7c3aed) 8%, var(--color-bg-secondary)); + background: color-mix(in srgb, var(--color-primary) 8%, var(--color-bg-secondary)); } .iap-pulse { @@ -105,7 +105,7 @@ const markers: Record = { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #7c3aed); + background: var(--color-primary); flex-shrink: 0; animation: iap-pulse 1.2s ease-in-out infinite; } @@ -172,8 +172,8 @@ const markers: Record = { font-family: inherit; } .iap-btn-toggle:hover { - border-color: var(--color-primary, #7c3aed); - color: var(--color-primary, #7c3aed); + border-color: var(--color-primary); + color: var(--color-primary); } .iap-actions { diff --git a/frontend/src/components/ShareDialog.vue b/frontend/src/components/ShareDialog.vue index 7edc1f9..23f84ba 100644 --- a/frontend/src/components/ShareDialog.vue +++ b/frontend/src/components/ShareDialog.vue @@ -337,7 +337,7 @@ onMounted(async () => { .btn-add-share { padding: 0.45rem 1rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: 6px; diff --git a/frontend/src/components/TagPill.vue b/frontend/src/components/TagPill.vue index 2c0d797..185599c 100644 --- a/frontend/src/components/TagPill.vue +++ b/frontend/src/components/TagPill.vue @@ -40,7 +40,7 @@ defineEmits<{ } .tag-pill:hover { color: var(--color-primary); - background: rgba(124, 58, 237, 0.08); + background: var(--color-primary-tint); } .dismiss { background: none; diff --git a/frontend/src/components/ToolCallCard.vue b/frontend/src/components/ToolCallCard.vue index e21228e..556175f 100644 --- a/frontend/src/components/ToolCallCard.vue +++ b/frontend/src/components/ToolCallCard.vue @@ -751,7 +751,7 @@ function closeEventSlideOver(changed = false) { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #7c3aed); + background: var(--color-primary); flex-shrink: 0; } @@ -773,14 +773,14 @@ function closeEventSlideOver(changed = false) { } .tool-event-card.clickable { cursor: pointer; } .tool-event-card.clickable:hover { - border-color: var(--color-primary, #7c3aed); - background: color-mix(in srgb, var(--color-primary, #7c3aed) 6%, var(--color-bg-card, #16161a)); + border-color: var(--color-primary); + background: color-mix(in srgb, var(--color-primary) 6%, var(--color-bg-card, #16161a)); } .tool-event-card-dot { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #7c3aed); + background: var(--color-primary); flex-shrink: 0; margin-top: 3px; } diff --git a/frontend/src/views/CalendarView.vue b/frontend/src/views/CalendarView.vue index fd5f3cf..aeb818f 100644 --- a/frontend/src/views/CalendarView.vue +++ b/frontend/src/views/CalendarView.vue @@ -435,7 +435,7 @@ const upcomingGrouped = computed(() => { } .btn-new-event { - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: 8px; @@ -482,8 +482,8 @@ const upcomingGrouped = computed(() => { } :deep(.fc-button:hover), :deep(.fc-button-active) { - background: var(--color-primary, #7c3aed) !important; - border-color: var(--color-primary, #7c3aed) !important; + background: var(--color-primary) !important; + border-color: var(--color-primary) !important; color: #fff !important; } :deep(.fc-button:focus) { box-shadow: none !important; } @@ -494,7 +494,7 @@ const upcomingGrouped = computed(() => { font-size: 0.82rem; } :deep(.fc-daygrid-day.fc-day-today) { - background: rgba(124, 58, 237, 0.08); + background: var(--color-primary-tint); } :deep(.fc-event) { border-radius: 4px; @@ -505,7 +505,7 @@ const upcomingGrouped = computed(() => { } :deep(.fc-event-main) { color: #fff; } :deep(.fc-event:not([style*="background"])) { - background: var(--color-primary, #7c3aed); + background: var(--color-primary); } :deep(.fc-daygrid-day-frame) { min-height: 5rem; } :deep(.fc-scrollgrid) { border-color: var(--color-border, #2a2b30); } @@ -575,7 +575,7 @@ const upcomingGrouped = computed(() => { } .picker-month.active { background: rgba(124,58,237,0.22); - color: var(--color-primary, #a78bfa); + color: var(--color-primary); font-weight: 700; } @@ -691,7 +691,7 @@ const upcomingGrouped = computed(() => { .popover-accent { height: 4px; - background: var(--color-primary, #7c3aed); + background: var(--color-primary); } .popover-content { @@ -750,7 +750,7 @@ const upcomingGrouped = computed(() => { } .popover-btn:hover { opacity: 0.85; } .popover-btn--edit { - background: var(--color-primary, #7c3aed); + background: var(--color-primary); color: #fff; } .popover-btn--close { diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 8300a0d..1840f4b 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -605,7 +605,7 @@ function formatUpcomingTime(event: EventEntry): string { align-items: center; gap: 0.4rem; padding: 0.55rem 1.1rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border-radius: var(--radius-sm); text-decoration: none; @@ -892,14 +892,14 @@ function formatUpcomingTime(event: EventEntry): string { width: 100%; } .upcoming-event-card:hover { - border-color: var(--color-primary, #7c3aed); - background: color-mix(in srgb, var(--color-primary, #7c3aed) 6%, var(--color-bg-card)); + border-color: var(--color-primary); + background: color-mix(in srgb, var(--color-primary) 6%, var(--color-bg-card)); } .upcoming-event-dot { width: 8px; height: 8px; border-radius: 50%; - background: var(--color-primary, #7c3aed); + background: var(--color-primary); flex-shrink: 0; margin-top: 4px; } diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue index 8f7f090..9c8015e 100644 --- a/frontend/src/views/KnowledgeView.vue +++ b/frontend/src/views/KnowledgeView.vue @@ -721,7 +721,7 @@ onUnmounted(() => { font-size: 0.78rem; } .today-link { - color: var(--color-primary, #7c3aed); + color: var(--color-primary); text-decoration: none; font-weight: 500; opacity: 0.85; @@ -777,7 +777,7 @@ onUnmounted(() => { border-radius: 8px; border: 1px solid rgba(124, 58, 237, 0.4); background: rgba(124, 58, 237, 0.12); - color: var(--color-primary, #a78bfa); + color: var(--color-primary); cursor: pointer; font-size: 0.85rem; font-weight: 500; @@ -830,8 +830,8 @@ onUnmounted(() => { } .filter-btn:hover { background: rgba(255,255,255,0.05); opacity: 1; } .filter-btn.active { - background: rgba(124, 58, 237, 0.15); - color: var(--color-primary, #a78bfa); + background: var(--color-primary-wash); + color: var(--color-primary); opacity: 1; } .filter-btn-label { flex: 1; } @@ -848,7 +848,7 @@ onUnmounted(() => { } .filter-btn.active .filter-count { background: rgba(124, 58, 237, 0.2); - color: var(--color-primary, #a78bfa); + color: var(--color-primary); } .filter-tag { font-size: 0.78rem; } @@ -893,7 +893,7 @@ onUnmounted(() => { outline: none; transition: border-color 0.15s; } -.search-input:focus { border-color: var(--color-primary, #7c3aed); } +.search-input:focus { border-color: var(--color-primary); } .sort-select { padding: 7px 10px; border-radius: 8px; @@ -920,9 +920,9 @@ onUnmounted(() => { } .btn-graph:hover { color: var(--color-text); border-color: rgba(255,255,255,0.2); } .btn-graph.active { - background: rgba(124, 58, 237, 0.15); + background: var(--color-primary-wash); border-color: rgba(124, 58, 237, 0.35); - color: var(--color-primary, #a78bfa); + color: var(--color-primary); } /* ── Card grid ───────────────────────────────────────────── */ diff --git a/frontend/src/views/NoteEditorView.vue b/frontend/src/views/NoteEditorView.vue index 488fad5..95616a9 100644 --- a/frontend/src/views/NoteEditorView.vue +++ b/frontend/src/views/NoteEditorView.vue @@ -731,7 +731,7 @@ onUnmounted(() => assist.clearSelection()); transition: border-color 0.15s; } .sb-select:focus, .sb-input:focus { - border-color: var(--color-primary, #7c3aed); + border-color: var(--color-primary); } /* Tag suggest row inside sidebar */ diff --git a/frontend/src/views/NoteViewerView.vue b/frontend/src/views/NoteViewerView.vue index 7bf4705..0d359f7 100644 --- a/frontend/src/views/NoteViewerView.vue +++ b/frontend/src/views/NoteViewerView.vue @@ -350,17 +350,17 @@ async function convertToTask() { padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; font-weight: 600; - box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); + box-shadow: var(--glow-cta); transition: box-shadow 0.15s, opacity 0.15s; } .btn-edit:hover { - box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); + box-shadow: var(--glow-cta-hover); opacity: 0.95; color: #fff; } diff --git a/frontend/src/views/ProjectView.vue b/frontend/src/views/ProjectView.vue index 8b13068..69f4f6a 100644 --- a/frontend/src/views/ProjectView.vue +++ b/frontend/src/views/ProjectView.vue @@ -676,17 +676,17 @@ async function confirmDelete() { align-items: center; gap: 0.35rem; padding: 0.45rem 1rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 0.875rem; font-weight: 600; text-decoration: none; - box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); + box-shadow: var(--glow-cta); transition: box-shadow 0.15s, opacity 0.15s; } -.btn-workspace:hover { box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); opacity: 0.95; color: #fff; } +.btn-workspace:hover { box-shadow: var(--glow-cta-hover); opacity: 0.95; color: #fff; } .btn-share { padding: 0.4rem 0.8rem; @@ -857,7 +857,7 @@ async function confirmDelete() { .btn-save-panel { padding: 0.45rem 0.9rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: var(--radius-sm); @@ -866,7 +866,7 @@ async function confirmDelete() { font-weight: 600; font-family: inherit; width: 100%; - box-shadow: 0 2px 8px rgba(124, 58, 237, 0.25); + box-shadow: var(--glow-soft); transition: box-shadow 0.15s, opacity 0.15s; } .btn-save-panel:hover:not(:disabled) { box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4); opacity: 0.95; } diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index 40d8b12..7cd41ed 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -3489,7 +3489,7 @@ FABLE_API_KEY=<your-api-key> } .sidebar-item.active { border-bottom-color: var(--color-primary); - background: rgba(124, 58, 237, 0.08); + background: var(--color-primary-tint); } .settings-grid { grid-template-columns: 1fr; diff --git a/frontend/src/views/TaskViewerView.vue b/frontend/src/views/TaskViewerView.vue index 754152d..5eebf91 100644 --- a/frontend/src/views/TaskViewerView.vue +++ b/frontend/src/views/TaskViewerView.vue @@ -480,23 +480,23 @@ const subTaskProgress = computed(() => { padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; font-weight: 600; - box-shadow: 0 2px 8px rgba(124, 58, 237, 0.28); + box-shadow: var(--glow-cta); transition: box-shadow 0.15s, opacity 0.15s; } .btn-edit:hover { - box-shadow: 0 4px 14px rgba(124, 58, 237, 0.42); + box-shadow: var(--glow-cta-hover); opacity: 0.95; color: #fff; } .btn-advance { padding: 0.45rem 1rem; - background: linear-gradient(135deg, #7c3aed, #5b21b6); + background: var(--gradient-cta); color: #fff; border: none; border-radius: var(--radius-sm); From 16d9af8b96f498aee1d2f2afc1a166c3053a4983 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 8 Apr 2026 12:22:03 -0400 Subject: [PATCH 03/11] fix(theme): increase dark mode contrast for borders, glows, and card edges; DRY color values into CSS variables --- frontend/src/assets/theme.css | 19 +++++++++++++++++-- frontend/src/components/AppHeader.vue | 6 +++--- frontend/src/views/KnowledgeView.vue | 14 +++++++------- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/frontend/src/assets/theme.css b/frontend/src/assets/theme.css index cae746b..7b19fff 100644 --- a/frontend/src/assets/theme.css +++ b/frontend/src/assets/theme.css @@ -58,6 +58,14 @@ --color-accent-warm-light: #d4a017; --color-primary-solid: #7c3aed; --color-primary-deep: #5b21b6; + /* Reusable brand patterns */ + --gradient-cta: linear-gradient(135deg, var(--color-primary-solid), var(--color-primary-deep)); + --glow-cta: 0 2px 10px rgba(124, 58, 237, 0.35); + --glow-cta-hover: 0 4px 20px rgba(124, 58, 237, 0.55); + --glow-soft: 0 0 16px rgba(124, 58, 237, 0.35); + --color-primary-faint: rgba(124, 58, 237, 0.08); + --color-primary-tint: rgba(124, 58, 237, 0.12); + --color-primary-wash: rgba(124, 58, 237, 0.20); } [data-theme="dark"] { @@ -68,8 +76,8 @@ --color-text: #e4e4f0; --color-text-secondary: #8888a8; --color-text-muted: #52526a; - --color-border: rgba(124, 58, 237, 0.12); - --color-input-border: rgba(124, 58, 237, 0.22); + --color-border: rgba(124, 58, 237, 0.22); + --color-input-border: rgba(124, 58, 237, 0.35); --color-primary: #a78bfa; --color-danger: #f44336; --color-tag-bg: #2a2a45; @@ -109,6 +117,13 @@ --color-accent-warm-light: #e8c45a; --color-primary-solid: #7c3aed; --color-primary-deep: #5b21b6; + --gradient-cta: linear-gradient(135deg, var(--color-primary-solid), var(--color-primary-deep)); + --glow-cta: 0 2px 12px rgba(124, 58, 237, 0.45); + --glow-cta-hover: 0 4px 24px rgba(124, 58, 237, 0.65); + --glow-soft: 0 0 18px rgba(124, 58, 237, 0.4); + --color-primary-faint: rgba(124, 58, 237, 0.10); + --color-primary-tint: rgba(124, 58, 237, 0.14); + --color-primary-wash: rgba(124, 58, 237, 0.22); } *, diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index 3c285e6..9f52698 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -153,7 +153,7 @@ router.afterEach(() => {