From efb3534f3adcac23fc949fca6da8fca69cd65d38 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 27 Apr 2026 23:02:59 -0400 Subject: [PATCH] =?UTF-8?q?feat(design):=20surface=20phase=20PR=204=20?= =?UTF-8?q?=E2=80=94=20Knowledge=20cluster=20polish?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the surface-phase spec for the Notes/Tasks viewers and editors: Long-form line-height - prose.css: bumped global .prose from 1.6 to 1.7. Applies to Note viewer body, Task viewer body, anywhere markdown renders into a reading surface. Chat assistant bubble already had the explicit override; now consistent with the rest. Button reclassification per Hybrid rule - Shared editor-shared.css: - btn-save: accent gradient → Moss (action-primary). Saving is "operating the software", not a brand moment. - btn-delete: --color-danger (Error terracotta) → Oxblood (action-destructive). Layout updated for inline-flex so the Trash2 icon at call sites lines up alongside the label. - NoteEditorView, TaskEditorView: Delete buttons now contain a Trash2 icon per Hybrid's "destructive paired with icon" rule. - NoteViewerView, TaskViewerView: - btn-edit (and TaskViewer's btn-advance): accent gradient → Moss. Switching to edit / advancing status are workflow actions. - btn-convert, btn-share: ghost-on-hover-to-accent → Bronze action-secondary (alternate paths). Two-weights-only - Snapped every font-weight: 600/700 to 500 across editor-shared.css and the five Knowledge-cluster views. Out of scope for this PR (deliberate punt) - Smaller utility buttons (.btn-suggest-tags, .btn-link-all, .btn-add-subtask, the AI-assist generate/proofread/accept/reject set, etc.) — currently ghost-styled, generally compliant. Will revisit only if they read off in practice. - Filter chip / sub-task list-row border audit — deferred since current styles already lean on background tint for affordance. Co-Authored-By: Claude Opus 4.7 --- frontend/src/assets/editor-shared.css | 36 ++++++++------ frontend/src/assets/prose.css | 5 +- frontend/src/views/KnowledgeView.vue | 14 +++--- frontend/src/views/NoteEditorView.vue | 7 ++- frontend/src/views/NoteViewerView.vue | 42 ++++++++--------- frontend/src/views/TaskEditorView.vue | 9 ++-- frontend/src/views/TaskViewerView.vue | 68 +++++++++++---------------- 7 files changed, 91 insertions(+), 90 deletions(-) diff --git a/frontend/src/assets/editor-shared.css b/frontend/src/assets/editor-shared.css index b3cdc85..cb6d480 100644 --- a/frontend/src/assets/editor-shared.css +++ b/frontend/src/assets/editor-shared.css @@ -50,34 +50,42 @@ border-color: var(--color-primary); color: var(--color-primary); } +/* Save: Moss action-primary per the Hybrid rule. Saving is "operating + the software" — not a brand moment. Accent gradient is reserved for + Send / empty-state CTAs. */ .btn-save { padding: 0.45rem 1.1rem; - background: linear-gradient(135deg, #5B4A8A, #3F3560); + background: var(--color-action-primary); color: #fff; border: none; border-radius: var(--radius-sm); cursor: pointer; - font-weight: 600; + font-weight: 500; font-size: 0.875rem; - box-shadow: 0 2px 8px rgba(91, 74, 138, 0.28); - transition: box-shadow 0.15s, opacity 0.15s; + transition: background 0.15s, opacity 0.15s; } .btn-save:hover:not(:disabled) { - box-shadow: 0 4px 14px rgba(91, 74, 138, 0.42); - opacity: 0.95; + background: var(--color-action-primary-hover); } .btn-save:disabled { opacity: 0.55; cursor: default; } +/* Delete: Oxblood action-destructive per Hybrid rule. Should be paired + with a Trash icon at the call site to reinforce intent. */ .btn-delete { padding: 0.45rem 1rem; - background: var(--color-danger); + background: var(--color-action-destructive); color: #fff; border: none; border-radius: var(--radius-sm); cursor: pointer; + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-weight: 500; } +.btn-delete:hover { background: var(--color-action-destructive-hover); } .btn-assist-toggle { margin-left: auto; padding: 0.4rem 0.9rem; @@ -99,7 +107,7 @@ border-bottom: 1.5px solid var(--color-border); border-radius: 0; font-size: 1.5rem; - font-weight: 700; + font-weight: 500; font-family: "Fraunces", Georgia, serif; background: transparent; color: var(--color-text); @@ -225,7 +233,7 @@ .assist-panel-title { flex: 1; font-size: 0.8rem; - font-weight: 700; + font-weight: 500; color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.05em; @@ -269,7 +277,7 @@ /* Section list */ .assist-sections-label { font-size: 0.72rem; - font-weight: 700; + font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); @@ -411,7 +419,7 @@ align-items: center; justify-content: space-between; font-size: 0.8rem; - font-weight: 600; + font-weight: 500; color: var(--color-text-secondary); } .btn-toggle-view { @@ -454,7 +462,7 @@ width: 1rem; text-align: center; user-select: none; - font-weight: 700; + font-weight: 500; } .diff-text { flex: 1; @@ -562,7 +570,7 @@ border: none; border-bottom: 1px solid var(--color-border); font-size: 0.85rem; - font-weight: 600; + font-weight: 500; color: var(--color-text-secondary); cursor: pointer; text-align: left; @@ -581,7 +589,7 @@ } .sb-label { font-size: 0.78rem; - font-weight: 600; + font-weight: 500; color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.04em; diff --git a/frontend/src/assets/prose.css b/frontend/src/assets/prose.css index b2d5a2c..b3cf455 100644 --- a/frontend/src/assets/prose.css +++ b/frontend/src/assets/prose.css @@ -1,5 +1,8 @@ .prose { - line-height: 1.6; + /* Long-form reading line-height per the design system — + reading surfaces (notes, tasks, journal, assistant bubbles) want 1.7 + so prose breathes like a book, not a UI snippet. */ + line-height: 1.7; } .prose h1 { diff --git a/frontend/src/views/KnowledgeView.vue b/frontend/src/views/KnowledgeView.vue index e80b881..1f7eae0 100644 --- a/frontend/src/views/KnowledgeView.vue +++ b/frontend/src/views/KnowledgeView.vue @@ -817,7 +817,7 @@ onUnmounted(() => { color: #fff; cursor: pointer; font-size: 0.85rem; - font-weight: 600; + font-weight: 500; transition: box-shadow 0.15s; } .btn-new-note:hover { box-shadow: var(--glow-cta-hover); } @@ -1067,7 +1067,7 @@ onUnmounted(() => { font-size: 0.68rem; padding: 2px 7px; border-radius: 10px; - font-weight: 600; + font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; } @@ -1079,7 +1079,7 @@ onUnmounted(() => { .k-card-body { flex: 1; padding-right: 40px; } .k-card-title { - font-weight: 600; + font-weight: 500; font-size: 0.92rem; margin-bottom: 5px; line-height: 1.3; @@ -1193,7 +1193,7 @@ onUnmounted(() => { font-size: 0.7rem; padding: 1px 7px; border-radius: 8px; - font-weight: 600; + font-weight: 500; } .status--todo { background: var(--color-status-todo-bg); color: var(--color-status-todo); } .status--in_progress { background: var(--color-status-in-progress-bg); color: var(--color-status-in-progress); } @@ -1204,7 +1204,7 @@ onUnmounted(() => { font-size: 0.7rem; padding: 1px 7px; border-radius: 8px; - font-weight: 600; + font-weight: 500; } .priority--low { background: var(--color-priority-low-bg); color: var(--color-priority-low); } .priority--normal { background: var(--color-priority-medium-bg); color: var(--color-priority-medium); } @@ -1272,7 +1272,7 @@ onUnmounted(() => { justify-content: space-between; padding: 10px 14px; font-size: 0.85rem; - font-weight: 600; + font-weight: 500; border-bottom: 1px solid var(--color-border, rgba(255,255,255,0.06)); flex-shrink: 0; } @@ -1335,7 +1335,7 @@ onUnmounted(() => { } .minichat-title { font-size: 0.82rem; - font-weight: 600; + font-weight: 500; color: var(--color-muted); text-transform: uppercase; letter-spacing: 0.06em; diff --git a/frontend/src/views/NoteEditorView.vue b/frontend/src/views/NoteEditorView.vue index ba2d9eb..e5f2d6f 100644 --- a/frontend/src/views/NoteEditorView.vue +++ b/frontend/src/views/NoteEditorView.vue @@ -21,6 +21,7 @@ import MilestoneSelector from "@/components/MilestoneSelector.vue"; import DiffView from "@/components/DiffView.vue"; import VersionHistorySection from "@/components/VersionHistorySection.vue"; import ConfirmDialog from "@/components/ConfirmDialog.vue"; +import { Trash2 } from "lucide-vue-next"; const route = useRoute(); const router = useRouter(); @@ -449,7 +450,9 @@ onUnmounted(() => assist.clearSelection()); - + assist.clearSelection()); .assist-section-title { font-size: 0.78rem; - font-weight: 700; + font-weight: 500; color: var(--color-text-secondary); text-transform: uppercase; letter-spacing: 0.05em; diff --git a/frontend/src/views/NoteViewerView.vue b/frontend/src/views/NoteViewerView.vue index 847606f..ab9e47f 100644 --- a/frontend/src/views/NoteViewerView.vue +++ b/frontend/src/views/NoteViewerView.vue @@ -345,41 +345,39 @@ async function convertToTask() { border-color: var(--color-primary); color: var(--color-primary); } +/* Edit: Moss action-primary — switching from view to edit is operating + the software, not a brand moment. */ .btn-edit { display: inline-flex; align-items: center; padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: var(--gradient-cta); + background: var(--color-action-primary); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; - font-weight: 600; - box-shadow: var(--glow-cta); - transition: box-shadow 0.15s, opacity 0.15s; + font-weight: 500; + transition: background 0.15s; } .btn-edit:hover { - box-shadow: var(--glow-cta-hover); - opacity: 0.95; + background: var(--color-action-primary-hover); color: #fff; } +/* Convert + Share: Bronze action-secondary — alternate paths */ .btn-convert { margin-left: auto; padding: 0.3rem 0.75rem; - background: var(--color-bg-secondary); - color: var(--color-text); - border: 1px solid var(--color-border); + background: var(--color-action-secondary); + color: #fff; + border: none; border-radius: var(--radius-sm); cursor: pointer; font-size: 0.85rem; + transition: background 0.15s; } -.btn-convert:hover { - background: var(--color-primary); - color: #fff; - border-color: var(--color-primary); -} +.btn-convert:hover { background: var(--color-action-secondary-hover); } .btn-convert:disabled { opacity: 0.6; cursor: default; @@ -387,21 +385,21 @@ async function convertToTask() { .btn-share { padding: 0.3rem 0.75rem; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); + background: var(--color-action-secondary); + border: none; border-radius: var(--radius-sm); - color: var(--color-text-secondary); + color: #fff; cursor: pointer; font-size: 0.85rem; font-family: inherit; - transition: border-color 0.15s, color 0.15s; + transition: background 0.15s; } -.btn-share:hover { border-color: var(--color-primary); color: var(--color-primary); } +.btn-share:hover { background: var(--color-action-secondary-hover); } .note-title { font-family: "Fraunces", Georgia, serif; font-size: 2rem; - font-weight: 700; + font-weight: 500; line-height: 1.2; margin: 0.25rem 0 0.5rem; color: var(--color-text); @@ -440,7 +438,7 @@ async function convertToTask() { align-items: center; gap: 0.4rem; font-size: 0.78rem; - font-weight: 700; + font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-muted); @@ -482,7 +480,7 @@ async function convertToTask() { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; - font-weight: 600; + font-weight: 500; padding: 0.1rem 0.45rem; border-radius: 999px; flex-shrink: 0; diff --git a/frontend/src/views/TaskEditorView.vue b/frontend/src/views/TaskEditorView.vue index 68b3b8a..ae283f7 100644 --- a/frontend/src/views/TaskEditorView.vue +++ b/frontend/src/views/TaskEditorView.vue @@ -25,6 +25,7 @@ import DiffView from "@/components/DiffView.vue"; import ConfirmDialog from "@/components/ConfirmDialog.vue"; import VersionHistorySection from "@/components/VersionHistorySection.vue"; import RecurrenceEditor from "@/components/RecurrenceEditor.vue"; +import { Trash2 } from "lucide-vue-next"; const route = useRoute(); const router = useRouter(); @@ -414,7 +415,9 @@ useEditorGuards(dirty, save); - + { border-color: var(--color-primary); color: var(--color-primary); } -.btn-edit { +/* Edit + Advance: Moss action-primary — both are "operating the software" + workflow actions, not brand moments. */ +.btn-edit, +.btn-advance { display: inline-flex; align-items: center; padding: 0.45rem 1.1rem; border: none; border-radius: var(--radius-sm); - background: var(--gradient-cta); + background: var(--color-action-primary); color: #fff; text-decoration: none; cursor: pointer; font-size: 0.875rem; - font-weight: 600; - box-shadow: var(--glow-cta); - transition: box-shadow 0.15s, opacity 0.15s; + font-weight: 500; + transition: background 0.15s; } -.btn-edit:hover { - box-shadow: var(--glow-cta-hover); - opacity: 0.95; +.btn-edit:hover, +.btn-advance:hover { + background: var(--color-action-primary-hover); color: #fff; } -.btn-advance { - padding: 0.45rem 1rem; - background: var(--gradient-cta); +/* Convert + Share: Bronze action-secondary — alternate paths */ +.btn-convert { + margin-left: auto; + padding: 0.3rem 0.75rem; + background: var(--color-action-secondary); color: #fff; border: none; border-radius: var(--radius-sm); cursor: pointer; - font-size: 0.9rem; - font-weight: 600; - transition: opacity 0.15s; -} -.btn-advance:hover { - opacity: 0.88; -} -.btn-convert { - margin-left: auto; - padding: 0.3rem 0.75rem; - background: var(--color-bg-secondary); - color: var(--color-text); - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - cursor: pointer; font-size: 0.85rem; + transition: background 0.15s; } -.btn-convert:hover { - background: var(--color-primary); - color: #fff; - border-color: var(--color-primary); -} +.btn-convert:hover { background: var(--color-action-secondary-hover); } .btn-convert:disabled { opacity: 0.6; cursor: default; @@ -531,21 +517,21 @@ const subTaskProgress = computed(() => { .btn-share { padding: 0.3rem 0.75rem; - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); + background: var(--color-action-secondary); + border: none; border-radius: var(--radius-sm); - color: var(--color-text-secondary); + color: #fff; cursor: pointer; font-size: 0.85rem; font-family: inherit; - transition: border-color 0.15s, color 0.15s; + transition: background 0.15s; } -.btn-share:hover { border-color: var(--color-primary); color: var(--color-primary); } +.btn-share:hover { background: var(--color-action-secondary-hover); } .task-title { font-family: "Fraunces", Georgia, serif; font-size: 2rem; - font-weight: 700; + font-weight: 500; line-height: 1.2; margin: 0.25rem 0 0.5rem; color: var(--color-text); @@ -580,7 +566,7 @@ const subTaskProgress = computed(() => { } .due-date.overdue { color: var(--color-overdue); - font-weight: 600; + font-weight: 500; } .task-meta-row { display: flex; @@ -618,7 +604,7 @@ const subTaskProgress = computed(() => { .subtasks-title { font-size: 1rem; margin: 0; - font-weight: 600; + font-weight: 500; } .subtasks-progress { font-size: 0.8rem; @@ -718,7 +704,7 @@ const subTaskProgress = computed(() => { align-items: center; gap: 0.4rem; font-size: 0.78rem; - font-weight: 700; + font-weight: 500; text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-muted); @@ -760,7 +746,7 @@ const subTaskProgress = computed(() => { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; - font-weight: 600; + font-weight: 500; padding: 0.1rem 0.45rem; border-radius: 999px; flex-shrink: 0;