Update layout
Sidebar now animates properly. Navigation has progressive blur behind it on scroll
This commit is contained in:
parent
06a91bd532
commit
b564a5e5e0
4 changed files with 134 additions and 90 deletions
|
|
@ -21,7 +21,7 @@
|
||||||
const { open = false, title, onclose, children, headerActions }: Props = $props()
|
const { open = false, title, onclose, children, headerActions }: Props = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<aside class="sidebar" class:open style:--sidebar-width={open ? SIDEBAR_WIDTH : '0'}>
|
<aside class="sidebar" class:open style:--sidebar-width={SIDEBAR_WIDTH}>
|
||||||
{#if title}
|
{#if title}
|
||||||
<SidebarHeader {title} {onclose} actions={headerActions} />
|
<SidebarHeader {title} {onclose} actions={headerActions} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -50,42 +50,56 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 0;
|
width: var(--sidebar-width);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: width $duration-slide ease-in-out;
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
transition:
|
||||||
|
transform $duration-slide ease-in-out,
|
||||||
|
opacity $duration-slide ease-in-out;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
width: var(--sidebar-width);
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-content {
|
.sidebar-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
padding: $unit-2x;
|
|
||||||
|
|
||||||
// Smooth scrolling
|
// Smooth scrolling
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
// Better scrollbar styling to match main content
|
// Use overlay scrollbars that auto-hide
|
||||||
|
overflow-y: overlay;
|
||||||
|
|
||||||
|
// Thin, minimal scrollbar styling
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background: var(--bg-secondary, #f1f1f1);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: var(--border-primary, #888);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 10px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--text-secondary, #555);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firefox scrollbar styling
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||||
|
|
||||||
// Improve mobile scrolling performance
|
// Improve mobile scrolling performance
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
@ -95,17 +109,10 @@
|
||||||
// Mobile styles - overlay approach
|
// Mobile styles - overlay approach
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
transform: translateX(100%);
|
|
||||||
transition:
|
|
||||||
transform $duration-slide ease-in-out,
|
|
||||||
width 0s;
|
|
||||||
width: 90vw !important;
|
width: 90vw !important;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
// Mobile already uses transform, no additional changes needed
|
||||||
&.open {
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -18,55 +18,71 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sidebar-header">
|
<div class="sidebar-header">
|
||||||
|
<div class="header-left">
|
||||||
|
{#if actions}
|
||||||
|
{@render actions()}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2 class="sidebar-title">{title}</h2>
|
<h2 class="sidebar-title">{title}</h2>
|
||||||
|
|
||||||
{#if actions}
|
<div class="header-right">
|
||||||
<div class="header-actions">
|
{#if onclose}
|
||||||
{@render actions()}
|
<button onclick={onclose} class="close-button" aria-label="Close sidebar">
|
||||||
</div>
|
{@html closeIcon}
|
||||||
{/if}
|
</button>
|
||||||
|
{/if}
|
||||||
{#if onclose}
|
</div>
|
||||||
<button
|
|
||||||
onclick={onclose}
|
|
||||||
class="close-button"
|
|
||||||
aria-label="Close sidebar"
|
|
||||||
>
|
|
||||||
{@html closeIcon}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use '$src/themes/spacing' as *;
|
@use '$src/themes/spacing' as *;
|
||||||
@use '$src/themes/colors' as *;
|
@use '$src/themes/colors' as *;
|
||||||
@use '$src/themes/typography' as *;
|
@use '$src/themes/typography' as *;
|
||||||
|
@use '$src/themes/layout' as *;
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $unit;
|
justify-content: space-between;
|
||||||
|
min-height: $nav-height;
|
||||||
padding: $unit-2x;
|
padding: $unit-2x;
|
||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
|
|
||||||
.sidebar-title {
|
// Match mobile navigation height
|
||||||
flex: 1;
|
@media (max-width: 768px) {
|
||||||
margin: 0;
|
min-height: $nav-height-mobile;
|
||||||
font-size: $font-large;
|
|
||||||
font-weight: $bold;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-actions {
|
.header-left,
|
||||||
|
.header-right {
|
||||||
|
width: 32px; // Same width as close button for balance
|
||||||
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: $unit;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: $font-regular;
|
||||||
|
font-weight: $medium;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
margin-left: $unit;
|
|
||||||
padding: $unit;
|
padding: $unit;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -76,10 +92,11 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
transition: background-color 0.2s, color 0.2s;
|
transition:
|
||||||
|
background-color 0.2s,
|
||||||
|
color 0.2s;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
:global(svg) {
|
:global(svg) {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
|
|
@ -97,4 +114,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,11 @@
|
||||||
|
|
||||||
<Tooltip.Provider>
|
<Tooltip.Provider>
|
||||||
<div class="app-container" class:sidebar-open={sidebar.isOpen}>
|
<div class="app-container" class:sidebar-open={sidebar.isOpen}>
|
||||||
<div class="nav-wrapper">
|
|
||||||
<Navigation isAuthenticated={data?.isAuthenticated} username={data?.account?.username} role={data?.account?.role} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="main-pane">
|
<div class="main-pane">
|
||||||
|
<div class="nav-blur-background"></div>
|
||||||
|
<div class="main-navigation">
|
||||||
|
<Navigation isAuthenticated={data?.isAuthenticated} username={data?.account?.username} role={data?.account?.role} />
|
||||||
|
</div>
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -87,23 +87,26 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
// Fixed navigation wrapper with blur effect
|
// Main pane with content
|
||||||
.nav-wrapper {
|
.main-pane {
|
||||||
position: fixed;
|
flex: 1;
|
||||||
top: 0;
|
display: flex;
|
||||||
left: 0;
|
flex-direction: column;
|
||||||
right: 0;
|
min-width: 0;
|
||||||
z-index: 100;
|
transition: margin-right $duration-slide ease-in-out;
|
||||||
width: 100vw;
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
// Single blur layer with gradient mask for progressive effect
|
// Blur background that shifts with main pane
|
||||||
&::before {
|
.nav-blur-background {
|
||||||
content: '';
|
position: fixed;
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 80px; // Taller to test the progressive effect
|
height: 80px; // Taller to test the progressive effect
|
||||||
|
z-index: 1; // Lower z-index so scrollbar appears above
|
||||||
|
pointer-events: none;
|
||||||
|
transition: right $duration-slide ease-in-out;
|
||||||
|
|
||||||
// Color gradient for the background
|
// Color gradient for the background
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
|
|
@ -131,67 +134,80 @@
|
||||||
black 40%,
|
black 40%,
|
||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation content above the blur layer
|
// Navigation wrapper - fixed but shifts with main-pane
|
||||||
:global(nav) {
|
.main-navigation {
|
||||||
position: relative;
|
position: fixed;
|
||||||
z-index: 2;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10; // Above blur but below scrollbar
|
||||||
|
transition: right $duration-slide ease-in-out;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Main pane with content
|
// Main content area with independent scroll
|
||||||
.main-pane {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-width: 0;
|
|
||||||
transition: margin-right $duration-slide ease-in-out;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
// Main content area with independent scroll - content starts at top
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 56px; // Space for fixed navigation to match blur height
|
padding-top: 80px; // Space for fixed navigation (matching test height)
|
||||||
|
z-index: 2; // Ensure scrollbar is above blur background
|
||||||
|
|
||||||
// Smooth scrolling
|
// Smooth scrolling
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
|
||||||
// Better scrollbar styling
|
// Use overlay scrollbars that auto-hide on macOS
|
||||||
|
overflow-y: overlay;
|
||||||
|
|
||||||
|
// Thin, minimal scrollbar styling
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background: var(--bg-secondary, #f1f1f1);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: var(--border-primary, #888);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 10px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
background-clip: padding-box;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--text-secondary, #555);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firefox scrollbar styling
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// When sidebar is open, adjust main pane width
|
// When sidebar is open, adjust main pane and navigation
|
||||||
&.sidebar-open {
|
&.sidebar-open {
|
||||||
.main-pane {
|
.main-pane {
|
||||||
margin-right: var(--sidebar-width, 420px);
|
margin-right: var(--sidebar-width, 420px);
|
||||||
|
|
||||||
|
// Blur background and navigation shift with the main pane
|
||||||
|
.nav-blur-background,
|
||||||
|
.main-navigation {
|
||||||
|
right: var(--sidebar-width, 420px);
|
||||||
|
}
|
||||||
|
|
||||||
// Mobile: don't adjust margin, use overlay
|
// Mobile: don't adjust margin, use overlay
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|
||||||
|
.nav-blur-background,
|
||||||
|
.main-navigation {
|
||||||
|
right: 0; // Don't shift on mobile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
@use 'spacing';
|
@use 'spacing';
|
||||||
|
|
||||||
|
// Navigation and header heights
|
||||||
|
$nav-height: 76px;
|
||||||
|
$nav-height-mobile: 60px;
|
||||||
|
|
||||||
// Scale factors
|
// Scale factors
|
||||||
$scale-wide: scale(1.05, 1.05);
|
$scale-wide: scale(1.05, 1.05);
|
||||||
$scale-tall: scale(1.012, 1.012);
|
$scale-tall: scale(1.012, 1.012);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue