DEV Community

Johannes Zillmann
Johannes Zillmann

Posted on

CSS main page layout with resizing and scroll overflow sections

Here is a picture of the layout I want:

┌─────────┬───────────────────────┐
│ Sidebar │ Editor                │
│         │                       │
│         │                       │
│       ◄─┼─►                 ▲   │
│         │                   │   │
│         ├───────────────────┼───┤
│         │ Preview           │   │
│         │                   ▼   │
│         │  Table                │
│         │  <overflow-scroll>    │
│         │                       │
└─────────┴───────────────────────┘
Enter fullscreen mode Exit fullscreen mode
  • The whole page should not be scrollable (more like a desktop app, 100vw +100vh).
  • The Sidebar should be vertically resizable
  • The Editor should be horizontally resizable
  • The Preview should take up the rest of the space. Overflow should be managed via vertically + 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)