Files
pad/web/src/app.css
T
xarmian 1d26c2b542 feat: add workspace top bar with drag-to-reorder (#80)
* feat: add workspace top bar with drag-to-reorder

Replace the sidebar WorkspaceSwitcher dropdown with a dedicated top bar
that provides fast workspace switching and a user menu.

Desktop:
- Horizontal bar above sidebar + content with workspace icons (colored
  first-letter circles) and names as real <a> links
- Drag-and-drop reorder via svelte-dnd-action
- User avatar on right with dropdown (settings, theme toggle, sign out)
- "+" button to create new workspaces

Mobile:
- Full-width fixed bar at top when sidebar opens (above sidebar/backdrop)
- Tap workspace to navigate and close sidebar
- Reorder button opens full-screen vertical list with drag handles
- Sidebar starts below the top bar with adjusted positioning

Backend:
- Migration 028: add sort_order to workspace_members (per-user ordering)
- GET /workspaces now returns workspaces in user's sort order
- PUT /workspaces/reorder endpoint for persisting order

Sidebar simplified:
- Removed WorkspaceSwitcher component, user section, theme toggle
- Theme initialization moved to root layout
- Cleaner footer with search, settings, and notification bell

Implements IDEA-129, relates to IDEA-126.

* fix: address codex review findings (P1+P2)

- Remove unsupported `direction` option from svelte-dnd-action dndzone
- Add Postgres migration 008 for workspace_members.sort_order
- Handle sql.ErrNoRows gracefully in reorder endpoint for admins who
  aren't members of all workspaces
- Restore mobile sign-out: add user name + logout button to sidebar
  footer on mobile (was only in desktop TopBar user menu)
2026-04-10 01:22:49 -04:00

278 lines
5.8 KiB
CSS

:root {
--bg-primary: #1a1a1a;
--bg-secondary: #242424;
--bg-tertiary: #2e2e2e;
--bg-hover: #353535;
--bg-active: #3a3a3a;
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-muted: #666666;
--border: #333333;
--border-subtle: #2a2a2a;
--accent-blue: #4a9eff;
--accent-green: #4ade80;
--accent-purple: #a78bfa;
--accent-amber: #fbbf24;
--accent-cyan: #22d3ee;
--accent-orange: #fb923c;
--accent-gray: #9ca3af;
--accent-brown: #a8896c;
--status-draft: #666666;
--status-active: #4ade80;
--status-completed: #4a9eff;
--status-archived: #444444;
--topbar-height: 44px;
--sidebar-width: 260px;
--detail-panel-width: 300px;
--content-max-width: 960px;
--font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-content: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--radius: 6px;
--radius-sm: 4px;
--radius-lg: 8px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: var(--font-ui);
font-size: 14px;
line-height: 1.5;
color: var(--text-primary);
background: var(--bg-primary);
-webkit-font-smoothing: antialiased;
}
a {
color: var(--accent-blue);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
font-family: inherit;
font-size: inherit;
cursor: pointer;
border: none;
background: none;
color: inherit;
}
input, textarea, select {
font-family: inherit;
font-size: inherit;
color: var(--text-primary);
background: var(--bg-tertiary);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: var(--space-2) var(--space-3);
outline: none;
}
input:focus, textarea:focus {
border-color: var(--accent-blue);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
}
/* Prose styles for rendered markdown */
.prose {
font-family: var(--font-content);
font-size: 15px;
line-height: 1.7;
color: var(--text-primary);
max-width: var(--content-max-width);
}
.prose h1 { font-size: 1.8em; margin: 1.2em 0 0.6em; font-weight: 700; }
.prose h2 { font-size: 1.4em; margin: 1em 0 0.5em; font-weight: 600; }
.prose h3 { font-size: 1.15em; margin: 0.8em 0 0.4em; font-weight: 600; }
.prose p { margin: 0.6em 0; }
.prose ul, .prose ol { margin: 0.6em 0; padding-left: 1.5em; }
.prose li { margin: 0.2em 0; }
.prose li input[type="checkbox"] { margin-right: 0.4em; }
.prose pre {
background: var(--bg-tertiary);
padding: var(--space-4);
border-radius: var(--radius);
overflow-x: auto;
margin: 0.8em 0;
font-family: var(--font-mono);
font-size: 0.9em;
line-height: 1.5;
}
.prose code {
background: var(--bg-tertiary);
padding: 0.15em 0.4em;
border-radius: var(--radius-sm);
font-family: var(--font-mono);
font-size: 0.9em;
}
.prose pre code {
background: none;
padding: 0;
}
.prose blockquote {
border-left: 3px solid var(--border);
padding-left: var(--space-4);
margin: 0.8em 0;
color: var(--text-secondary);
}
.prose table {
border-collapse: collapse;
width: 100%;
margin: 0.8em 0;
}
.prose th, .prose td {
border: 1px solid var(--border);
padding: var(--space-2) var(--space-3);
text-align: left;
}
.prose th {
background: var(--bg-secondary);
font-weight: 600;
}
.prose hr {
border: none;
border-top: 1px solid var(--border);
margin: 1.5em 0;
}
.prose img {
max-width: 100%;
border-radius: var(--radius);
}
.prose a.doc-link {
background: var(--bg-tertiary);
padding: 0.1em 0.5em;
border-radius: var(--radius-sm);
color: var(--accent-blue);
font-weight: 500;
}
.prose a.doc-link:hover {
background: var(--bg-hover);
text-decoration: none;
}
.prose .doc-link.broken {
color: var(--accent-orange);
opacity: 0.7;
text-decoration: line-through;
}
/* External links in rendered markdown */
.prose .external-link {
color: var(--accent-blue);
text-decoration: none;
}
.prose .external-link:hover {
text-decoration: underline;
}
.prose .external-icon {
font-size: 0.75em;
opacity: 0.6;
vertical-align: super;
}
/* Editor link styles — autolinked URLs and wiki-links */
.prose a.editor-link {
color: var(--accent-blue);
text-decoration: none;
border-bottom: 1px solid color-mix(in srgb, var(--accent-blue) 30%, transparent);
transition: border-color 0.15s;
}
.prose a.editor-link:hover {
border-bottom-color: var(--accent-blue);
}
/* Light mode */
[data-theme="light"] {
--bg-primary: #ffffff;
--bg-secondary: #f7f7f8;
--bg-tertiary: #eeeff1;
--bg-hover: #e5e6e8;
--bg-active: #dddee0;
--text-primary: #1a1a1a;
--text-secondary: #555555;
--text-muted: #999999;
--border: #e0e0e0;
--border-subtle: #eaeaea;
--accent-blue: #2563eb;
--accent-green: #16a34a;
--accent-purple: #7c3aed;
--accent-amber: #d97706;
--accent-cyan: #0891b2;
--accent-orange: #ea580c;
--accent-gray: #6b7280;
--accent-brown: #92400e;
--status-draft: #999999;
--status-active: #16a34a;
--status-completed: #2563eb;
--status-archived: #bbbbbb;
}
@media (prefers-color-scheme: light) {
:root:not([data-theme="dark"]) {
--bg-primary: #ffffff;
--bg-secondary: #f7f7f8;
--bg-tertiary: #eeeff1;
--bg-hover: #e5e6e8;
--bg-active: #dddee0;
--text-primary: #1a1a1a;
--text-secondary: #555555;
--text-muted: #999999;
--border: #e0e0e0;
--border-subtle: #eaeaea;
--accent-blue: #2563eb;
--accent-green: #16a34a;
--accent-purple: #7c3aed;
--accent-amber: #d97706;
--accent-cyan: #0891b2;
--accent-orange: #ea580c;
--accent-gray: #6b7280;
--accent-brown: #92400e;
--status-draft: #999999;
--status-active: #16a34a;
--status-completed: #2563eb;
--status-archived: #bbbbbb;
}
}
/* Responsive */
@media (max-width: 768px) {
:root {
--sidebar-width: 280px;
}
}