# 3.1 — Tab Bar & Document Lifecycle **Next Step:** → [Sidebar](../../04-file-organization/4.1-sidebar/4.1-sidebar.md) --- ## Page Metadata | Property | Value | |----------|-------| | **Scenario** | 03 — Document Management | | **Page Number** | 3.1 | | **Platform** | Web (PWA), portable to macOS/Windows | | **Page Type** | Embedded Strip (within App Shell, between header and editor) | --- ## Overview **Page Purpose:** Enable multi-document workflow. Users can open, create, close, reorder, and rename documents via tabs. The tab bar is the primary navigation between open documents. **Success Criteria:** - Switching tabs feels instant (< 50ms) - Users never lose work (auto-save before switch) - Tab state survives page reload - 15+ tabs remain usable (horizontal scroll) --- ## Layout Structure ``` ┌──────────────────────────────────────────────────────────┐ │ [● Budget ×] [ Invoice ×] [ Unit Conv ×] [+] ··· │ 36px └──────────────────────────────────────────────────────────┘ ● = unsaved changes dot Active tab: connected to editor (no bottom border) [+] = new document button ··· = overflow gradient when scrollable ``` --- ## Spacing | Property | Token | Pixels | |----------|-------|--------| | Tab bar height | — | 36px | | Tab padding horizontal | space-sm | 8px | | Tab gap | — | 0px (tabs are flush, separated by 1px border) | | Tab min width | — | 100px | | Tab max width | — | 200px | | Close button size | — | 16px × 16px | | Close button margin-left | space-xs | 6px | | Modified dot size | — | 6px | | Modified dot margin-right | space-2xs | 4px | | New tab button width | — | 36px (square) | --- ## Typography | Element | Size | Weight | Color | |---------|------|--------|-------| | Tab label (active) | text-xs | 500 | var(--text-h) | | Tab label (inactive) | text-xs | 400 | var(--text) | | Close × | text-xs | 400 | var(--text) at 50%, hover → var(--text) | | New + icon | text-sm | 300 | var(--text), hover → var(--accent) | --- ## Tab States ### Active Tab | Property | Value | |----------|-------| | Background | var(--bg) — same as editor, creates visual connection | | Border bottom | None — tab "opens into" the editor | | Border left/right | 1px solid var(--border) | | Border top | 2px solid var(--accent) — active indicator | | Label | Weight 500, var(--text-h) | | Close button | Always visible | ### Inactive Tab | Property | Value | |----------|-------| | Background | var(--bg-secondary) | | Border bottom | 1px solid var(--border) — closed off from editor | | Border left/right | 1px solid var(--border) | | Border top | 2px solid transparent | | Label | Weight 400, var(--text) | | Close button | Visible on hover only | ### Hover (Inactive) | Property | Value | |----------|-------| | Background | Blend between bg-secondary and bg (subtle lighten) | | Transition | background 0.1s | ### Dragging | Property | Value | |----------|-------| | Appearance | Tab lifts with subtle shadow (0 2px 8px rgba(0,0,0,0.15)) | | Opacity | 90% | | Placeholder | 2px var(--accent) vertical line at insertion point | | Cursor | grabbing | --- ## Interactions | Action | Behavior | |--------|----------| | **Click tab** | Switch to document. Auto-save current. Restore editor state (content, cursor, scroll, undo). | | **Click +** | Create "Untitled" doc, open in new tab, focus editor. | | **Click ×** | Close tab. If unsaved and modified, no prompt (auto-saved). Remove from openTabIds. | | **Middle-click tab** | Close tab (same as ×). | | **Double-click tab** | Inline rename — label becomes input field, Enter confirms, Esc cancels. | | **Drag tab** | Reorder. Drop position shown by accent line indicator. | | **Ctrl/Cmd+Tab** | Next tab (wraps). | | **Ctrl/Cmd+Shift+Tab** | Previous tab (wraps). | | **Ctrl/Cmd+W** | Close active tab. If last tab, create new "Untitled". | | **Ctrl/Cmd+N** | New document + tab. | | **Ctrl/Cmd+1–9** | Jump to tab by position. | | **Mouse wheel on tab bar** | Horizontal scroll when tabs overflow. | --- ## Tab Overflow | Property | Value | |----------|-------| | Trigger | Total tab width > container width | | Scroll | Horizontal, smooth, via mouse wheel or trackpad | | Indicators | 16px fade gradient on left/right edges when scrollable | | Active tab | Auto-scrolls into view when selected via keyboard | | New tab button | Sticky right — always visible outside scroll area | --- ## Document Lifecycle ### Create | Trigger | Behavior | |---------|----------| | Click [+] | New doc: `{ id: uuid(), title: "Untitled", content: "", folderId: null }` | | Sidebar template click | New doc with template content and suggested title | | Ctrl/Cmd+N | Same as [+] | Title auto-increments: "Untitled", "Untitled 2", "Untitled 3"... ### Rename | Trigger | Behavior | |---------|----------| | Double-click tab | Label becomes ``, pre-selected text, 200px max width | | Sidebar right-click → Rename | Same inline editing in sidebar | | Enter | Confirm rename, update document and sidebar | | Esc | Cancel, revert to previous name | | Blur | Confirm (same as Enter) | | Empty name | Revert to "Untitled" | ### Delete | Trigger | Behavior | |---------|----------| | Sidebar right-click → Delete | Confirmation: "Delete '{title}'? This cannot be undone." | | Confirm | Remove from documents[], close tab if open, remove from folder | | Cancel | No action | | Undo | 5-second toast: "Document deleted. [Undo]" — restores document if clicked | ### Duplicate | Trigger | Behavior | |---------|----------| | Sidebar right-click → Duplicate | New doc: same content, title = "{original} (copy)", same folder | | Opens in new tab automatically | ### Auto-Save | Property | Value | |----------|-------| | Trigger | Document content change | | Debounce | 500ms after last keystroke | | Storage | localStorage (calctext-documents) | | Indicator | Modified dot (●) appears immediately on change, disappears on save | | Manual save | Ctrl/Cmd+S shows brief checkmark animation in tab (visual confirmation) | --- ## Modified Indicator | Property | Value | |----------|-------| | Shape | Filled circle, 6px | | Color | var(--text) at 60% | | Position | Before tab label, 4px gap | | Appears | On first character change | | Disappears | On auto-save completion (500ms debounce) | | Animation | Fade in 0.2s | --- ## Context Menu (Right-Click Tab) | Item | Action | |------|--------| | Close | Close this tab | | Close Others | Close all tabs except this one | | Close to the Right | Close all tabs to the right | | — | (separator) | | Rename | Inline rename | | Duplicate | Duplicate document | | — | (separator) | | Reveal in Sidebar | Scroll sidebar to show this file | --- ## Mobile Adaptations | Property | Value | |----------|-------| | Tab bar | Horizontal scroll, touch-friendly | | Tab height | 40px (larger touch target) | | Close button | Hidden — swipe left on tab to reveal close | | New tab | [+] button at far right | | Rename | Long-press → context menu → Rename | | Reorder | Long-press + drag | --- ## Page States | State | When | Behavior | |-------|------|----------| | **Single tab** | Only one document open | Tab still shown (establishes pattern). Close creates new "Untitled". | | **Many tabs (>10)** | Heavy usage | Horizontal scroll. Active tab auto-scrolls into view. | | **All tabs closed** | User closed everything | Auto-create "Untitled" tab (workspace never empty). | | **First launch** | No localStorage | Single "Welcome" tab with demo content. | --- ## Technical Notes - **Tab switching performance:** Store `EditorState` per tab in memory. On switch: destroy current EditorView, create new with stored state. Content swap < 20ms. - **Tab order persistence:** `openTabIds: string[]` in localStorage maintains order. - **Cross-platform:** Maps to native tab bars. macOS: NSTabView or custom tab strip. Windows: iced tabs widget. - **Max tabs:** Soft limit at 20 with performance hint in status bar. No hard limit. --- _Created using Whiteport Design Studio (WDS) methodology_