diff --git a/web/css/components/app-tabs.css b/web/css/components/app-tabs.css index acec9aa..a7a213f 100644 --- a/web/css/components/app-tabs.css +++ b/web/css/components/app-tabs.css @@ -1,77 +1,154 @@ @layer components { -.app-tabs { - display: flex; - flex-direction: column; - gap: var(--app-spacing); -} - -/* No-JS fallback: show only first tab panel. */ -html.no-js [data-tabs] [data-tab-panel] { - display: none; -} - -html.no-js [data-tabs] [data-tab-panel]:first-of-type { - display: block; -} - -/* JS mode: keep panels hidden until tab init completed (prevents flash). */ -html.js [data-tabs]:not([data-tabs-ready="1"]) [data-tab-panel] { - display: none !important; -} - -.app-tabs-nav { + .app-tabs { display: flex; + flex-direction: column; + gap: var(--app-spacing); + } + + /* No-JS fallback: show only first tab panel. */ + html.no-js [data-tabs] [data-tab-panel] { + display: none; + } + + html.no-js [data-tabs] [data-tab-panel]:first-of-type { + display: block; + } + + /* JS mode: keep panels hidden until tab init completed (prevents flash). */ + html.js [data-tabs]:not([data-tabs-ready="1"]) [data-tab-panel] { + display: none !important; + } + + /* ------------------------------------------------------------------ */ + /* Tab nav wrapper — holds optional chevron buttons + scrollable nav */ + /* ------------------------------------------------------------------ */ + .app-tabs-nav-wrap { + position: relative; + display: flex; + align-items: stretch; border-bottom: 1px solid var(--tabs-border); + } + + /* Chevron scroll buttons — styled like .outline.secondary.small */ + .app-tabs-nav-wrap > .app-tabs-chevron { + flex: 0 0 auto; + display: none; + align-items: center; + justify-content: center; + border: 1px solid var(--app-border); + border-radius: var(--app-border-radius); + background-color: transparent; + color: var(--app-secondary); + font-size: 13px; + line-height: 1; + cursor: pointer; + z-index: 1; + margin-block: auto; + transition: + background-color var(--app-transition), + border-color var(--app-transition), + color var(--app-transition); + } + + .app-tabs-nav-wrap > .app-tabs-chevron:hover, + .app-tabs-nav-wrap > .app-tabs-chevron:focus-visible { + border-color: var(--app-secondary-hover-border); + color: var(--app-secondary-hover); + } + + .app-tabs-nav-wrap > .app-tabs-chevron[disabled] { + opacity: 0.25; + cursor: default; + pointer-events: none; + } + + .app-tabs-nav-wrap > .app-tabs-chevron--start { + order: -1; + margin-inline-end: 4px; + } + + .app-tabs-nav-wrap > .app-tabs-chevron--end { + order: 1; + margin-inline-start: 4px; + } + + /* Show chevrons when overflow is detected */ + .app-tabs-nav-wrap.has-overflow > .app-tabs-chevron { + display: flex; + } + + /* ------------------------------------------------------------------ */ + /* The scrollable tab strip */ + /* ------------------------------------------------------------------ */ + .app-tabs-nav { + display: flex; + border-bottom: 1px solid var(--tabs-border); /* No-JS fallback border */ overflow-x: auto; -webkit-overflow-scrolling: touch; padding-block-start: 2px; padding-left: 2px; white-space: nowrap; -} + flex: 1 1 0%; + scroll-behavior: smooth; + } -.app-tabs-nav button, -.app-tabs-nav a { - background: transparent; - border: 0; - padding: 5px 10px 5px 10px; - color: var(--tabs-muted) !important; - cursor: pointer; - border-bottom: 2px solid transparent; - text-decoration: none; - border-radius: 0; - margin-bottom: 0; -} + /* When wrapped by JS, the wrapper owns the border */ + .app-tabs-nav-wrap > .app-tabs-nav { + border-bottom: none; + } -.app-tabs-nav button:hover, -.app-tabs-nav button:focus-visible, -.app-tabs-nav a:hover, -.app-tabs-nav a:focus-visible { - --app-color: inherit; - color: var(--app-color) !important; -} + /* When JS is active: hide the native scrollbar (chevrons take over) */ + html.js .app-tabs-nav { + scrollbar-width: none; /* Firefox */ + } -.app-tabs-nav button.is-active, -.app-tabs-nav a.is-active { - --app-color: inherit; - color: var(--app-color) !important; - border-color: var(--tabs-active); -} + html.js .app-tabs-nav::-webkit-scrollbar { + display: none; /* Chrome / Safari / Edge */ + } -.app-tabs-nav button.has-invalid, -.app-tabs-nav a.has-invalid { - color: var(--app-danger, #dc3545) !important; -} + .app-tabs-nav button, + .app-tabs-nav a { + background: transparent; + border: 0; + padding: 5px 10px 5px 10px; + color: var(--tabs-muted) !important; + cursor: pointer; + border-bottom: 2px solid transparent; + text-decoration: none; + border-radius: 0; + margin-bottom: 0; + } -[data-tab-panel][hidden] { - display: none; -} + .app-tabs-nav button:hover, + .app-tabs-nav button:focus-visible, + .app-tabs-nav a:hover, + .app-tabs-nav a:focus-visible { + --app-color: inherit; + color: var(--app-color) !important; + } -html.js [data-tabs][data-tabs-ready="1"] [data-tab-panel]:not([hidden]) { - display: block; -} + .app-tabs-nav button.is-active, + .app-tabs-nav a.is-active { + --app-color: inherit; + color: var(--app-color) !important; + border-color: var(--tabs-active); + } -/* Safety fallback when JS is available but the ready marker is missing. */ -html:not(.no-js) [data-tabs] [data-tab-panel]:not([hidden]) { - display: block; -} + .app-tabs-nav button.has-invalid, + .app-tabs-nav a.has-invalid { + color: var(--app-danger, #dc3545) !important; + } + + [data-tab-panel][hidden] { + display: none; + } + + html.js [data-tabs][data-tabs-ready="1"] [data-tab-panel]:not([hidden]) { + display: block; + } + + /* Safety fallback when JS is available but the ready marker is missing. */ + html:not(.no-js) [data-tabs] [data-tab-panel]:not([hidden]) { + display: block; + } } diff --git a/web/css/layout/app-sidebar.css b/web/css/layout/app-sidebar.css index f8d137c..47ddd65 100644 --- a/web/css/layout/app-sidebar.css +++ b/web/css/layout/app-sidebar.css @@ -226,6 +226,7 @@ padding: 0 var(--app-spacing); font-size: var(--app-sidebar-summary-size); color: var(--app-muted-color); + margin: 0; } nav#aside-panel-people { @@ -256,7 +257,7 @@ } .app-sidebar #aside-panel-admin .app-sidebar-admin-group { - margin-bottom: 5px; + margin-bottom: 15px; width: 100%; } diff --git a/web/js/components/app-tabs.js b/web/js/components/app-tabs.js index 8a4973f..073377f 100644 --- a/web/js/components/app-tabs.js +++ b/web/js/components/app-tabs.js @@ -1,10 +1,17 @@ /** - * Initializes tab navigation - * Usage: - * - Tab buttons: - * - Tab panels: