// ============================================================================= // Styles for AppLayout — the shell (sidebar + content area) wrapping every // authenticated page. Colocated next to AppLayout.tsx since nothing else // uses these classes. // ============================================================================= // No `@use` of the theme partial needed here — see HomePage.scss's identical // note: every token below is a CSS custom property, available at runtime. .app-layout { min-height: 100vh; display: flex; background: var(--color-background); } // Fixed-width nav rail. Its own surface (not the page background), same // elevation language as a card, so it reads as a distinct, permanent piece // of chrome rather than part of the scrolling content. `width` transitions // so collapsing/expanding (see `.collapsed` below) reads as a deliberate // motion rather than an abrupt layout jump. .app-sidebar { display: flex; flex-direction: column; flex-shrink: 0; width: 15rem; padding: var(--space-lg) var(--space-md); background: var(--color-surface); border-right: 1px solid var(--color-border); transition: width 0.15s ease; &__top { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--space-sm) var(--space-xl); } &__brand { display: flex; align-items: center; font-family: var(--font-display); font-weight: 700; font-size: var(--font-size-lg); color: var(--color-primary); overflow: hidden; white-space: nowrap; } // Full wordmark by default; swapped for a short monogram when collapsed // (see `.collapsed &__brand-full`/`&__brand-mark` below) — clearer than // an arbitrary icon standing in for the brand itself. &__brand-mark { display: none; } &__collapse-toggle { width: 1.75rem; height: 1.75rem; flex-shrink: 0; display: grid; place-items: center; border: 1px solid var(--color-border); border-radius: var(--radius-base); background: var(--color-surface); color: var(--color-text-muted); cursor: pointer; svg { width: 1rem; height: 1rem; transition: transform 0.15s ease; } &:hover { background: var(--color-surface-alt); color: var(--color-text); } } &__nav { display: flex; flex: 1; flex-direction: column; gap: var(--space-xs); a { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-sm); border-radius: var(--radius-base); color: var(--color-text); font-weight: 600; font-size: var(--font-size-sm); text-decoration: none; white-space: nowrap; overflow: hidden; svg { flex-shrink: 0; width: 1.25rem; height: 1.25rem; } &:hover { background: var(--color-surface-alt); } // Applied by NavLink itself (see the `className` prop in // AppLayout.tsx), not a router-provided class — plain `.active`. &.active { background: var(--color-primary); color: #fff; } } } // --- "Paramètres" collapsible menu ------------------------------------- &__settings { padding-top: var(--space-md); border-top: 1px solid var(--color-border); } &__settings-toggle { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: var(--space-sm); font-family: var(--font-body); font-size: var(--font-size-sm); font-weight: 600; text-align: left; color: var(--color-text); background: none; border: none; border-radius: var(--radius-base); cursor: pointer; &:hover { background: var(--color-surface-alt); } } &__settings-toggle-left { display: flex; align-items: center; gap: var(--space-sm); overflow: hidden; svg { flex-shrink: 0; width: 1.25rem; height: 1.25rem; } } &__settings-nav { margin-top: var(--space-xs); } // --- Account menu (bottom of the sidebar) ------------------------------- &__footer { position: relative; padding-top: var(--space-md); border-top: 1px solid var(--color-border); } &__account-toggle { display: flex; align-items: center; gap: var(--space-sm); width: 100%; padding: 0.4rem var(--space-sm); font-family: var(--font-body); font-size: var(--font-size-sm); font-weight: 600; text-align: left; cursor: pointer; border-radius: var(--radius-base); border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text); overflow: hidden; white-space: nowrap; &:hover { background: var(--color-surface-alt); } } &__avatar { flex-shrink: 0; width: 1.5rem; height: 1.5rem; display: grid; place-items: center; border-radius: 50%; background: var(--color-primary); color: #fff; font-size: var(--font-size-xs); font-weight: 700; } // Anchored just above the toggle rather than inline in the flow — it's a // transient overlay, not part of the sidebar's permanent layout. &__account-menu { position: absolute; bottom: calc(100% + var(--space-xs)); left: 0; right: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-base); box-shadow: var(--shadow-md); a, button { padding: var(--space-sm); font-family: var(--font-body); font-size: var(--font-size-sm); font-weight: 600; text-align: left; text-decoration: none; color: var(--color-text); background: none; border: none; cursor: pointer; white-space: nowrap; &:hover { background: var(--color-surface-alt); } } } // --- Collapsed (icon-only rail) state ----------------------------------- // A single class toggle on the root element — every nested rule below // just hides labels/chevrons and re-centers icons via CSS, no child // component needs to know the sidebar is collapsed. &.collapsed { width: 4.25rem; padding-left: var(--space-sm); padding-right: var(--space-sm); .app-sidebar__top { flex-direction: column; gap: var(--space-sm); padding-left: 0; padding-right: 0; } .app-sidebar__brand-full { display: none; } .app-sidebar__brand-mark { display: block; } .app-sidebar__collapse-toggle svg { transform: rotate(180deg); } .app-sidebar__nav a, .app-sidebar__settings-toggle, .app-sidebar__account-toggle { justify-content: center; padding-left: 0; padding-right: 0; } .app-sidebar__settings-toggle-left { gap: 0; } .label, .chevron { display: none; } // The popover would otherwise shrink to the icon rail's own width, // squashing "Mon compte"/"Se déconnecter" — give it a normal, // comfortable width instead, still anchored to the rail's left edge. .app-sidebar__account-menu { left: 0; right: auto; width: 12rem; } } } .app-content { flex: 1; // Content can scroll independently of the sidebar (e.g. a long planning // table) without the fixed-width rail ever needing to shrink. min-width: 0; padding: var(--space-xl); } // Below this width the fixed-width rail would eat too much of the // viewport (relevant early given this app is meant to be embedded via // Capacitor later, see the root README) — collapse it into a horizontal // top bar instead of a side rail. @media (max-width: 640px) { .app-layout { flex-direction: column; } .app-sidebar { flex-direction: row; align-items: center; width: 100%; padding: var(--space-sm) var(--space-md); border-right: none; border-bottom: 1px solid var(--color-border); &__top { padding: 0 var(--space-sm) 0 0; } &__brand { padding: 0; } &__nav { flex-direction: row; overflow-x: auto; } &__settings, &__footer { padding-top: 0; border-top: none; } } }