/** * EcoOS UI Design System * Based on dees-catalog design patterns */ import { css } from '@design.estate/dees-element'; export const sharedStyles = css` :host { /* Colors - dees-catalog theme (HSL) */ --bg: hsl(0 0% 3.9%); --bg-elevated: hsl(0 0% 7.8%); --bg-hover: hsl(0 0% 14.9%); --border: hsl(0 0% 14.9%); --border-hover: hsl(0 0% 20.9%); --text: hsl(0 0% 95%); --text-secondary: hsl(215 20.2% 55.1%); --text-tertiary: hsl(215 20.2% 45%); /* Semantic colors */ --accent: hsl(217.2 91.2% 59.8%); --success: hsl(142.1 76.2% 36.3%); --warning: hsl(45.4 93.4% 47.5%); --error: hsl(0 84.2% 60.2%); /* Typography scale */ --text-xs: 11px; --text-sm: 12px; --text-base: 13px; --text-lg: 15px; --text-xl: 18px; --text-2xl: 24px; display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: var(--text-base); color: var(--text); line-height: 1.5; -webkit-font-smoothing: antialiased; } /* Monospace utility */ .mono { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; } /* Section - lightweight container */ .section { padding: 12px 0; border-bottom: 1px solid var(--border); } .section:last-child { border-bottom: none; } .section-title { font-size: var(--text-xs); font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-tertiary); margin-bottom: 8px; } /* Card - minimal styling */ .card { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 6px; padding: 12px; } /* Table styling */ .table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); } .table th { text-align: left; font-size: var(--text-xs); font-weight: 500; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-tertiary); padding: 6px 8px; border-bottom: 1px solid var(--border); } .table td { padding: 8px; border-bottom: 1px solid var(--border); vertical-align: middle; } .table tr:last-child td { border-bottom: none; } .table tr:hover td { background: var(--bg-hover); } .table .mono { font-size: var(--text-xs); color: var(--text-secondary); } /* Status dot */ .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--text-tertiary); } .dot.success { background: var(--success); } .dot.warning { background: var(--warning); } .dot.error { background: var(--error); } .dot.accent { background: var(--accent); } .dot.pulse { animation: pulse 2s ease-in-out infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } /* Status with dot and text */ .status { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); } .status.success { color: var(--success); } .status.warning { color: var(--warning); } .status.error { color: var(--error); } /* Progress bar - thin */ .progress { height: 3px; background: var(--border); border-radius: 1.5px; overflow: hidden; } .progress-bar { height: 100%; background: var(--accent); transition: width 300ms ease; } .progress-bar.success { background: var(--success); } .progress-bar.warning { background: var(--warning); } .progress-bar.error { background: var(--error); } /* Badge - compact */ .badge { display: inline-flex; align-items: center; font-size: var(--text-xs); font-weight: 500; padding: 2px 6px; border-radius: 3px; background: var(--border); color: var(--text-secondary); } .badge.primary { background: var(--accent); color: #fff; } .badge.success { background: rgba(12, 206, 107, 0.15); color: var(--success); } .badge.warning { background: rgba(245, 166, 35, 0.15); color: var(--warning); } .badge.error { background: rgba(238, 0, 0, 0.15); color: var(--error); } /* Data row - key value pair */ .data-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; } .data-row + .data-row { border-top: 1px solid var(--border); } .data-label { font-size: var(--text-sm); color: var(--text-secondary); } .data-value { font-family: 'SF Mono', monospace; font-size: var(--text-sm); color: var(--text); } /* Stat - large value display */ .stat { display: flex; flex-direction: column; } .stat-label { font-size: var(--text-xs); color: var(--text-tertiary); margin-bottom: 2px; } .stat-value { font-size: var(--text-2xl); font-weight: 600; font-family: 'SF Mono', monospace; letter-spacing: -0.02em; color: var(--text); } .stat-value.sm { font-size: var(--text-lg); } /* Grid layouts */ .grid { display: grid; gap: 12px; } .grid-2 { grid-template-columns: repeat(2, 1fr); } .grid-3 { grid-template-columns: repeat(3, 1fr); } .grid-auto { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } /* Flex utilities */ .flex { display: flex; align-items: center; } .flex-between { display: flex; align-items: center; justify-content: space-between; } .gap-4 { gap: 4px; } .gap-6 { gap: 6px; } .gap-8 { gap: 8px; } .gap-12 { gap: 12px; } /* Tabs - underline style */ .tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); } .tab { padding: 8px 12px; font-size: var(--text-sm); font-weight: 500; color: var(--text-tertiary); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; transition: color 150ms ease; } .tab:hover { color: var(--text); } .tab.active { color: var(--text); border-bottom-color: var(--text); } /* Empty state */ .empty { padding: 24px; text-align: center; color: var(--text-tertiary); font-size: var(--text-sm); } /* Actions row */ .actions { display: flex; gap: 8px; margin-top: 12px; } /* Collapsible details */ details summary { cursor: pointer; padding: 8px 0; font-size: var(--text-sm); color: var(--text-secondary); list-style: none; display: flex; align-items: center; gap: 6px; } details summary::-webkit-details-marker { display: none; } details summary::before { content: '▶'; font-size: 8px; transition: transform 150ms ease; } details[open] summary::before { transform: rotate(90deg); } details summary:hover { color: var(--text); } .details-content { padding: 8px 0 8px 14px; } /* Alert/Banner - slim */ .banner { display: flex; align-items: center; gap: 8px; padding: 8px 12px; font-size: var(--text-sm); border-radius: 4px; background: rgba(0, 112, 243, 0.1); border: 1px solid rgba(0, 112, 243, 0.2); color: var(--text); } .banner.success { background: rgba(12, 206, 107, 0.1); border-color: rgba(12, 206, 107, 0.2); } .banner.warning { background: rgba(245, 166, 35, 0.1); border-color: rgba(245, 166, 35, 0.2); } .banner.error { background: rgba(238, 0, 0, 0.1); border-color: rgba(238, 0, 0, 0.2); } /* Scrollbar - minimal */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--border-hover); } /* Text utilities */ .text-xs { font-size: var(--text-xs); } .text-sm { font-size: var(--text-sm); } .text-base { font-size: var(--text-base); } .text-lg { font-size: var(--text-lg); } .text-secondary { color: var(--text-secondary); } .text-tertiary { color: var(--text-tertiary); } `; /** * Format bytes to human readable string */ export function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } /** * Format uptime seconds to human readable string */ export function formatUptime(seconds: number): string { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const mins = Math.floor((seconds % 3600) / 60); if (days > 0) return `${days}d ${hours}h ${mins}m`; if (hours > 0) return `${hours}h ${mins}m`; return `${mins}m`; } /** * Format age in hours to human readable string */ export function formatAge(hours: number): string { if (hours < 1) return `${Math.round(hours * 60)}m ago`; if (hours < 24) return `${Math.round(hours)}h ago`; return `${Math.round(hours / 24)}d ago`; }