Daniel
Daniel

Reputation: 2662

Collapsible flexible-width sidebar using only CSS

Let's assume I have a collapsible fixed-width sidebar defined like this:

HTML

<div class="container">
  <div class="sidebar" id="sidebar">
    SIDEBAR
  </div>
  <div class="content">
    lorem bla bla
    <button onclick="document.getElementsByClassName('sidebar')[0].classList.toggle('collapsed')">
      toggle Sidebar
    </button>
  </div>
</div>

CSS

body {
  margin:0;
}

.container {
  display: flex;
  min-height:100px;
}

.sidebar {
  position: relative;
  left: 0;
  width: 200px;
  background-color: #ccc;
  transition: all .25s;
}

.sidebar.collapsed {
  left:-200px;
  margin-right:-200px;
}

Codepen here: http://codepen.io/anon/pen/pJRYJb

So here's the question:

How can I go from there to a flexible-width sidebar?

Here are the constraints:

The main issue here is that I can't find a way to say margin-right: -100% where 100% refers to the width of sidebar

Any help will be greatly appreciated!

Upvotes: 9

Views: 22500

Answers (3)

user007
user007

Reputation: 1683

For what it's worth, I do have a partly js solution. The animation itself is done entirely with CSS, it just resolves the width:auto problem. You just set the width of the sidebar domElement with js, when the element is loaded:

domElement.style.width = `${parseInt(domElement.offsetWidth)}px`;

This resolves the 'unknown' width problem. And you can use your .collapsed approach just as you like to do. The only adjustment is that you'll need to add !important to the .collapsed {width: 300px !important;} width value.

Upvotes: 0

JFlo
JFlo

Reputation: 101

Piggy-backing off Pangloss' suggestion re: the transform... you can just apply a negative margin to the content if you know the width of the sidebar. No need to have the sidebar switch to absolute positioning.

@mixin transitionAll() {
  transition: all .3s ease-out;
}
html, body {
  height: 100%;
}
.container {
  display: flex;
  width: 100%;
  height: 100%;
}
.sidebar {
  flex: 1 0 300px;
  background: #333;
  height: 100%;
  @include transitionAll();
  &.is-collapsed {
    transform: translateX(-100%);
  }
}
.content {
  width: 100%;
  height: 100%;
  padding: 1rem;
  background: #999;
  @include transitionAll();
  &.is-full-width {
    margin-left: -300px;
  }
}

--

<div class="container">
  <aside class="sidebar">
  </aside>
  <section class="content">
    <button class="btn btn-primary">Toggle</button>
  </section>
</div>

--

$(function() {
  $('.btn').on('click', function() {
    $('.sidebar').toggleClass('is-collapsed');
    $('.content').toggleClass('is-full-width');
  });
});
See the Pen LxRYQB by Jason Florence (@JFlo) on CodePen.

Upvotes: 0

Stickers
Stickers

Reputation: 78686

How about changing the width instead of position on click? I use max-width in this case, it works almost the same as unknown width. This will probably cause the content reflow on the sidebar, so use white-space:nowrap if it's acceptable.

http://jsfiddle.net/dn4ge901/

body {
    margin: 0;
}
.container {
    display: flex;
}
.sidebar {
    background: #ccc;
    transition: all .1s;
    max-width: 1000px;
}
.sidebar.collapsed {
    max-width: 0;
    overflow: hidden;
}
<div class="container">
    <div class="sidebar" id="sidebar">SIDEBAR</div>
    <div class="content">lorem bla bla
        <button onclick="document.getElementsByClassName('sidebar')[0].classList.toggle('collapsed')">toggle Sidebar</button>
    </div>
</div>

Another workaround is using transform width position together, but the animation effect will be slightly different.

http://jsfiddle.net/vkhyp960/

body {
    margin: 0;
}
.container {
    display: flex;
}
.sidebar {
    background: #ccc;
    transition: all .1s;
}
.sidebar.collapsed {
    transform: translateX(-100%);
    position: absolute;
}
<div class="container">
    <div class="sidebar" id="sidebar">
        <div>SIDEBAR</div>
    </div>
    <div class="content">lorem bla bla
        <button onclick="document.getElementsByClassName('sidebar')[0].classList.toggle('collapsed')">toggle Sidebar</button>
    </div>
</div>

Upvotes: 4

Related Questions