diff --git a/base.css b/base.css index 71a18c1..d04358f 100644 --- a/base.css +++ b/base.css @@ -3266,3 +3266,930 @@ input[type="range"].lt-range::-moz-range-thumb { /* Monospace table-style number alignment */ .lt-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; } + + +/* ================================================================ + v1.1 COMPONENT EXTENSIONS + 51. Light Theme + 52. Theme Toggle Button + 53. Skeleton Loader Variants + 54. Empty State Enhancements + 55. Nav Notification Badge + 56. Right-Side Drawer + 57. Sticky Table + 58. Multi-Select / Combobox + 59. Context Menu + 60. Offline Banner + 61. Timeline / Activity Feed + 62. Avatar + 63. Split Pane + 64. Chart Container + 65. Toast Queue + 66. Autocomplete / Typeahead + 67. WebSocket Status + 68. Print Enhancements + ================================================================ */ + + +/* ---------------------------------------------------------------- + 51. LIGHT THEME + ---------------------------------------------------------------- */ +html[data-theme="light"] { + --bg-primary: #eef1f6; + --bg-secondary: #e2e6ed; + --bg-tertiary: #d5dae3; + --bg-card: #ffffff; + --bg-terminal: #f7f9fc; + --bg-overlay: rgba(238,241,246,0.96); + --bg-input: #ffffff; + + --text-primary: #1a2035; + --text-secondary: #3a4a6a; + --text-muted: #647898; + --text-dim: #8fa0b8; + + --border-color: rgba(0,100,180,0.20); + --border-color-hi: var(--accent-cyan); + --border-color-dim: rgba(0,100,180,0.10); + --border-dim: rgba(0,100,180,0.10); + + /* Slightly muted glows for light bg */ + --glow-orange: 0 0 4px rgba(255,107,0,0.6), 0 0 10px rgba(255,107,0,0.3); + --glow-cyan: 0 0 4px rgba(0,150,200,0.6), 0 0 10px rgba(0,150,200,0.3); + --glow-green: 0 0 4px rgba(0,180,80,0.5), 0 0 10px rgba(0,180,80,0.25); + --glow-red: 0 0 4px rgba(220,0,50,0.5), 0 0 10px rgba(220,0,50,0.25); + --glow-amber: 0 0 4px rgba(200,130,0,0.5), 0 0 10px rgba(200,130,0,0.25); + + color-scheme: light; +} +/* Hide CRT overlays in light mode */ +html[data-theme="light"] body::before, +html[data-theme="light"] body::after { display: none; } +/* Soften dot grid */ +html[data-theme="light"] body { + background-image: radial-gradient(circle, rgba(0,80,160,0.10) 1px, transparent 1px); +} +/* Nav / header adjustments */ +html[data-theme="light"] .lt-header { + background: rgba(238,241,246,0.96); + border-bottom-color: var(--border-color); +} +html[data-theme="light"] .lt-nav-link, +html[data-theme="light"] .lt-nav-link:hover { color: var(--text-secondary); } +html[data-theme="light"] .lt-nav-link.active { color: var(--accent-orange); } +html[data-theme="light"] .lt-sidebar { background: var(--bg-secondary); border-color: var(--border-color); } +html[data-theme="light"] .lt-card, +html[data-theme="light"] .lt-frame { background: var(--bg-card); border-color: var(--border-color); } +html[data-theme="light"] .lt-section { background: var(--bg-card); border-color: var(--border-color); } +html[data-theme="light"] .lt-input, +html[data-theme="light"] .lt-select, +html[data-theme="light"] .lt-textarea { background: var(--bg-input); border-color: var(--border-color); color: var(--text-primary); } +html[data-theme="light"] .lt-table th { background: var(--bg-secondary); } +html[data-theme="light"] .lt-table tr:hover td { background: rgba(0,100,200,0.04); } +html[data-theme="light"] .lt-nav-drawer { background: var(--bg-card); } +html[data-theme="light"] code, +html[data-theme="light"] .lt-code-block { background: var(--bg-secondary); color: var(--text-primary); } + + +/* ---------------------------------------------------------------- + 52. THEME TOGGLE BUTTON + ---------------------------------------------------------------- */ +.lt-theme-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; height: 32px; + border-radius: 4px; + border: 1px solid var(--border-color); + background: transparent; + color: var(--accent-cyan); + font-size: 1rem; + cursor: pointer; + transition: var(--transition-fast); + flex-shrink: 0; +} +.lt-theme-btn:hover { + background: var(--accent-cyan-dim); + border-color: var(--accent-cyan-border); + box-shadow: var(--box-glow-cyan); +} +.lt-theme-btn:focus-visible { + outline: 1px solid var(--accent-cyan); + outline-offset: 2px; +} +@media (pointer: coarse) { + .lt-theme-btn { width: 44px; height: 44px; font-size: 1.2rem; } +} + + +/* ---------------------------------------------------------------- + 53. SKELETON LOADER VARIANTS + (Extends existing .lt-skeleton in section 19) + ---------------------------------------------------------------- */ +/* Override section 19 shimmer with improved cyan-tinted version */ +.lt-skeleton { + background: linear-gradient( + 90deg, + var(--bg-secondary) 25%, + rgba(0,212,255,0.05) 50%, + var(--bg-secondary) 75% + ); + background-size: 200% 100%; + animation: lt-shimmer 1.6s ease-in-out infinite; + border-radius: 2px; + display: block; +} +@keyframes lt-shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} +@media (prefers-reduced-motion: reduce) { + .lt-skeleton { animation: none; background: var(--bg-secondary); } +} +@media (pointer: coarse) { + .lt-skeleton { animation: none; } +} +/* Size variants */ +.lt-skeleton-text { height: 0.8rem; width: 100%; margin-bottom: 0.4rem; } +.lt-skeleton-title { height: 1.1rem; width: 55%; margin-bottom: 0.6rem; } +.lt-skeleton-avatar { height: 2.25rem; width: 2.25rem; border-radius: 50%; flex-shrink: 0; } +.lt-skeleton-btn { height: 1.9rem; width: 5.5rem; } +.lt-skeleton-badge { height: 1.1rem; width: 4rem; border-radius: 999px; } +.lt-skeleton-line-sm { height: 0.7rem; width: 40%; margin-bottom: 0.3rem; } +.lt-skeleton-line-lg { height: 0.8rem; width: 80%; margin-bottom: 0.4rem; } + +/* Card skeleton */ +.lt-skeleton-card { + background: var(--bg-card); + border: 1px solid var(--border-dim); + clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%); + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.4rem; +} +.lt-skeleton-card-header { + display: flex; + gap: 0.75rem; + align-items: center; + margin-bottom: 0.4rem; +} +/* Table row skeleton */ +.lt-skeleton-row { + display: grid; + grid-template-columns: 1.5rem 1fr 2.5fr 1fr 1fr 1fr; + gap: 1rem; + padding: 0.65rem 1rem; + border-bottom: 1px solid var(--border-dim); + align-items: center; +} +@media (max-width: 639px) { + .lt-skeleton-row { grid-template-columns: 1fr 2fr; gap: 0.5rem; } +} + + +/* ---------------------------------------------------------------- + 54. EMPTY STATE ENHANCEMENTS + (Extends existing .lt-empty in section 19) + ---------------------------------------------------------------- */ +.lt-empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 3rem 2rem; + text-align: center; + gap: 0.6rem; +} +.lt-empty-state-icon { + font-size: 2.5rem; + opacity: 0.35; + margin-bottom: 0.25rem; + line-height: 1; +} +.lt-empty-state-title { + color: var(--text-secondary); + font-size: 0.8rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; +} +.lt-empty-state-body { + color: var(--text-muted); + font-size: 0.72rem; + max-width: 30ch; + line-height: 1.7; +} +.lt-empty-state .lt-btn { margin-top: 0.5rem; } +/* Small inline variant */ +.lt-empty-state--sm { + padding: 1.5rem 1rem; +} +.lt-empty-state--sm .lt-empty-state-icon { font-size: 1.5rem; } +.lt-empty-state--sm .lt-empty-state-title { font-size: 0.72rem; } + + +/* ---------------------------------------------------------------- + 55. NAV NOTIFICATION BADGE + ---------------------------------------------------------------- */ +.lt-notif-wrap { + position: relative; + display: inline-flex; +} +.lt-notif-badge { + position: absolute; + top: -4px; + right: -4px; + min-width: 1rem; + height: 1rem; + padding: 0 0.2rem; + background: var(--accent-red); + color: #fff; + font-size: 0.58rem; + font-weight: 700; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + border: 1.5px solid var(--bg-primary); + pointer-events: none; + animation: lt-notif-pulse 2.5s ease-in-out infinite; +} +.lt-notif-badge[data-count="0"], +.lt-notif-badge:empty { display: none; } +@keyframes lt-notif-pulse { + 0%, 100% { box-shadow: 0 0 0 0 rgba(255,45,85,0.7); } + 50% { box-shadow: 0 0 0 5px rgba(255,45,85,0); } +} +@media (pointer: coarse) { .lt-notif-badge { animation: none; } } + + +/* ---------------------------------------------------------------- + 56. RIGHT-SIDE DRAWER + ---------------------------------------------------------------- */ +.lt-drawer-right { + position: fixed; + top: var(--header-height); + right: 0; + height: calc(100vh - var(--header-height)); + width: 400px; + max-width: 92vw; + background: var(--bg-card); + border-left: 1px solid var(--border-color); + clip-path: polygon(10px 0, 100% 0, 100% 100%, 0 100%, 0 10px); + transform: translateX(100%); + transition: transform 0.25s cubic-bezier(0.4,0,0.2,1); + z-index: calc(var(--z-overlay) + 2); /* 10001 — same level as nav overlay */ + display: flex; + flex-direction: column; + overflow: hidden; +} +.lt-drawer-right.is-open { transform: translateX(0); } +.lt-drawer-right-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--border-dim); + flex-shrink: 0; +} +.lt-drawer-right-title { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--accent-cyan); + text-shadow: var(--glow-cyan); +} +.lt-drawer-right-close { + width: 32px; height: 32px; + display: flex; align-items: center; justify-content: center; + background: none; + border: 1px solid transparent; + color: var(--text-muted); + font-size: 1rem; + cursor: pointer; + border-radius: 2px; + transition: var(--transition-fast); +} +.lt-drawer-right-close:hover { color: var(--accent-red); border-color: var(--accent-red); } +.lt-drawer-right-close:focus-visible { outline: 1px solid var(--accent-cyan); outline-offset: 2px; } +.lt-drawer-right-body { + flex: 1; + overflow-y: auto; + padding: 1rem; + overscroll-behavior: contain; +} +.lt-drawer-right-footer { + padding: 0.75rem 1rem; + border-top: 1px solid var(--border-dim); + display: flex; + gap: 0.5rem; + justify-content: flex-end; + flex-shrink: 0; +} +/* Overlay for right drawer */ +.lt-drawer-right-overlay { + position: fixed; + inset: 0; + background: rgba(3,5,8,0.65); + z-index: calc(var(--z-overlay) + 1); /* 10000 */ + opacity: 0; + pointer-events: none; + transition: opacity 0.25s ease; +} +.lt-drawer-right-overlay.is-open { opacity: 1; pointer-events: auto; } +@media (max-width: 479px) { + .lt-drawer-right { width: 100vw; max-width: 100vw; top: 0; height: 100vh; border-left: none; border-top: 1px solid var(--border-color); } +} +@media (pointer: coarse) { + .lt-drawer-right-close { width: 44px; height: 44px; } +} + + +/* ---------------------------------------------------------------- + 57. STICKY TABLE + ---------------------------------------------------------------- */ +.lt-table-sticky-wrap { + max-height: 420px; + overflow-y: auto; + overflow-x: auto; + overscroll-behavior: contain; + border: 1px solid var(--border-dim); + border-radius: 2px; +} +.lt-table-sticky-wrap .lt-table { margin: 0; border: none; } +.lt-table-sticky-wrap .lt-table thead th { + position: sticky; + top: 0; + z-index: 2; + background: var(--bg-tertiary); + backdrop-filter: blur(6px); + border-bottom: 2px solid var(--border-color); + box-shadow: 0 2px 8px rgba(3,5,8,0.5); +} +/* Custom scrollbar inside sticky wrap */ +.lt-table-sticky-wrap::-webkit-scrollbar { width: 6px; height: 6px; } +.lt-table-sticky-wrap::-webkit-scrollbar-track { background: var(--bg-primary); } +.lt-table-sticky-wrap::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; } + + +/* ---------------------------------------------------------------- + 58. MULTI-SELECT / COMBOBOX + ---------------------------------------------------------------- */ +.lt-combobox { + position: relative; + display: block; +} +.lt-combobox-input-wrap { + display: flex; + flex-wrap: wrap; + gap: 4px; + align-items: center; + min-height: 36px; + padding: 4px 8px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + cursor: text; + transition: border-color 0.15s; +} +.lt-combobox-input-wrap:focus-within { + border-color: var(--accent-cyan); + box-shadow: var(--box-glow-cyan); +} +.lt-combobox-input-wrap .lt-combobox-input { + flex: 1; + min-width: 80px; + background: none; + border: none; + outline: none; + color: var(--text-primary); + font-family: var(--font-mono); + font-size: 0.8rem; + padding: 2px 0; +} +/* Selected tag chips inside the input */ +.lt-combobox-tag { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 1px 6px; + background: var(--accent-cyan-dim); + border: 1px solid var(--accent-cyan-border); + color: var(--accent-cyan); + font-size: 0.7rem; + border-radius: 2px; + white-space: nowrap; +} +.lt-combobox-tag-remove { + background: none; border: none; cursor: pointer; + color: var(--text-muted); font-size: 0.75rem; padding: 0; line-height: 1; + display: flex; align-items: center; +} +.lt-combobox-tag-remove:hover { color: var(--accent-red); } +/* Dropdown list */ +.lt-combobox-dropdown { + position: absolute; + top: calc(100% + 2px); + left: 0; right: 0; + background: var(--bg-card); + border: 1px solid var(--border-color); + clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%); + z-index: var(--z-dropdown); + max-height: 200px; + overflow-y: auto; + display: none; +} +.lt-combobox-dropdown.is-open { display: block; } +.lt-combobox-option { + padding: 0.4rem 0.75rem; + font-size: 0.78rem; + color: var(--text-secondary); + cursor: pointer; + display: flex; + align-items: center; + gap: 0.5rem; + transition: background 0.1s; +} +.lt-combobox-option:hover, +.lt-combobox-option.is-focused { background: var(--accent-cyan-dim); color: var(--accent-cyan); } +.lt-combobox-option.is-selected::before { + content: '✓'; + color: var(--accent-green); + font-size: 0.7rem; + width: 0.8rem; + flex-shrink: 0; +} +.lt-combobox-option:not(.is-selected)::before { content: ''; width: 0.8rem; display: inline-block; flex-shrink: 0; } +.lt-combobox-empty { + padding: 0.6rem 0.75rem; + font-size: 0.72rem; + color: var(--text-muted); + text-align: center; +} + + +/* ---------------------------------------------------------------- + 59. CONTEXT MENU + ---------------------------------------------------------------- */ +.lt-context-menu { + position: fixed; + background: var(--bg-card); + border: 1px solid var(--border-color); + clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%); + min-width: 160px; + z-index: var(--z-tooltip); + box-shadow: 0 8px 32px rgba(0,0,0,0.45); + padding: 4px 0; + display: none; +} +.lt-context-menu.is-open { display: block; } +.lt-context-menu-item { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.4rem 0.75rem; + font-size: 0.78rem; + color: var(--text-secondary); + cursor: pointer; + transition: background 0.1s; + user-select: none; + white-space: nowrap; +} +.lt-context-menu-item:hover { background: var(--accent-cyan-dim); color: var(--accent-cyan); } +.lt-context-menu-item.is-danger:hover { background: var(--accent-red-dim); color: var(--accent-red); } +.lt-context-menu-item .icon { width: 1rem; text-align: center; opacity: 0.7; font-size: 0.75rem; } +.lt-context-menu-item kbd { + margin-left: auto; + font-size: 0.6rem; + color: var(--text-muted); + background: var(--bg-tertiary); + border: 1px solid var(--border-dim); + padding: 1px 4px; + border-radius: 2px; +} +.lt-context-menu-divider { + height: 1px; + background: var(--border-dim); + margin: 4px 0; +} +.lt-context-menu-label { + padding: 0.25rem 0.75rem; + font-size: 0.62rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-muted); + pointer-events: none; +} + + +/* ---------------------------------------------------------------- + 60. OFFLINE BANNER + ---------------------------------------------------------------- */ +.lt-offline-banner { + position: fixed; + top: var(--header-height); + left: 0; right: 0; + padding: 0.4rem 1rem; + background: var(--accent-red-dim); + border-top: 1px solid var(--accent-red); + border-bottom: 1px solid var(--accent-red); + color: var(--accent-red); + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.1em; + text-align: center; + z-index: var(--z-fixed); + transform: translateY(-100%); + transition: transform 0.25s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} +.lt-offline-banner.is-visible { transform: translateY(0); } +.lt-offline-banner .lt-dot--red { + animation: lt-pulse 1s ease-in-out infinite; +} +@keyframes lt-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} +body.lt-is-offline .lt-main { margin-top: 2rem; transition: margin-top 0.25s ease; } + + +/* ---------------------------------------------------------------- + 61. TIMELINE / ACTIVITY FEED + ---------------------------------------------------------------- */ +.lt-timeline { + display: flex; + flex-direction: column; + gap: 0; + padding-left: 1.25rem; + border-left: 1px solid var(--border-dim); + margin-left: 0.5rem; +} +.lt-timeline-item { + position: relative; + padding: 0 0 1.25rem 1.25rem; +} +.lt-timeline-item:last-child { padding-bottom: 0; } +/* Connector dot */ +.lt-timeline-item::before { + content: ''; + position: absolute; + left: -5px; + top: 4px; + width: 8px; height: 8px; + border-radius: 50%; + background: var(--accent-cyan); + box-shadow: 0 0 6px var(--accent-cyan); + border: 1.5px solid var(--bg-primary); +} +.lt-timeline-item--orange::before { background: var(--accent-orange); box-shadow: var(--glow-orange); } +.lt-timeline-item--green::before { background: var(--accent-green); box-shadow: var(--glow-green); } +.lt-timeline-item--red::before { background: var(--accent-red); box-shadow: var(--glow-red); } +.lt-timeline-item--dim::before { background: var(--text-muted); box-shadow: none; } +.lt-timeline-meta { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.68rem; + color: var(--text-muted); + margin-bottom: 0.2rem; + flex-wrap: wrap; +} +.lt-timeline-actor { color: var(--accent-cyan); } +.lt-timeline-time { margin-left: auto; white-space: nowrap; } +.lt-timeline-body { font-size: 0.78rem; color: var(--text-secondary); line-height: 1.5; } +.lt-timeline-body code { font-size: 0.72rem; color: var(--accent-green); } + + +/* ---------------------------------------------------------------- + 62. AVATAR + ---------------------------------------------------------------- */ +.lt-avatar { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: var(--bg-tertiary); + border: 1.5px solid var(--border-color); + color: var(--accent-cyan); + font-weight: 700; + font-size: 0.75rem; + text-transform: uppercase; + overflow: hidden; + flex-shrink: 0; + user-select: none; +} +.lt-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; } +/* Sizes */ +.lt-avatar--xs { width: 1.5rem; height: 1.5rem; font-size: 0.55rem; } +.lt-avatar--sm { width: 2rem; height: 2rem; font-size: 0.65rem; } +.lt-avatar { width: 2.5rem; height: 2.5rem; font-size: 0.75rem; } /* default */ +.lt-avatar--lg { width: 3.5rem; height: 3.5rem; font-size: 1rem; } +.lt-avatar--xl { width: 5rem; height: 5rem; font-size: 1.4rem; } +/* Color variants */ +.lt-avatar--orange { background: var(--accent-orange-dim); border-color: var(--accent-orange-border); color: var(--accent-orange); } +.lt-avatar--green { background: var(--accent-green-dim); border-color: var(--accent-green-border); color: var(--accent-green); } +.lt-avatar--red { background: var(--accent-red-dim); border-color: var(--accent-red); color: var(--accent-red); } +.lt-avatar--purple { background: var(--accent-purple-dim); border-color: var(--accent-purple); color: var(--accent-purple); } +/* Avatar group (overlapping row) */ +.lt-avatar-group { display: flex; } +.lt-avatar-group .lt-avatar { + margin-left: -0.5rem; + border: 2px solid var(--bg-primary); + transition: transform 0.15s; +} +.lt-avatar-group .lt-avatar:first-child { margin-left: 0; } +.lt-avatar-group .lt-avatar:hover { transform: translateY(-2px) scale(1.08); z-index: 1; } +/* Status ring */ +.lt-avatar-wrap { + position: relative; + display: inline-flex; + flex-shrink: 0; +} +.lt-avatar-status { + position: absolute; + bottom: 1px; right: 1px; + width: 9px; height: 9px; + border-radius: 50%; + border: 1.5px solid var(--bg-primary); + background: var(--text-muted); +} +.lt-avatar-status--online { background: var(--accent-green); box-shadow: 0 0 4px var(--accent-green); } +.lt-avatar-status--away { background: var(--accent-amber); } +.lt-avatar-status--busy { background: var(--accent-red); } + + +/* ---------------------------------------------------------------- + 63. SPLIT PANE + ---------------------------------------------------------------- */ +.lt-split { + display: flex; + width: 100%; + height: 100%; + overflow: hidden; + position: relative; +} +.lt-split--vertical { flex-direction: column; } +.lt-split-pane { + overflow: auto; + flex: 1; + min-width: 0; + min-height: 0; +} +.lt-split-divider { + flex: 0 0 5px; + background: var(--border-dim); + cursor: col-resize; + transition: background 0.15s; + position: relative; + user-select: none; + touch-action: none; +} +.lt-split-divider::after { + content: '⠿'; + position: absolute; + top: 50%; left: 50%; + transform: translate(-50%, -50%); + color: var(--text-muted); + font-size: 0.6rem; + pointer-events: none; +} +.lt-split-divider:hover, +.lt-split-divider.is-dragging { background: var(--accent-cyan-border); } +.lt-split--vertical .lt-split-divider { cursor: row-resize; } +/* On mobile, stack vertically and hide divider */ +@media (max-width: 767px) { + .lt-split:not(.lt-split--vertical) { flex-direction: column; } + .lt-split:not(.lt-split--vertical) .lt-split-divider { display: none; } + .lt-split:not(.lt-split--vertical) .lt-split-pane { flex: 0 0 auto; } +} + + +/* ---------------------------------------------------------------- + 64. CHART CONTAINER + ---------------------------------------------------------------- */ +.lt-chart-wrap { + position: relative; + background: var(--bg-card); + border: 1px solid var(--border-dim); + clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%); + padding: 1rem; + overflow: hidden; +} +.lt-chart-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + flex-wrap: wrap; + gap: 0.5rem; +} +.lt-chart-title { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--text-secondary); +} +.lt-chart-legend { + display: flex; + gap: 1rem; + font-size: 0.68rem; + color: var(--text-muted); + flex-wrap: wrap; +} +.lt-chart-legend-item { + display: flex; + align-items: center; + gap: 0.35rem; +} +.lt-chart-legend-dot { + width: 8px; height: 8px; + border-radius: 50%; + flex-shrink: 0; +} +.lt-chart-body { + position: relative; + width: 100%; +} +/* Sparkline — inline mini chart */ +.lt-sparkline { + display: inline-block; + vertical-align: middle; +} +/* Axis labels */ +.lt-chart-axis { + display: flex; + justify-content: space-between; + font-size: 0.62rem; + color: var(--text-muted); + margin-top: 0.25rem; +} +/* Loading / no-data states */ +.lt-chart-wrap.is-loading .lt-chart-body { min-height: 120px; } +.lt-chart-wrap.is-loading .lt-chart-body::after { + content: 'LOADING DATA...'; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.68rem; + color: var(--text-muted); + letter-spacing: 0.1em; +} + + +/* ---------------------------------------------------------------- + 65. TOAST QUEUE (enhanced multi-toast stack) + ---------------------------------------------------------------- */ +/* Toast container already exists in section 14; these are addons */ +#lt-toast-container { + /* Stack from bottom — newest on top */ + display: flex; + flex-direction: column-reverse; + gap: 0.5rem; +} +.lt-toast + .lt-toast { margin-top: 0; } /* gap handles spacing */ +/* Progress bar on toast (auto-dismiss countdown) */ +.lt-toast-progress { + position: absolute; + bottom: 0; left: 0; + height: 2px; + background: currentColor; + opacity: 0.4; + animation: lt-toast-drain linear forwards; +} +@keyframes lt-toast-drain { + from { width: 100%; } + to { width: 0; } +} +/* Pause on hover */ +.lt-toast:hover .lt-toast-progress { animation-play-state: paused; } + + +/* ---------------------------------------------------------------- + 66. AUTOCOMPLETE / TYPEAHEAD + ---------------------------------------------------------------- */ +.lt-typeahead { + position: relative; + display: block; +} +.lt-typeahead-dropdown { + position: absolute; + top: calc(100% + 2px); + left: 0; right: 0; + background: var(--bg-card); + border: 1px solid var(--border-color); + clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%); + z-index: var(--z-dropdown); + max-height: 220px; + overflow-y: auto; + display: none; +} +.lt-typeahead-dropdown.is-open { display: block; } +.lt-typeahead-item { + padding: 0.4rem 0.75rem; + font-size: 0.78rem; + color: var(--text-secondary); + cursor: pointer; + display: flex; + align-items: center; + gap: 0.5rem; + transition: background 0.1s; +} +.lt-typeahead-item:hover, +.lt-typeahead-item.is-focused { background: var(--accent-cyan-dim); color: var(--accent-cyan); } +.lt-typeahead-item mark { + background: none; + color: var(--accent-orange); + font-weight: 700; +} +.lt-typeahead-item .icon { + font-size: 0.75rem; + opacity: 0.6; + width: 1rem; + text-align: center; + flex-shrink: 0; +} +.lt-typeahead-empty { + padding: 0.6rem 0.75rem; + font-size: 0.72rem; + color: var(--text-muted); + text-align: center; + letter-spacing: 0.05em; +} +/* Loading state in dropdown */ +.lt-typeahead-loading { + padding: 0.6rem 0.75rem; + font-size: 0.72rem; + color: var(--text-muted); + display: flex; + align-items: center; + gap: 0.5rem; +} +.lt-typeahead-loading::before { + content: ''; + width: 10px; height: 10px; + border: 1.5px solid var(--border-color); + border-top-color: var(--accent-cyan); + border-radius: 50%; + animation: spin 0.7s linear infinite; + flex-shrink: 0; +} + + +/* ---------------------------------------------------------------- + 67. WEBSOCKET STATUS INDICATOR + ---------------------------------------------------------------- */ +.lt-ws-status { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-size: 0.68rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--text-muted); + padding: 2px 8px; + border: 1px solid var(--border-dim); + border-radius: 2px; +} +.lt-ws-status .lt-dot { flex-shrink: 0; } +.lt-ws-status[data-state="connected"] { color: var(--accent-green); border-color: var(--accent-green-border); } +.lt-ws-status[data-state="connecting"] { color: var(--accent-amber); border-color: var(--accent-amber-dim); } +.lt-ws-status[data-state="disconnected"] { color: var(--accent-red); border-color: var(--accent-red-dim); } +.lt-ws-status[data-state="connected"] .lt-dot { background: var(--accent-green); box-shadow: var(--glow-green); } +.lt-ws-status[data-state="connecting"] .lt-dot { background: var(--accent-amber); box-shadow: var(--glow-amber); animation: lt-pulse 0.8s ease-in-out infinite; } +.lt-ws-status[data-state="disconnected"] .lt-dot { background: var(--accent-red); } + + +/* ---------------------------------------------------------------- + 68. PRINT ENHANCEMENTS + (Extends section 25) + ---------------------------------------------------------------- */ +@media print { + .lt-sidebar, .lt-nav, .lt-header-right, + .lt-btn-group, .lt-menu-btn, + .lt-nav-drawer, .lt-nav-drawer-overlay, + .lt-offline-banner, .lt-context-menu, + [data-modal-open], [data-tooltip] { display: none !important; } + + .lt-section, .lt-card, .lt-frame { + break-inside: avoid; + box-shadow: none; + clip-path: none; + border: 1px solid #999; + } + .lt-table { border-collapse: collapse; } + .lt-table th, .lt-table td { border: 1px solid #ccc; } + .lt-table thead th { background: #eee !important; color: black !important; } + a[href]::after { content: " (" attr(href) ")"; font-size: 0.7em; color: #666; } + a[href^="#"]::after, a[href^="javascript"]::after { content: ""; } + .lt-code-block { white-space: pre-wrap; word-break: break-all; } + .lt-page-header { border-bottom: 2px solid #333; padding-bottom: 0.5rem; margin-bottom: 1rem; } +} diff --git a/base.html b/base.html index 3ecaa4d..d290243 100644 --- a/base.html +++ b/base.html @@ -111,6 +111,16 @@
Dark/light mode with OS preference detection and localStorage persistence.
+lt.theme.toggle() | .set('light') | .get()
+ compute-storage-01. Ticket auto-created.Detail/inspect panel from the right. Focus trap, ESC close, overlay backdrop, return-focus.
+lt.rightDrawer.open('id') | .close() | .toggle()
+ Right-click any element with data-context-menu or trigger programmatically.
| ID | Priority | Title | Status | Assignee |
|---|---|---|---|---|
| #001 | P1 | Link-down on compute-storage-01 | Open | jdoe |
| #002 | P2 | Switch port flapping USW-Pro-24 | In Progress | smith |
| #003 | P3 | Scheduled SFP+ replacement | Pending | ops-bot |
| #004 | P4 | SSL cert renewal wiki | Open | admin |
| #005 | P1 | RAID controller firmware | Open | jdoe |
| #006 | P2 | Backup job failure nas-01 | Closed | backup-bot |
| #007 | P3 | Prometheus alert rule tuning | In Progress | ops-team |
Drag the divider to resize. Stacks vertically on mobile.
+Both panels maintain independent scrolling.
+lt.splitPane.init(el, { initial: 0.4, minA: 120 })
Offline banner + body class auto-applied on navigator.onLine change. WS manager has exponential backoff, event emitter, status indicator binding.