/* Mobile formatting toolbar - appears below editor on narrow viewports */ /* Toolbar height: 32px buttons + 1px border-top = 33px. * Used to bound the action-row shadows; layout reserves the measured height. */ :root { --formatting-toolbar-height: 33px; } .formatting-toolbar { position: fixed; isolation: isolate; left: var(--ribbon-width); right: 0; bottom: 0; background-color: var(--bg-secondary); border-top: 1px solid var(--border-color); overflow: hidden; z-index: 99; } .formatting-toolbar__scroller { display: flex; align-items: center; gap: 1px; /* No vertical padding: 32px buttons fill the toolbar height edge-to-edge * so hover/active fills are flush with the bar. Horizontal padding only. */ padding-left: 0.5rem; padding-right: 0.5rem; overflow-x: auto; -webkit-overflow-scrolling: touch; } .formatting-toolbar__scroll-shadow { position: absolute; top: 0; /* Bound the shadow to the 32px action row. */ height: calc(var(--formatting-toolbar-height) - 1px); width: 28px; pointer-events: none; opacity: 0; transition: opacity 0.15s ease-out; z-index: 1; } .formatting-toolbar__scroll-shadow--left { left: 0; background: linear-gradient(to right, rgba(0, 0, 0, 0.42), transparent); } .formatting-toolbar__scroll-shadow--right { right: 0; background: linear-gradient(to left, rgba(0, 0, 0, 0.42), transparent); } .formatting-toolbar[data-scroll-shadow-left="true"] .formatting-toolbar__scroll-shadow--left { opacity: 1; } .formatting-toolbar[data-scroll-shadow-right="true"] .formatting-toolbar__scroll-shadow--right { opacity: 1; } @media (max-width: 767px) { /* Follow the ribbon's width animation when the sidebar mode changes. */ .formatting-toolbar { transition: left 0.15s ease-out; } } /* Mobile is always enabled. Respect the user's desktop visibility setting. */ @media (min-width: 768px) { .formatting-toolbar:not([data-show-desktop="true"]) { display: none; } /* Desktop with toolbar visible: * * 1. Offset left edge to avoid overlapping the ribbon and sidebar. * --toolbar-left-offset is computed in AppShell.tsx and set as an * inline CSS variable on .app-shell: * - 0px when ribbon is hidden * - var(--ribbon-width) when only ribbon is visible * - var(--ribbon-width) + sidebarWidth when both are visible * * 2. Offset right edge to avoid overlapping the right panel. * --toolbar-right-offset is set to rightPanelWidth when expanded, * 0px when collapsed. * * 3. Keep the toolbar pinned to the viewport bottom like mobile, but * constrain it horizontally to the editor lane so it clears the * ribbon/sidebar/right-panel chrome. * * The `transition` matches the 0.15s ease-out used by .app-shell-sidebar * and .app-shell-right-panel (AppShell.css). Without this, the toolbar * snaps to its final position instantly while the panels animate, briefly * re-introducing the original overlap bug for ~150ms. * * The transition is suppressed during sidebar/right-panel resize drags * (when .app-shell has data-resizing="true") so the toolbar tracks the * cursor in real time instead of lagging 150ms behind. * * `prefers-reduced-motion` is honored by the rule below. * * Bug fix: #213 — formatting toolbar overlaps settings icon on desktop. */ .formatting-toolbar[data-show-desktop="true"] { left: var(--toolbar-left-offset, 0px); right: var(--toolbar-right-offset, 0px); width: auto; bottom: 0; transition: left 0.15s ease-out, right 0.15s ease-out; } /* Suppress transition during resize drag — the toolbar should track * the sidebar edge in real time, not lag behind it. ResizeHandle.tsx * sets data-resizing="true" on the active handle during pointermove. */ .app-shell:has(.resize-handle[data-resizing="true"]) .formatting-toolbar[data-show-desktop="true"] { transition: none; } } /* Honor reduced-motion preference. Applies in any viewport (desktop or * mobile) — though the desktop rule is where it matters most. */ @media (prefers-reduced-motion: reduce) { .formatting-toolbar, .formatting-toolbar[data-show-desktop="true"] { transition: none; } } /* Hide when not focused */ .formatting-toolbar[data-visible="false"] { display: none; } /* Hide in Read mode — editing is disabled, no formatting actions available */ .formatting-toolbar[data-mode="read"] { display: none; } /* Hide when there are no visible actions (e.g., no file open, or all actions hidden) */ .formatting-toolbar[data-empty="true"] { display: none; } .formatting-toolbar__button { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; background: none; border: 1px solid transparent; /* Square — matches the rectilinear chrome. */ border-radius: 0; color: var(--text-secondary); cursor: pointer; flex-shrink: 0; transition: background-color 0.15s, color 0.15s, border-color 0.15s; -webkit-tap-highlight-color: transparent; } .formatting-toolbar__button:hover { background-color: var(--bg-tertiary); color: var(--text-primary); } .formatting-toolbar__button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } .formatting-toolbar__button:active { background-color: var(--bg-active); border-color: var(--border-color); } .formatting-toolbar__button svg { width: 16px; height: 16px; } .formatting-toolbar__divider { width: 1px; height: 16px; background-color: var(--border-color); margin: 0 2px; flex-shrink: 0; }