Replace remaining hard-coded colors across all CSS files with design tokens from tokens.css. Remove duplicate inline positioning from live.html panels (now in layout.css). Add replay session blob fetch for immediate 3D scene state on seek. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
420 lines
9.9 KiB
CSS
420 lines
9.9 KiB
CSS
/**
|
|
* Spaxel Dashboard - Expert Mode Styles
|
|
*
|
|
* Expert mode 3D scene layout with responsive canvas sizing.
|
|
* Handles iOS Safari visual viewport quirks and bottom navigation spacing.
|
|
*
|
|
* WCAG 2.1 Touch Target Compliance:
|
|
* - All interactive elements meet minimum 44x44px touch target size
|
|
* - Touch targets are expanded using padding or pseudo-elements where needed
|
|
*/
|
|
|
|
/* ===== WCAG 2.1 Touch Target Compliance Utilities ===== */
|
|
|
|
/* Touch target expansion for small checkboxes */
|
|
.panel-form-checkbox input[type="checkbox"],
|
|
.pattern-checkbox input[type="checkbox"],
|
|
.timeline-category-checkbox input[type="checkbox"],
|
|
.sim-gdop-controls input[type="checkbox"] {
|
|
/* Ensure checkbox touch area is 44x44px minimum */
|
|
width: 18px;
|
|
height: 18px;
|
|
min-width: 18px;
|
|
min-height: 18px;
|
|
position: relative;
|
|
}
|
|
|
|
/* Expand checkbox hit area with pseudo-element */
|
|
.panel-form-checkbox input[type="checkbox"]::before,
|
|
.pattern-checkbox input[type="checkbox"]::before,
|
|
.timeline-category-checkbox input[type="checkbox"]::before,
|
|
.sim-gdop-controls input[type="checkbox"]::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
/* Expand small toggle switches to 44px height */
|
|
.toggle-switch {
|
|
min-height: 44px;
|
|
}
|
|
|
|
.toggle-switch .slider {
|
|
min-height: 44px;
|
|
height: 44px;
|
|
}
|
|
|
|
.toggle-switch .slider:before {
|
|
width: 36px;
|
|
height: 36px;
|
|
top: 4px;
|
|
left: 4px;
|
|
}
|
|
|
|
.toggle-switch input:checked + .slider:before {
|
|
transform: translateX(22px);
|
|
}
|
|
|
|
/* Expand slider thumb touch area */
|
|
.panel-form-group input[type="range"] {
|
|
height: 44px; /* Full touch target height */
|
|
background: transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.panel-form-group input[type="range"]::-webkit-slider-runnable-track {
|
|
height: 6px;
|
|
background: var(--bg-hover);
|
|
border-radius: var(--radius-control);
|
|
margin: 19px; /* Center track within 44px height */
|
|
}
|
|
|
|
.panel-form-group input[type="range"]::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 32px;
|
|
height: 32px;
|
|
background: var(--blue-10);
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
margin-top: -13px; /* Center thumb on track */
|
|
transition: transform 0.1s;
|
|
}
|
|
|
|
.panel-form-group input[type="range"]::-webkit-slider-thumb:hover {
|
|
transform: scale(1.15);
|
|
}
|
|
|
|
.panel-form-group input[type="range"]::-moz-range-track {
|
|
height: 6px;
|
|
background: var(--bg-hover);
|
|
border-radius: var(--radius-control);
|
|
}
|
|
|
|
.panel-form-group input[type="range"]::-moz-range-thumb {
|
|
width: 32px;
|
|
height: 32px;
|
|
background: var(--blue-10);
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
border: none;
|
|
}
|
|
|
|
/* Expand close button touch targets to 44x44px */
|
|
.panel-close,
|
|
.modal-close,
|
|
.panel-modal-close,
|
|
.sim-close-btn {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
width: 44px;
|
|
height: 44px;
|
|
font-size: var(--text-3xl);
|
|
line-height: 44px;
|
|
padding: 0;
|
|
}
|
|
|
|
/* Expand context menu items to 44px minimum height */
|
|
.context-item,
|
|
.blob-context-menu-item {
|
|
min-height: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* Expand link list items to 44px minimum */
|
|
.link-item {
|
|
min-height: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
font-size: var(--text-sm);
|
|
}
|
|
|
|
/* Ensure all buttons meet 44x44px minimum */
|
|
.panel-btn,
|
|
.modal-btn,
|
|
.btn,
|
|
.sim-btn,
|
|
.alert-banner-btn,
|
|
.timeline-filter-toggle,
|
|
.timeline-load-more-btn,
|
|
.view-btn,
|
|
#floorplan-btn,
|
|
.node-identify-btn {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
padding: var(--space-3) var(--space-5);
|
|
font-size: var(--text-sm);
|
|
}
|
|
|
|
/* Small buttons: expand with padding while keeping appearance compact */
|
|
.btn-sm,
|
|
.timeline-feedback-btn,
|
|
.timeline-seek-btn {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
padding: 14px var(--space-3); /* Extra vertical padding to reach 44px */
|
|
}
|
|
|
|
/* Volume tools buttons */
|
|
.volume-tools button {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* Hamburger tabs - ensure 44px minimum */
|
|
.hamburger-tab {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
}
|
|
|
|
/* Hamburger items - already 44px, ensure explicit */
|
|
.hamburger-item {
|
|
min-height: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* Toast dismiss button */
|
|
.toast-dismiss {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
width: 44px;
|
|
height: 44px;
|
|
font-size: var(--text-xl);
|
|
}
|
|
|
|
/* Mode toggle buttons */
|
|
.mode-toggle-btn {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* Action items */
|
|
.action-item .action-remove {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
padding: var(--space-3) var(--space-2);
|
|
font-size: var(--text-sm);
|
|
}
|
|
|
|
/* Sim item delete button */
|
|
.sim-item-delete {
|
|
min-height: 44px;
|
|
min-width: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* Form controls */
|
|
.panel-form-group input[type="text"],
|
|
.panel-form-group input[type="number"],
|
|
.panel-form-group input[type="password"],
|
|
.panel-form-group input[type="email"],
|
|
.panel-form-group input[type="url"],
|
|
.panel-form-group select,
|
|
.panel-form-group textarea,
|
|
.panel-input,
|
|
.form-control,
|
|
.timeline-filter-select,
|
|
.timeline-search,
|
|
.timeline-date-input {
|
|
min-height: 44px;
|
|
padding: var(--space-3) var(--space-4);
|
|
}
|
|
|
|
/* ===== Scene Container (3D Canvas) — grid child =====
|
|
#scene-container lives inside .app-main (the grid main area).
|
|
It fills its parent via width/height: 100% instead of position:fixed. */
|
|
#scene-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
touch-action: none;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Canvas height adjustment when simple mode navigation is visible */
|
|
body.simple-mode #scene-container {
|
|
/* Simple mode has 56px bottom nav, subtract from viewport height */
|
|
height: calc(100% - 56px);
|
|
}
|
|
|
|
/* iOS Safari-specific handling for visual viewport */
|
|
@supports (-webkit-touch-callout: none) {
|
|
/* iOS Safari with visual viewport API */
|
|
#scene-container {
|
|
/* Use dynamic viewport height on iOS (handles address bar) */
|
|
height: 100dvh;
|
|
}
|
|
|
|
body.simple-mode #scene-container {
|
|
/* Subtract bottom navigation bar height */
|
|
height: calc(100dvh - 56px);
|
|
}
|
|
}
|
|
|
|
/* iOS Safari orientation change handling */
|
|
@supports (-webkit-touch-callout: none) {
|
|
body {
|
|
-webkit-text-size-adjust: 100%;
|
|
}
|
|
}
|
|
|
|
/* Ensure canvas doesn't overflow during transitions */
|
|
#scene-container canvas {
|
|
display: block;
|
|
max-width: 100%;
|
|
max-height: 100%;
|
|
touch-action: none;
|
|
}
|
|
|
|
/* Handle visual viewport resize events (iOS Safari keyboard, address bar) */
|
|
.visual-viewport-resizing #scene-container {
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ===== Status Bar — grid header via layout.css =====
|
|
When #status-bar is a direct child of .app-shell--live, it is the
|
|
grid header. Uses position:sticky to stay visible during scroll. */
|
|
#status-bar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
|
|
/* Simple mode navigation bar */
|
|
body.simple-mode .simple-quick-actions {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 56px;
|
|
z-index: 100;
|
|
}
|
|
|
|
/* Simple mode navigation bar safe area support */
|
|
@supports (padding: max(0px)) {
|
|
body.simple-mode .simple-quick-actions {
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
height: calc(56px + env(safe-area-inset-bottom));
|
|
}
|
|
}
|
|
|
|
/* ===== Mode Toggle Bar — positioned below sticky status bar ===== */
|
|
.mode-toggle-bar {
|
|
position: sticky;
|
|
top: 56px;
|
|
height: 44px;
|
|
z-index: 90;
|
|
}
|
|
|
|
/* ===== Alert Banner — full-width overlay ===== */
|
|
#alert-banner {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 5000;
|
|
}
|
|
|
|
/* ===== Orientation-specific adjustments ===== */
|
|
|
|
/* Landscape orientation */
|
|
@media screen and (orientation: landscape) {
|
|
#scene-container {
|
|
height: 100%;
|
|
height: 100dvh;
|
|
}
|
|
|
|
body.simple-mode #scene-container {
|
|
height: calc(100vh - 56px);
|
|
height: calc(100dvh - 56px);
|
|
}
|
|
}
|
|
|
|
/* Portrait orientation */
|
|
@media screen and (orientation: portrait) {
|
|
#scene-container {
|
|
height: 100%;
|
|
height: 100dvh;
|
|
}
|
|
|
|
body.simple-mode #scene-container {
|
|
height: calc(100vh - 56px);
|
|
height: calc(100dvh - 56px);
|
|
}
|
|
}
|
|
|
|
/* ===== Responsive breakpoints ===== */
|
|
|
|
/* Small mobile devices */
|
|
@media screen and (max-width: 600px) {
|
|
#scene-container {
|
|
height: 100%;
|
|
height: 100dvh;
|
|
}
|
|
|
|
body.simple-mode #scene-container {
|
|
height: calc(100vh - 56px);
|
|
height: calc(100dvh - 56px);
|
|
}
|
|
}
|
|
|
|
/* Tablets and larger */
|
|
@media screen and (min-width: 601px) {
|
|
#scene-container {
|
|
height: 100%;
|
|
height: 100dvh;
|
|
}
|
|
}
|
|
|
|
/* ===== Safe area insets for notched devices ===== */
|
|
@supports (padding: max(0px)) {
|
|
body {
|
|
padding-top: env(safe-area-inset-top);
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
#scene-container {
|
|
padding-left: env(safe-area-inset-left);
|
|
padding-right: env(safe-area-inset-right);
|
|
}
|
|
|
|
#scene-container {
|
|
height: calc(100% - env(safe-area-inset-top) - env(safe-area-inset-bottom));
|
|
height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
|
|
}
|
|
|
|
body.simple-mode #scene-container {
|
|
height: calc(100% - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 56px);
|
|
height: calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 56px);
|
|
}
|
|
}
|
|
|
|
/* ===== Prevent overscroll/bounce on iOS ===== */
|
|
@supports (-webkit-touch-callout: none) {
|
|
#scene-container {
|
|
-webkit-overflow-scrolling: touch;
|
|
overscroll-behavior: none;
|
|
}
|
|
}
|
|
|
|
/* ===== Loading state ===== */
|
|
#scene-container.loading {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
/* ===== Hide scene container in simple mode ===== */
|
|
body.simple-mode #scene-container {
|
|
display: none;
|
|
}
|
|
|
|
/* ===== Expert mode specific styles ===== */
|
|
body:not(.simple-mode) #scene-container {
|
|
display: block;
|
|
}
|