Skip to main
Article

Off-Canvas Layout with Susy

The off-canvas layout pattern for responsive web design has been getting all the attention lately, and I’ve had several people ask how Susy One might play along. I’ll show you how easy it is, and how much flexibility Susy can add along the way.

UPDATE: I fixed a bug with show-left failing on small screens. The main area was dropping below the left sidebar, as floats sometimes do. The fix is actually simpler than the original code: just set and leave a 100% negative right-margin on the main area, removing all state changes to that margin.

The off-canvas layout pattern for responsive websites has been getting all the attention lately, and I’ve had several people ask how Susy might play along.

The truth is, Susy handles off-canvas layout the same as any other layout. All you need to do is pull some of your columns off the screen. I’ll show you how, following Jason’s lead, and adding in the Susy bits.

Check out the demo, and make sure you understand off-canvas layouts before you go on.

I’ve used a few shortcuts that require the latest Susy release (1.0.5), but the concepts remain true in older versions as well.

<div class="container">
  <header>
    <a href="#left" class="toggle">show-left</a>
    <a href="#right" class="toggle">show-right</a>
    header
  </header>
  <div class="left" id="left">left</div>
  <div class="main">main</div>
  <div class="right" id="right">right</div>
  <footer>footer</footer>
</div>

We have a simple container with header, footer, and three body columns: left, right, and main. Inside the header we have links we can hijack in JavaScript to toggle state body-classes.

$total-columns: 5;
$column-width: 4em;
$gutter-width: 1em;
$grid-padding: 1em;

Since this is a mobile-first design pattern, we’ll start with settings for a typical mobile-first Susy grid. You can change those any way you like.

I’m also going to establish my medium and large column settings right away:

$medium-columns: 8;
$large-columns: 12;

And I’ll set Susy’s $container-width override to the largest layout width, so the container is fluid up to that point:

$container-width  : container-outer-width($large-columns);
.container {
  @include container;
  overflow: hidden;
}

Besides establishing the usual Susy container, I also set overflow to hidden so that our off-canvas elements don’t trigger a horizontal scrollbar.

For our smallest layout the .main section is visible at all times, full-width by default or pushed to one side to make room for the .left or .right sections to appear.

$anchor: 1;
$side: $total-columns - $anchor;

I’ve created an $anchor variable to control how many columns of the main section remain visible while side-sections are displayed. The $side width of our left & right sections is based on the remaining space.

.left {
  @include span-columns($side);
  margin-left: -100%;
  .show-left & { margin-left: 0; }
}

.main {
  @include span-columns($total-columns);
  margin-right: -100%;
  .show-right & { margin-left: - space($side); }
}

.right {
  @include span-columns($side omega);
  margin-right: -100%;
  .show-right & { margin-right: 0; }
}

The span-column mixins establish our spacing, just like any other Susy site. The main difference here is that our total columns-spanned is much larger than the number of columns available. A few margin adjustments, and we’ve pulled the left and right sections off the canvas. I also removed the margin-right gutter on our main column, since it spans the full width.

The .show-left and .show-right selectors allow us to move everything around when we want to show and hide the sidebars. The space() function is used to push our main section only as far as it needs to go: space() represents the space taken by a given number of columns() with the final gutter() included.

$main: 5;
$side: $medium-columns - $main;

These variables simply establish the widths we will use for our columns. You could, of course, set different right and left widths. I’ll leave that as an exercise for the reader.

@include at-breakpoint($medium-columns) {
  [href="#left"] { visibility: hidden; }

  .left {
    @include span-columns($side);
    margin-left: 0;
    .show-right & { margin-left: - 100%; }
  }

  .main {
    width: columns($main);
    .show-right & { margin-left: 0; }
  }

  .right {
    width: columns($side);
    .show-right & { margin-right: 0; }
  }
}

At our medium breakpoint, we change the styles to show both the left and main sections by default. I used width: columns() instead of span-columns on the main & right sections because only the width actually needs to change, while our left column needs the gutter adjusted as well.

We also hide the left toggle ( [href="#left"] ) as it is no longer needed.

$main : 6;
$side : ($large-columns - $main)/2;

Nothing new here; we’re just dividing up the space into variables we can use.

@include at-breakpoint($large-columns) {
  [href="#right"] { visibility: hidden; }

  .left {
    @include span-columns($side);
    .show-right & { margin-left: 0; }
  }

  .main {
    width: columns($main);
  }

  .right {
    @include span-columns($side omega);
  }
}

At our largest breakpoint we are simply overriding everything to get ourselves back to a normal layout. No more off-canvas malarkey here. Hide the other toggle-link, make sure everything stays put even if we have leftover classes, and you’re done.

See the Pen Susy1 Off-Canvas Demo by @miriamsuzanne on CodePen.

I’ve added a number of styles to make it obvious what’s going on and highlight the transitions in our demo. You also need a bit of JS to make the toggles work, but this is all you need for the Susy setup.

Play around with all the numbers; it’s amazingly flexible. It works the same as any other Susy grid: any reasonable settings should work.

Recent Articles

  1. A dog zooming by the camera, up-close, body twisted and eyes wide as it circles a grass yard
    Article post type

    Zoom, zoom, and zoom

    The three types of browser (and CSS!) magnification

    I’m working on an article about fluid typography, and relative units. But instead, I fell down this rabbit hole – or a cleverly-disguised trap? – trying to understand ‘zoom’ in the browser (not Zoom™️ the software). Since I couldn’t find any up-to-date articles on the subject, I thought I shoul…

    see all Article posts
  2. A rusty anchor hanging with the sea in the background.
    Article post type

    Updates to the Anchor Position Polyfill

    Catching up to the spec

    Our sponsors are supporting the continued development of the CSS Anchor Positioning Polyfill. Here’s a summary of the latest updates.

    see all Article posts
  3. A back hoe on the bank of the Suez, trying to free the Ever Given cargo ship
    Article post type

    Learn Grid Now, Container Queries Can Wait

    Take your time with new CSS, but don’t sleep on the essentials

    Several people have asked recently why container queries aren’t being used more broadly in production. But I think we underestimate the level of legacy browser support that most companies require to re-write a code-base.

    see all Article posts