separate editor FAB and panel for cleaner animation
All checks were successful
deploy / deploy (push) Successful in 1m32s

Split the editor sheet into two distinct elements:
- .editor-fab: floating action button, always a pill in the corner
- .editor-panel: sliding panel that animates in/out independently

This enables proper CSS keyframe animations (slide-up/down on mobile,
slide-in/out on desktop) with a closing class for exit transitions.
Simplified the JS hook to only handle close behaviour.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-03-07 19:01:32 +00:00
parent 3f96769840
commit bd07c9c7d9
6 changed files with 230 additions and 249 deletions

View File

@@ -2378,94 +2378,19 @@
}
/* ══════════════════════════════════════════════════════════════════════════
Editor sheet (unified bottom/right sheet for page editing)
Editor FAB + Panel (page editing)
Two separate elements:
- .editor-fab: floating action button, always a pill in the corner
- .editor-panel: sliding panel, animates in/out
══════════════════════════════════════════════════════════════════════════ */
.editor-sheet {
/* ── Floating action button ── */
.editor-fab {
position: fixed;
z-index: 1000;
background: var(--t-surface-base);
box-shadow: var(--t-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
display: flex;
flex-direction: column;
}
@media (prefers-reduced-motion: reduce) {
.editor-sheet {
transition: none;
}
}
/* ── Mobile: bottom-anchored ── */
@media (max-width: 767px) {
/* Collapsed: floating pill button, no panel background */
.editor-sheet {
left: auto;
right: 16px;
bottom: 16px;
top: auto;
width: auto;
height: auto;
background: transparent;
box-shadow: none;
border: none;
border-radius: 0;
}
/* Open: full panel from bottom */
.editor-sheet[data-state="open"] {
left: 0;
right: 0;
bottom: 0;
top: 15dvh;
width: auto;
height: auto;
background: var(--t-surface-base);
box-shadow: var(--t-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
border-radius: var(--t-radius-lg, 12px) var(--t-radius-lg, 12px) 0 0;
border: 1px solid var(--t-border-default);
border-bottom: none;
}
}
/* ── Desktop: right-anchored ── */
/* ── Desktop: same floating button, side panel when open ── */
@media (min-width: 768px) {
/* Collapsed: floating pill button, no panel background (same as mobile) */
.editor-sheet {
left: auto;
right: 16px;
bottom: 16px;
top: auto;
width: auto;
height: auto;
background: transparent;
box-shadow: none;
border: none;
border-radius: 0;
}
/* Open: side panel from right */
.editor-sheet[data-state="open"] {
top: 0;
right: 0;
bottom: 0;
width: 420px;
max-width: 90vw;
background: var(--t-surface-base);
box-shadow: var(--t-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
border-radius: var(--t-radius-lg, 12px) 0 0 var(--t-radius-lg, 12px);
border: 1px solid var(--t-border-default);
border-right: none;
}
}
/* ── Edit button in collapsed state ── */
.editor-sheet-edit-btn {
right: 16px;
bottom: 16px;
display: flex;
align-items: center;
gap: 0.5rem;
@@ -2478,62 +2403,119 @@
font-weight: 500;
cursor: pointer;
transition: filter 0.15s, box-shadow 0.15s;
/* Soft glow for visibility over any background */
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.2),
0 0 0 3px rgba(255, 255, 255, 0.6);
}
.editor-sheet-edit-btn:hover {
.editor-fab:hover {
filter: brightness(1.1);
}
.editor-sheet-edit-btn svg {
.editor-fab svg {
width: 16px;
height: 16px;
flex-shrink: 0;
}
/* ── Dirty indicator ── */
.editor-sheet-dirty {
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--t-status-warning, oklch(0.75 0.18 85));
font-size: 0.75rem;
font-weight: 500;
}
.editor-sheet-dirty-dot {
.editor-fab-dirty {
width: 8px;
height: 8px;
border-radius: 9999px;
background: currentColor;
background: var(--t-status-warning, oklch(0.75 0.18 85));
flex-shrink: 0;
}
/* Desktop collapsed: hide "Unsaved" text, show only dot */
/* ── Editor panel ── */
.editor-panel {
position: fixed;
z-index: 1000;
display: flex;
flex-direction: column;
background: var(--t-surface-base);
box-shadow: var(--t-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.15));
}
/* Hidden when collapsed (but not during close animation) */
.editor-panel[data-state="collapsed"]:not(.closing) {
display: none;
}
/* ── Mobile: bottom sheet ── */
@media (max-width: 767px) {
.editor-panel[data-state="open"],
.editor-panel.closing {
left: 0;
right: 0;
bottom: 0;
top: 15dvh;
border-radius: var(--t-radius-lg, 12px) var(--t-radius-lg, 12px) 0 0;
border: 1px solid var(--t-border-default);
border-bottom: none;
}
.editor-panel[data-state="open"] {
animation: editor-panel-slide-up 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
}
.editor-panel.closing {
animation: editor-panel-slide-down 0.25s cubic-bezier(0.32, 0.72, 0, 1) both;
}
}
@keyframes editor-panel-slide-up {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes editor-panel-slide-down {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(24px); }
}
/* ── Desktop: side panel ── */
@media (min-width: 768px) {
.editor-sheet[data-state="collapsed"] .editor-sheet-dirty span:not(.editor-sheet-dirty-dot) {
/* Visually hidden but accessible to screen readers */
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
.editor-panel[data-state="open"],
.editor-panel.closing {
top: 0;
right: 0;
bottom: 0;
width: 420px;
max-width: 90vw;
border-radius: var(--t-radius-lg, 12px) 0 0 var(--t-radius-lg, 12px);
border: 1px solid var(--t-border-default);
border-right: none;
}
.editor-sheet[data-state="collapsed"] .editor-sheet-dirty {
position: relative;
.editor-panel[data-state="open"] {
animation: editor-panel-slide-in 0.3s cubic-bezier(0.32, 0.72, 0, 1) both;
}
.editor-panel.closing {
animation: editor-panel-slide-out 0.25s cubic-bezier(0.32, 0.72, 0, 1) both;
}
}
/* ── Sheet header (when expanded) ── */
.editor-sheet-header {
@keyframes editor-panel-slide-in {
from { opacity: 0; transform: translateX(24px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes editor-panel-slide-out {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(24px); }
}
/* Disable animations for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
.editor-panel[data-state="open"],
.editor-panel.closing {
animation: none;
}
}
/* ── Panel header ── */
.editor-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
@@ -2543,28 +2525,45 @@
gap: 0.5rem;
}
.editor-sheet-header-left {
.editor-panel-header-left {
display: flex;
align-items: center;
gap: 0.75rem;
min-width: 0;
}
.editor-sheet-title {
.editor-panel-title {
font-size: 0.875rem;
font-weight: 600;
color: var(--t-text-primary);
}
.editor-sheet-header-actions {
.editor-panel-header-actions {
display: flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
}
/* ── Sheet content ── */
.editor-sheet-content {
/* ── Dirty indicator ── */
.editor-panel-dirty {
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--t-status-warning, oklch(0.75 0.18 85));
font-size: 0.75rem;
font-weight: 500;
}
.editor-panel-dirty-dot {
width: 8px;
height: 8px;
border-radius: 9999px;
background: currentColor;
}
/* ── Panel content ── */
.editor-panel-content {
flex: 1;
overflow-y: auto;
overscroll-behavior: contain;
@@ -2575,18 +2574,8 @@
}
}
/* ── Hide content when collapsed ── */
.editor-sheet[data-state="collapsed"] .editor-sheet-content {
display: none;
}
/* Collapsed header doesn't need bottom border */
.editor-sheet[data-state="collapsed"] .editor-sheet-header {
border-bottom: none;
}
/* ── Page header inside sheet ── */
.editor-sheet-page-header {
/* ── Page header inside panel ── */
.editor-panel-page-header {
display: flex;
align-items: center;
justify-content: space-between;
@@ -2598,7 +2587,7 @@
}
}
.editor-sheet-page-title {
.editor-panel-page-title {
font-size: 0.9375rem;
font-weight: 600;
flex: 1;
@@ -2612,26 +2601,26 @@
}
}
.editor-sheet-undo-redo {
.editor-panel-undo-redo {
display: flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
}
/* Picker inside the editor sheet — grid scrolls within a capped height */
.editor-sheet .block-picker-overlay {
/* Picker inside the editor panel — grid scrolls within a capped height */
.editor-panel .block-picker-overlay {
position: static;
background: none;
}
.editor-sheet .block-picker {
.editor-panel .block-picker {
border-radius: 0;
max-height: none;
padding: 0;
}
.editor-sheet .block-picker-grid {
.editor-panel .block-picker-grid {
max-height: 45dvh;
overflow-y: auto;
}