@use 'mixins' as *; // ---------- Layout shell ---------- .app-shell { display: flex; min-height: 100vh; } .app-main { flex: 1; min-width: 0; } // ---------- Sidebar (site navigation) ---------- .site-sidebar { position: sticky; top: 0; z-index: 60; flex-shrink: 0; display: flex; flex-direction: column; gap: 8px; width: var(--sidebar-w); height: 100vh; padding: 22px 18px; backdrop-filter: blur(14px); background: color-mix(in srgb, var(--bg) 78%, transparent); border-right: 1px solid var(--border); transition: width 0.22s ease, padding 0.22s ease; } .sidebar-nav { display: flex; flex-direction: column; align-items: stretch; gap: 6px; margin-top: 26px; .btn { justify-content: center; margin-top: 6px; } } .sidebar-footer { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; margin-top: auto; // the footer buttons read as nav rows — icon then label, centred, taking the // sidebar's width so they grow and shrink with it (both classes are grid, which // would drop the label under the icon, and the label shows on mobile too, so // the row layout lives outside the desktop block) .panel-icon-btn, .icon-btn { display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 0.86rem; font-weight: 550; white-space: nowrap; svg { flex-shrink: 0; } } } // The account row: who the wiki is being written as. It reads as one of the // sidebar's rows — an initial in a circle, the name while the sidebar has room // for it, and the way out at the end of the line. .account-row { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 10px; color: var(--muted); font-size: 0.86rem; font-weight: 550; white-space: nowrap; } .account-avatar { @include avatar(30px, 0.84rem); } // the row's avatar and name together form the button that opens the profile // popup; the exit button beside it stays its own .account-open { display: flex; align-items: center; justify-content: center; gap: 8px; flex: 1; min-width: 0; border-radius: 9px; text-align: left; &:hover .account-avatar { background: color-mix(in srgb, var(--accent) 24%, transparent); } &:hover .account-name { color: var(--accent); } } .account-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--text); } .account-exit { @include icon-target(24px, 7px); color: var(--faint); transition: color 0.15s, background-color 0.15s; &:hover { background: var(--surface-2); color: var(--danger); } } @media (max-width: 900px) { .app-shell { flex-direction: column; } .site-sidebar { width: 100%; height: auto; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; row-gap: 10px; padding: 12px clamp(16px, 4vw, 32px); border-right: 0; border-bottom: 1px solid var(--border); } .sidebar-nav { flex-direction: row; align-items: center; margin-top: 0; .btn { margin-top: 0; } } .sidebar-search { width: auto; min-width: 132px; max-width: 240px; } .sidebar-footer { margin-top: 0; flex-direction: row; align-items: center; } // the rail is a desktop affordance — the top bar is always expanded .sidebar-collapse-btn { display: none; } } // ---------- Collapsed sidebar: an icon rail (desktop only) ---------- .sidebar-collapse-btn svg { transition: transform 0.22s ease; } @media (min-width: 901px) { // desktop: stack above the reading surface (book layer, z 70/74) so the // book's edge shadow falls behind the nav instead of darkening it .site-sidebar { z-index: 75; } // on desktop they fill the sidebar's width instead of their own: the collapsing // sidebar relayouts them every frame, the rail drops their labels (.hide-collapsed) // and their fixed heights hold, so the icons ride the centre the whole way .sidebar-footer .panel-icon-btn, .sidebar-footer .icon-btn, .sidebar-footer .account-row { width: 100%; } // the root class also moves the fixed reading surface in step, since // --sidebar-w feeds the book layer's left inset html.sidebar-collapsed { --sidebar-w: 68px; } .site-sidebar.collapsed { padding-inline: 12px; .sidebar-collapse-btn svg { transform: rotate(180deg); } // with the labels gone, center what's left: the icons. The buttons keep // the height the labels used to give them (font-size x line-height 1.6 // + their vertical padding), so the rail doesn't shrink them .sidebar-search { justify-content: center; padding-inline: 0; min-height: 40px; // 9+9 padding + 13.76px x 1.6 label line } .nav-link { justify-content: center; padding-inline: 0; min-height: 39px; // 8+8 padding + 14.4px x 1.6 label line } .sidebar-nav .btn { min-height: 35px; // 7+7 padding + 13.12px x 1.6 label line } // the mark is all that's left of the wordmark, so it rides the rail's // centre like the icons beside it .logo { justify-content: center; } // the way out drops away with the labels, leaving just the avatar centred // on the rail; the avatar sets the row's height now, so the rail doesn't // shrink it .account-row { min-height: 44px; // 1+1 border + 6+6 padding + 30px avatar } } // collapsing drops the labels at once; expanding waits for the rail to // open before fading them back in, so they never render squished at // half width mid-transition .hide-collapsed { transition: opacity 0.16s ease 0.1s, display 0.26s allow-discrete; } @starting-style { .hide-collapsed { opacity: 0; } } html.sidebar-collapsed .hide-collapsed { display: none; transition: none; } } // ---------- "Open" articles: the count beside the button that shows them ---------- .sidebar-open.is-current { color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); } .tabs-count { margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px; display: inline-grid; place-items: center; border-radius: 9px; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); font-size: 0.68rem; font-weight: 700; font-variant-numeric: tabular-nums; } .logo { display: flex; align-items: center; gap: 11px; text-decoration: none; color: var(--text); font-weight: 800; font-size: 1.15rem; letter-spacing: -0.02em; white-space: nowrap; } // a 40px square — the height the nav rows settle at (the search bar's own // padding + label line), so the sigma reads as one of the sidebar's buttons .logo-mark { width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center; background: linear-gradient(135deg, var(--accent), var(--accent-2)); color: #fff; font-size: 1.4rem; font-weight: 700; box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--accent) 60%, transparent); } .logo-accent { @include gradient-text; } .nav-link { display: inline-flex; align-items: center; gap: 7px; padding: 8px 13px; border-radius: 9px; color: var(--muted); text-decoration: none; font-size: 0.9rem; font-weight: 550; @include hover-row; &.router-link-active { color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); } } // small icon button used in modal/popup headers .panel-icon-btn { @include icon-target(30px, 8px); color: var(--muted); @include hover-row; }