spaxel/dashboard/css/expert.css
jedarden 1e8876d6b4 style(dashboard): continue design token migration across remaining CSS
Replace additional hard-coded colors with design tokens in layout,
notifications, panels, timeline, and other CSS files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 16:41:18 -04:00

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;
topvar(--space-1);
leftvar(--space-1);
}
.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;
}