Search code examples
javascripthtmlcsssveltesveltekit

2 column layout until first column height is done and then one column layout


My webpage has a 2 column layout, left side is filters and right side is articles, like this: 2 column layout

Goals:

  • Fixed width filters (left side) @ big viewport
  • Full width filters above articles (one column layout) @ small viewport
  • @ big viewport stack articles on right till we get to the height end of 1st column and then full width articles, like this:

End of 2 column layout

I am currently doing this with JS getBoundingClientRect() but would love to know if there is a CSS solution please?!

As requested, current JS solution via SvelteKit:

import {
  onMount
} from 'svelte';
import type {
  PageData
} from './$types'
import Source from '$lib/components/Source.svelte'
import type {
  QuoteCategory
} from '$lib/util/types'
import AuthorChips from '$lib/components/chips/AuthorChips.svelte'
import QuoteCategoryChips from '$lib/components/chips/QuoteCategoryChips.svelte'

export let data: PageData
let categories: QuoteCategory[]
if (data.categories) categories = data.categories

onMount(fullSizeSources)

function fullSizeSources() {
  let wideDone = false
  const scrollTop = document.documentElement.scrollTop
  const authors = document.getElementById('author-chips')
  const sources = document.getElementsByClassName('source')
  const authorBottom = (authors ? .getBoundingClientRect() ? .bottom || 0) + scrollTop

  for (let source of sources) {
    if (wideDone || (source.getBoundingClientRect().top + scrollTop) > authorBottom) {
      wideDone = true
      source.classList.add('full')
    } else {
      source.classList.remove('full')
    }
  }
}
.wrapper {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  .left {
    max-width: 36rem;
    margin-right: 1.8rem;
    display: flex;
    flex-direction: column;
  }
   :global(.source.full) {
    margin-left: -37rem;
    width: calc(100% + 37rem);
  }
}
<div class="wrapper">
  <div class="left">
    { #if data.categories }
    <QuoteCategoryChips categories={ data.categories } location="nav" /> { /if } { #if data.authors }
    <AuthorChips authors={ data.authors } /> { /if }
  </div>

  <div>
    { #if data.sources } { #each data.sources as source }
    <Source { source } /> { /each } { /if }
  </div>
</div>


Solution

  • If I understand you correctly, you can solve this problem by using float and @media. For small viewport use normal layout with one column. For big viewport you can use float: left; so that your articles are on the right side and after reaching the height of the left column they become 100% width.

    /* Just some styles to visualise. Notice the use of flex, it makes it easier to work with the content of these elements */
    
    .left,
    .right-article {
      display: flex;
      flex-direction: column;
      padding: 1rem;
      margin: 1rem;
      border: 1px solid blue;
    }
    
    
    /* Replace 600px with whatever you need */
    
    @media (min-width: 600px) {
      .left {
        /* Replace the width with whatever you need */
        width: 300px;
        float: left;
      }
    }
    <div>
      <div class="left">
        <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.</p>
        <p>Exercitationem et qui fuga non illo nemo, harum ab, vero in illum maxime, dolore dignissimos velit provident id iure accusantium fugiat amet!</p>
      </div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit..</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit..</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit..</div>
    
      <div class="right-article">Lorem ipsum, dolor sit amet consectetur adipisicing elit.</div>
    </div>