// ============================================================================ // OpenClaw Control Center — Utility Mixins // ============================================================================ // Reusable patterns that enforce design-system consistency. // Components should @use this module and include mixins rather than // writing repetitive CSS blocks. // ============================================================================ @use 'tokens' as *; // --------------------------------------------------------------------------- // Elevation / Surface Card // Applies consistent card styling using design tokens. // Usage: @include utils.card-surface(); // --------------------------------------------------------------------------- @mixin card-surface($elevation: 1) { background-color: var(--cc-surface-medium); border-radius: var(--cc-card-radius); border: 1px solid var(--cc-surface-lighter); box-shadow: var(--cc-shadow-#{$elevation}); transition: box-shadow var(--cc-duration-short) var(--cc-easing-standard); } // --------------------------------------------------------------------------- // Elevated card on hover // --------------------------------------------------------------------------- @mixin card-hover($elevation: 2) { &:hover { box-shadow: var(--cc-shadow-#{$elevation}); } } // --------------------------------------------------------------------------- // Status-aware left border // Applies colored left border based on agent status. // Usage: @include utils.status-border('active'); // --------------------------------------------------------------------------- @mixin status-border($status) { $status-map: ( 'active': var(--cc-status-active), 'idle': var(--cc-status-idle), 'thinking': var(--cc-status-thinking), 'error': var(--cc-status-error), 'offline': var(--cc-status-offline), ); $color: map-get($status-map, $status); @if not $color { $color: var(--cc-status-offline); } border-left: 4px solid $color; } // --------------------------------------------------------------------------- // Status badge / pill // --------------------------------------------------------------------------- @mixin status-badge($status) { $fg-map: ( 'active': var(--cc-status-active), 'idle': var(--cc-status-idle), 'thinking': var(--cc-status-thinking), 'error': var(--cc-status-error), 'offline': var(--cc-status-offline), ); $bg-map: ( 'active': var(--cc-status-active-bg), 'idle': var(--cc-status-idle-bg), 'thinking': var(--cc-status-thinking-bg), 'error': var(--cc-status-error-bg), 'offline': var(--cc-status-offline-bg), ); display: inline-flex; align-items: center; gap: 6px; height: $badge-height; padding: 0 8px; border-radius: $badge-border-radius; background-color: map-get($bg-map, $status); color: map-get($fg-map, $status); font-size: $font-size-label-medium; font-weight: $font-weight-medium; white-space: nowrap; } // --------------------------------------------------------------------------- // Glass surface (frosted glass effect) // --------------------------------------------------------------------------- @mixin glass-surface { background-color: rgba(19, 22, 26, 0.8); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border: 1px solid var(--cc-surface-lighter); } // --------------------------------------------------------------------------- // Responsive grid // Creates a responsive grid that adapts from 1-col to 2-col. // --------------------------------------------------------------------------- @mixin responsive-grid($min-col-width: $card-min-width, $gap: $spacing-card-gap) { display: grid; grid-template-columns: repeat(auto-fill, minmax($min-col-width, 1fr)); gap: $gap; } // --------------------------------------------------------------------------- // Scroll container // --------------------------------------------------------------------------- @mixin scroll-container($direction: 'y') { @if $direction == 'y' { overflow-y: auto; overflow-x: hidden; } @else { overflow-x: auto; overflow-y: hidden; } -webkit-overflow-scrolling: touch; // Custom scrollbar (tactical dark) &::-webkit-scrollbar { width: 6px; height: 6px; } &::-webkit-scrollbar-track { background: var(--cc-surface-dark); } &::-webkit-scrollbar-thumb { background: var(--cc-surface-lighter); border-radius: 3px; &:hover { background: var(--cc-on-surface-variant); } } } // --------------------------------------------------------------------------- // Page container // Standard page padding and layout // --------------------------------------------------------------------------- @mixin page-container { padding: $spacing-section; min-height: 400px; overflow-x: hidden; } // --------------------------------------------------------------------------- // Transition helpers // --------------------------------------------------------------------------- @mixin transition-colors($duration: $duration-short) { transition: color #{$duration} $easing-standard, background-color #{$duration} $easing-standard, border-color #{$duration} $easing-standard; } @mixin transition-transform($duration: $duration-medium) { transition: transform #{$duration} $easing-standard; } @mixin transition-opacity($duration: $duration-short) { transition: opacity #{$duration} $easing-standard; } // --------------------------------------------------------------------------- // Reduced motion // Wraps content in a reduced-motion media query. // --------------------------------------------------------------------------- @mixin reduced-motion { @media (prefers-reduced-motion: reduce) { @content; } }