Files
openclaw/ui/src/styles/layout.css
Val Alexander 1b1c11f6b8 feat(ui): add config file opening functionality and enhance styles
- Implemented a new handler to open the configuration file using the default application based on the operating system.
- Updated various CSS styles across components for improved visual consistency and usability, including adjustments to padding, margins, and font sizes.
- Introduced new styles for the data table and sidebar components to enhance layout and interaction.
- Added tests for the collapsed navigation rail to ensure proper functionality in different states.
2026-03-12 06:11:46 -05:00

1030 lines
19 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ===========================================
Shell Layout
=========================================== */
.shell {
--shell-pad: 16px;
--shell-gap: 16px;
--shell-nav-width: 220px;
--shell-nav-rail-width: 72px;
--shell-topbar-height: 52px;
--shell-focus-duration: 200ms;
--shell-focus-ease: var(--ease-out);
height: 100vh;
display: grid;
grid-template-columns: var(--shell-nav-width) minmax(0, 1fr);
grid-template-rows: var(--shell-topbar-height) 1fr;
grid-template-areas:
"topbar topbar"
"nav content";
gap: 0;
animation: dashboard-enter 0.3s var(--ease-out);
transition: grid-template-columns var(--shell-focus-duration) var(--shell-focus-ease);
overflow: hidden;
}
@supports (height: 100dvh) {
.shell {
height: 100dvh;
}
}
.shell--chat {
min-height: 100vh;
height: 100vh;
overflow: hidden;
}
@supports (height: 100dvh) {
.shell--chat {
height: 100dvh;
}
}
.shell--nav-collapsed {
grid-template-columns: var(--shell-nav-rail-width) minmax(0, 1fr);
}
.shell--chat-focus {
grid-template-columns: 0px minmax(0, 1fr);
}
.shell--onboarding {
grid-template-rows: 0 1fr;
}
.shell--onboarding .topbar {
display: none;
}
.shell--onboarding .content {
padding-top: 0;
}
.shell--chat-focus .content {
padding-top: 0;
}
.shell--chat-focus .content > * + * {
margin-top: 0;
}
/* ===========================================
Topbar
=========================================== */
.topbar {
grid-area: topbar;
position: sticky;
top: 0;
z-index: 40;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
padding: 0 20px;
height: var(--shell-topbar-height);
border-bottom: 1px solid var(--border);
background: color-mix(in srgb, var(--bg) 85%, transparent);
backdrop-filter: blur(12px) saturate(1.6);
-webkit-backdrop-filter: blur(12px) saturate(1.6);
}
.topbar-left {
display: flex;
align-items: center;
gap: 12px;
}
.topbar .nav-collapse-toggle {
width: 36px;
height: 36px;
margin-bottom: 0;
}
.topbar .nav-collapse-toggle__icon {
width: 20px;
height: 20px;
}
.topbar .nav-collapse-toggle__icon svg {
width: 20px;
height: 20px;
}
/* Brand */
.brand {
display: flex;
align-items: center;
gap: 8px;
}
.brand-logo {
width: 26px;
height: 26px;
flex-shrink: 0;
}
.brand-logo img {
width: 100%;
height: 100%;
object-fit: contain;
}
.brand-text {
display: flex;
flex-direction: column;
gap: 0;
}
.brand-title {
font-size: 15px;
font-weight: 700;
letter-spacing: -0.03em;
line-height: 1.1;
color: var(--text-strong);
}
.brand-sub {
font-size: 9px;
font-weight: 500;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
line-height: 1;
}
/* Topbar status */
.topbar-status {
display: flex;
align-items: center;
gap: 8px;
}
.topbar-status .pill {
padding: 6px 10px;
gap: 6px;
font-size: 12px;
font-weight: 500;
height: 32px;
box-sizing: border-box;
}
.topbar-status .pill .mono {
display: flex;
align-items: center;
line-height: 1;
margin-top: 0px;
}
.topbar-status .statusDot {
width: 6px;
height: 6px;
}
.topbar-status .theme-orb__trigger {
width: 26px;
height: 26px;
font-size: 13px;
}
/* Topbar search trigger */
.topbar-search {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 7px 12px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--bg-elevated);
color: var(--muted);
font-size: 13px;
cursor: pointer;
transition:
border-color var(--duration-fast) ease,
background var(--duration-fast) ease,
color var(--duration-fast) ease;
min-width: 180px;
}
.topbar-search:hover {
border-color: var(--border-strong);
background: var(--bg-hover);
color: var(--text);
}
.topbar-search:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.topbar-search__label {
flex: 1;
text-align: left;
}
.topbar-search__kbd {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg);
font-family: var(--mono);
font-size: 11px;
line-height: 1;
color: var(--muted);
}
.topbar-theme-mode {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 3px;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: color-mix(in srgb, var(--bg-elevated) 70%, transparent);
}
.topbar-theme-mode__btn {
width: 30px;
height: 30px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 1px solid transparent;
border-radius: calc(var(--radius-md) - 1px);
background: transparent;
color: var(--muted);
cursor: pointer;
transition:
color var(--duration-fast) ease,
background var(--duration-fast) ease,
border-color var(--duration-fast) ease;
}
.topbar-theme-mode__btn:hover {
color: var(--text);
background: var(--bg-hover);
}
.topbar-theme-mode__btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
.topbar-theme-mode__btn--active {
color: var(--accent);
background: var(--accent-subtle);
border-color: color-mix(in srgb, var(--accent) 25%, transparent);
}
.topbar-theme-mode__btn svg {
width: 14px;
height: 14px;
stroke: currentColor;
fill: none;
stroke-width: 1.75px;
stroke-linecap: round;
stroke-linejoin: round;
}
/* ===========================================
Navigation Sidebar (shadcn-inspired)
=========================================== */
/* Sidebar wrapper occupies the "nav" grid area */
.shell-nav {
grid-area: nav;
display: flex;
min-height: 0;
overflow: hidden;
transition: width var(--shell-focus-duration) var(--shell-focus-ease);
}
/* The sidebar panel itself */
.sidebar {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
min-width: 0;
overflow: hidden;
background: var(--bg);
}
:root[data-theme="light"] .sidebar {
background: var(--panel);
}
/* Collapsed: icon-only rail */
.sidebar--collapsed {
width: var(--shell-nav-rail-width);
min-width: var(--shell-nav-rail-width);
flex: 0 0 var(--shell-nav-rail-width);
}
/* Header: brand + collapse toggle */
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 14px 14px 10px;
flex-shrink: 0;
}
.sidebar--collapsed .sidebar-header {
justify-content: center;
padding: 14px 10px 10px;
}
/* Brand lockup */
.sidebar-brand {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.sidebar-brand__logo {
width: 22px;
height: 22px;
flex-shrink: 0;
border-radius: 6px;
}
.sidebar-brand__title {
font-size: 14px;
font-weight: 700;
letter-spacing: -0.025em;
color: var(--text-strong);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Scrollable nav body */
.sidebar-nav {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 4px 8px;
scrollbar-width: none;
}
.sidebar-nav::-webkit-scrollbar {
display: none;
}
.sidebar--collapsed .sidebar-nav {
padding: 6px 8px 10px;
display: grid;
gap: 12px;
}
/* Collapsed sidebar: centre icons, hide text */
.sidebar--collapsed .nav-group__label {
display: none;
}
.sidebar--collapsed .nav-group {
gap: 8px;
margin-bottom: 14px;
}
.sidebar--collapsed .nav-item {
justify-content: center;
width: 44px;
height: 44px;
padding: 0;
margin: 0 auto;
border-radius: 14px;
}
.sidebar--collapsed .nav-item__icon {
width: 18px;
height: 18px;
opacity: 0.78;
}
.sidebar--collapsed .nav-item__icon svg {
width: 18px;
height: 18px;
}
.sidebar--collapsed .nav-item__text {
display: none;
}
.sidebar--collapsed .nav-item__external-icon {
display: none;
}
/* Footer: docs link + version */
.sidebar-footer {
flex-shrink: 0;
padding: 8px;
border-top: 1px solid var(--border);
}
.sidebar--collapsed .sidebar-footer {
padding: 12px 8px 10px;
}
.sidebar-footer__docs-block {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.sidebar--collapsed .sidebar-footer__docs-block {
align-items: center;
gap: 10px;
}
.sidebar--collapsed .sidebar-footer .nav-item {
justify-content: center;
width: 44px;
height: 44px;
padding: 0;
}
.sidebar-version {
display: flex;
align-items: center;
justify-content: center;
padding: 4px 10px;
}
.sidebar-version__text {
font-size: 11px;
color: var(--muted);
font-weight: 500;
letter-spacing: 0.02em;
}
.sidebar-version__dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: color-mix(in srgb, var(--accent) 78%, white 22%);
box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent);
opacity: 1;
margin: 0 auto;
}
/* Drag-to-resize handle */
.sidebar-resizer {
width: 3px;
cursor: col-resize;
flex-shrink: 0;
background: transparent;
transition: background var(--duration-fast) ease;
position: relative;
}
.sidebar-resizer::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 3px;
background: transparent;
transition: background var(--duration-fast) ease;
}
.sidebar-resizer:hover::after {
background: var(--accent);
opacity: 0.35;
}
.sidebar-resizer:active::after {
background: var(--accent);
opacity: 0.6;
}
/* Shell-level collapsed / focus overrides */
.shell--nav-collapsed .shell-nav {
width: var(--shell-nav-rail-width);
min-width: var(--shell-nav-rail-width);
}
.shell--chat-focus .shell-nav {
width: 0;
min-width: 0;
overflow: hidden;
pointer-events: none;
opacity: 0;
}
/* Legacy .nav kept for mobile breakpoints */
.nav {
grid-area: nav;
overflow-y: auto;
overflow-x: hidden;
padding: 12px 10px;
background: var(--bg);
border-right: 1px solid var(--border);
scrollbar-width: none;
transition:
width var(--shell-focus-duration) var(--shell-focus-ease),
padding var(--shell-focus-duration) var(--shell-focus-ease),
opacity var(--shell-focus-duration) var(--shell-focus-ease);
min-height: 0;
}
.nav::-webkit-scrollbar {
display: none;
}
.shell--chat-focus .nav {
width: 0;
padding: 0;
border-width: 0;
overflow: hidden;
pointer-events: none;
opacity: 0;
}
.nav--collapsed {
width: 0;
min-width: 0;
padding: 0;
overflow: hidden;
border: none;
opacity: 0;
pointer-events: none;
}
/* Nav collapse toggle */
.nav-collapse-toggle {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: 1px solid transparent;
border-radius: var(--radius-sm);
cursor: pointer;
transition:
background var(--duration-fast) ease,
border-color var(--duration-fast) ease,
color var(--duration-fast) ease;
margin-bottom: 0;
color: var(--muted);
}
.nav-collapse-toggle:hover {
background: var(--bg-hover);
color: var(--text);
}
.nav-collapse-toggle__icon {
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
color: inherit;
}
.nav-collapse-toggle__icon svg {
width: 16px;
height: 16px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-collapse-toggle:hover .nav-collapse-toggle__icon {
color: inherit;
}
/* Nav groups */
.nav-group {
margin-bottom: 16px;
display: grid;
gap: 1px;
}
.nav-group:last-child {
margin-bottom: 0;
}
.nav-group__items {
display: grid;
gap: 1px;
}
.nav-group--collapsed .nav-group__items {
display: none;
}
/* Nav label */
.nav-label,
.nav-group__label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
padding: 5px 10px;
font-size: 10px;
font-weight: 600;
color: var(--muted);
margin-bottom: 2px;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
text-transform: uppercase;
letter-spacing: 0.06em;
border-radius: var(--radius-sm);
transition:
color var(--duration-fast) ease,
background var(--duration-fast) ease;
}
.nav-label:hover,
.nav-group__label:hover {
color: var(--text);
background: var(--bg-hover);
}
.nav-label--static,
.nav-group__label--static {
cursor: default;
}
.nav-label--static:hover,
.nav-group__label--static:hover {
color: var(--muted);
background: transparent;
}
.nav-label__text,
.nav-group__label-text {
flex: 1;
}
.nav-label__chevron,
.nav-group__chevron {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 10px;
opacity: 0.5;
transition: transform var(--duration-fast) ease;
}
.nav-label__chevron svg,
.nav-group__chevron svg {
width: 12px;
height: 12px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-group--collapsed .nav-label__chevron,
.nav-group--collapsed .nav-group__chevron {
transform: rotate(-90deg);
}
/* Nav items */
.nav-item {
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 8px;
padding: 7px 10px;
border-radius: var(--radius-md);
border: 1px solid transparent;
background: transparent;
color: var(--muted);
cursor: pointer;
text-decoration: none;
transition:
border-color var(--duration-fast) ease,
background var(--duration-fast) ease,
color var(--duration-fast) ease;
}
.nav-item__icon {
width: 15px;
height: 15px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
opacity: 0.6;
transition: opacity var(--duration-fast) ease;
}
.nav-item__icon svg {
width: 15px;
height: 15px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-item__text {
font-size: 13px;
font-weight: 450;
white-space: nowrap;
}
.nav-item:hover {
color: var(--text);
background: var(--bg-hover);
text-decoration: none;
}
.nav-item:hover .nav-item__icon {
opacity: 0.9;
}
.nav-item.active,
.nav-item--active {
color: var(--text-strong);
background: var(--accent-subtle);
border-color: color-mix(in srgb, var(--accent) 15%, transparent);
}
.nav-item.active .nav-item__icon,
.nav-item--active .nav-item__icon {
opacity: 1;
color: var(--accent);
}
.sidebar--collapsed .nav-item--active::before,
.sidebar--collapsed .nav-item.active::before {
content: "";
position: absolute;
left: -10px;
top: 10px;
bottom: 10px;
width: 3px;
border-radius: 999px;
background: var(--accent);
}
.sidebar--collapsed .nav-collapse-toggle {
width: 44px;
height: 44px;
margin-bottom: 0;
border-color: var(--border);
border-radius: 14px;
background: color-mix(in srgb, var(--bg-elevated) 86%, transparent);
}
.sidebar--collapsed .nav-collapse-toggle:hover {
border-color: color-mix(in srgb, var(--border-strong) 72%, transparent);
}
.nav-item__external-icon {
width: 12px;
height: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-left: auto;
opacity: 0;
transition: opacity var(--duration-fast) ease;
}
.nav-item__external-icon svg {
width: 12px;
height: 12px;
stroke: currentColor;
fill: none;
stroke-width: 1.5px;
stroke-linecap: round;
stroke-linejoin: round;
}
.nav-item:hover .nav-item__external-icon {
opacity: 0.5;
}
/* ===========================================
Content Area
=========================================== */
.content {
grid-area: content;
padding: 16px 20px 32px;
display: block;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
}
.content > * + * {
margin-top: 20px;
}
:root[data-theme="light"] .content {
background: var(--bg-content);
}
.content--chat {
display: flex;
flex-direction: column;
gap: 24px;
overflow: hidden;
padding-bottom: 0;
}
.content--chat > * + * {
margin-top: 0;
}
/* Content header */
.content-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 16px;
padding: 4px 8px;
overflow: hidden;
transform-origin: top center;
transition:
opacity var(--shell-focus-duration) var(--shell-focus-ease),
transform var(--shell-focus-duration) var(--shell-focus-ease),
max-height var(--shell-focus-duration) var(--shell-focus-ease),
padding var(--shell-focus-duration) var(--shell-focus-ease);
max-height: 80px;
}
.shell--chat-focus .content-header {
opacity: 0;
transform: translateY(-8px);
max-height: 0px;
padding: 0;
pointer-events: none;
}
.page-title {
font-size: 22px;
font-weight: 650;
letter-spacing: -0.03em;
line-height: 1.2;
color: var(--text-strong);
}
.page-sub {
color: var(--muted);
font-size: 13px;
font-weight: 400;
margin-top: 4px;
letter-spacing: -0.005em;
}
.page-meta {
display: flex;
gap: 8px;
}
/* Chat view header adjustments */
.content--chat .content-header {
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.content--chat .content-header > div:first-child {
text-align: left;
}
.content--chat .page-meta {
justify-content: flex-start;
}
.content--chat .chat-controls {
flex-shrink: 0;
}
/* ===========================================
Grid Utilities
=========================================== */
.grid {
display: grid;
gap: 20px;
}
.grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.stat-grid {
display: grid;
gap: 14px;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.note-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.row {
display: flex;
gap: 12px;
align-items: center;
}
.stack {
display: grid;
gap: 12px;
grid-template-columns: minmax(0, 1fr);
}
.filters {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
/* ===========================================
Responsive - Tablet
=========================================== */
@media (max-width: 1100px) {
.shell {
--shell-pad: 12px;
--shell-gap: 12px;
grid-template-columns: 1fr;
grid-template-rows: auto auto 1fr;
grid-template-areas:
"topbar"
"nav"
"content";
}
.nav {
position: static;
max-height: none;
display: flex;
gap: 6px;
overflow-x: auto;
border-right: none;
border-bottom: 1px solid var(--border);
padding: 10px 14px;
background: var(--bg);
}
.nav-group {
grid-auto-flow: column;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
margin-bottom: 0;
}
.grid-cols-2,
.grid-cols-3 {
grid-template-columns: 1fr;
}
.topbar {
position: static;
padding: 12px 14px;
gap: 10px;
}
.topbar-status {
flex-wrap: wrap;
}
.table-head,
.table-row {
grid-template-columns: 1fr;
}
.list-item {
grid-template-columns: 1fr;
}
}