Here is a picture of the layout I want:
┌─────────┬───────────────────────┐
│ Sidebar │ Editor │
│ │ │
│ │ │
│ ◄─┼─► ▲ │
│ │ │ │
│ ├───────────────────┼───┤
│ │ Preview │ │
│ │ ▼ │
│ │ Table │
│ │ <overflow-scroll> │
│ │ │
└─────────┴───────────────────────┘
- The
whole page
shouldnot be scrollable
(more like a desktop app, 100vw +100vh). - The
Sidebar
should bevertically resizable
- The
Editor
should behorizontally resizable
- The
Preview
should take up the rest of the space.Overflow
should be managed viavertically + horizontally scrolling
.
Initial Problem
I was using flex box instruction here and there. While that worked in general for putting things where I want them to be, ultimately it lead to parts of the table being rendered outside of the visible and reachable area.
Naively I thought the browser knows what space is left for the table (because of 100vw +100vh
on html
) but that isn't true.
So i implemented the same layout in css grid
and flex box
(but this time a wholesome flex box which wraps the entire screen vertically and horizontally).
Flexbox approach
The code is in Svelte+Tailwind
. Didn't take the time to unwind that further.
<script lang="ts">
import Resizer from './Resizer.svelte';
let sidebarWidth = 256;
let topHeight = 312;
</script>
<svelte:head>
<script src="https://cdn.tailwindcss.com"></script>
</svelte:head>
<div class="wrapper">
<!-- SIDEBAR -->
<aside id="sidebar p-2" aria-label="Sidebar" class="sidebar p-2" style:--sidebar-width={sidebarWidth}>
<span class="font-bold text-xl">Sidebar:</span>
<span>
Lorem ipsum!
</span>
</aside>
<!-- SIDEBAR/MAIN DIVIDER -->
<div class="shrink-0 px-1">
<Resizer
size={sidebarWidth}
min={128}
max={512}
orientation="vertical"
onResize={(newSize) => (sidebarWidth = newSize)}
onResizeEnd={() => {}}
/>
</div>
<!-- MAIN -->
<div id="main" class="main">
<!-- TOP -->
<div id="top p-2 bg-green-100" class="mainTop" style:--top-height={topHeight}>
<span class="font-bold text-xl">Top:</span>
Lorem ipsum?
</div>
<!-- TOP/BOTTOM DIVIDER -->
<div class="shrink-0 py-2">
<Resizer
size={topHeight}
min={64}
max={5120}
orientation="horizontal"
onResize={(newSize) => (topHeight = newSize)}
onResizeEnd={() => {}}
/>
</div>
<!-- BOTTOM -->
<div id="Bottom" class="mainBottom">
<span class="font-bold text-xl">Bottom:</span>
Lorem ipsum.
</div>
</div>
</div>
<style>
:global(.html) {
height: 100vh;
width: 100vw;
}
.wrapper {
height: 100%;
width: 100%;
display: flex;
}
.sidebar {
width: calc(var(--sidebar-width) * 1px);
overflow: scroll;
flex-shrink: 0;
border: 1px solid yellow;
}
.main {
display: flex;
flex-direction: column;
min-width: 0;
}
.mainTop {
height: calc(var(--top-height) * 1px);
overflow: scroll;
border: 1px solid yellow;
}
.mainBottom {
flex: 1 1 0%;
overflow: scroll;
border: 1px solid yellow;
}
</style>
Play around with it at https://svelte.dev/repl/674dcfad17aa4b628e5c2fef4fda100f?version=4.2.0
CSS Grid approach
The code is in Svelte+Tailwind
. Didn't take the time to unwind that further.
<script lang="ts">
import Resizer from './Resizer.svelte';
let sidebarWidth = 256;
let topHeight = 312;
</script>
<svelte:head>
<script src="https://cdn.tailwindcss.com"></script>
</svelte:head>
<div class="wrapper">
<!-- SIDEBAR -->
<aside id="sidebar" aria-label="Sidebar" class="sidebar p-2" style:--sidebar-width={sidebarWidth}>
<span class="font-bold text-xl">Sidebar:</span>
<span>
Lorem ipsum!
</span>
</aside>
<!-- SIDEBAR/MAIN DIVIDER -->
<div style="grid-area: sidebarResizer;">
<Resizer
size={sidebarWidth}
min={128}
max={512}
orientation="vertical"
onResize={(newSize) => (sidebarWidth = newSize)}
onResizeEnd={() => {}}
/>
</div>
<!-- TOP -->
<div id="top" class="mainTop p-2" style:--top-height={topHeight}>
<span class="font-bold text-xl">Top:</span>
Lorem ipsum?
</div>
<!-- TOP/BOTTOM DIVIDER -->
<div class="" style="grid-area: mainResizer;">
<Resizer
size={topHeight}
min={128}
max={640}
orientation="horizontal"
onResize={(newSize) => (topHeight = newSize)}
onResizeEnd={() => {}}
/>
</div>
<!-- BOTTOM -->
<div id="Bottom" class="mainBottom p-2">
<span class="font-bold text-xl">Bottom:</span>
Lorem ipsum.
</div>
</div>
<style>
.wrapper {
display: grid;
grid-template-columns: auto auto 1fr;
grid-template-areas:
'sidebar sidebarResizer mainTop'
'sidebar sidebarResizer mainResizer'
'sidebar sidebarResizer mainBottom';
gap: 5px;
height: 100vh;
width: 100vw;
}
.sidebar {
grid-area: sidebar;
width: calc(var(--sidebar-width) * 1px);
overflow: scroll;
border: 1px solid yellow;
}
.mainTop {
grid-area: mainTop;
height: calc(var(--top-height) * 1px);
overflow: scroll;
border: 1px solid yellow;
}
.mainBottom {
grid-area: mainBottom;
overflow: scroll;
border: 1px solid yellow;
}
</style>
Play around with it at https://svelte.dev/repl/ed4679d507c04c34a512db05cf6de601?version=4.2.0
Conclusions
Think the grid
variant is far more comprehensible.
Flexbox
is cluttered with not so obvious tricks
to make it work
(try removing min-width: 0
or some of the flex: 1 1 0%
statements).
Hope there is something for somebody in here! 🙏
Top comments (0)