jmancherje
jmancherje

Reputation: 6633

Use flexbox to create a responsive wrappable row of buttons

I am attempting to make a row of buttons responsive. There can be either 3 or 2 buttons in the row. The reason I'm thinking flexbox (or maybe grid) is the solution is because the text in the buttons is internationalized and the language will vary meaning they will wrap depending on the text in the buttons, not just the screen width so I can't just write media queries that correspond to screen width.

These are the initial states with no wrapping: The starting states (no wrapping) are either of these images (depending on 2 or 3 buttons):

initial state with 3 buttons

initial state with 2 buttons

These are the desired wrapping states when the container gets too small:

Small screen with 3 buttons

enter image description here

I created a fiddle that allows you to adjust the width of the box and toggle the optional button here:

See Fiddle here to test each variant

This is the starting html, but maybe it would help to wrap the first two buttons together


    <div class="container">
      <div class="button button1">
        This is a longer button
      </div>
      <div class="button button2">
        Optional Button
      </div>
      <div class="button button3">
        Confirmation button
      </div>
    </div>
    <button class="width-button">Toggle width</button>
    <button class="toggle-button-button">Toggle optional button</button

>

Upvotes: 2

Views: 1555

Answers (2)

Temani Afif
Temani Afif

Reputation: 272909

And idea using flexbox. Resize both containers to see the result

.container {
  width: 480px;
  background-color: lightgray;
  margin: 40px 0;
  padding: 12px;
  overflow:hidden;
  resize:horizontal;
  display:flex;
  flex-wrap:wrap;
  z-index:0;
  position:relative;
}


.button {
  padding: 12px;
  background-color: green;
  display: inline-block;
  border-radius: 8px;
  margin:2px;
  position:relative;
}

.button3 {
  flex-grow:1; /* grow on wrap  */
  order:2; /* put after the pseudo element*/
}

/* this will push the confirmation button 
and will complete the first button on wrap */
.container::after {
  content:"";
  flex-grow:999; /* push the last button */
  flex-basis:1px;
  background:green;
  border-radius:8px;
  position:relative;
  margin:2px 2px 2px -20px;
  z-index:-3;
}

/* hide the pseudo element when confirmation button on the first row */
.button3::before {
  content:"";
  position:absolute;
  z-index:-1;
  top:0;
  bottom:0;
  right:100%;
  width:100vw;
  background:lightgray;
}

/* make sure the button area is big enough for the events*/
.button::after {
   content:"";
   position:absolute;
   z-index:-2;
   top:0;
   bottom:0;
   left:100%;
   width:100vw;
}
<div class="container">
  <div class="button button1">
    This is a longer button
  </div>
  <div class="button button2">
    Optional Button
  </div>
  <div class="button button3">
    Confirmation button
  </div>
</div>

<div class="container">
  <div class="button button1">
    This is a longer button
  </div>
  <div class="button button2" style="display:none;">
    Optional Button
  </div>
  <div class="button button3">
    Confirmation button
  </div>
</div>

Upvotes: 0

dippas
dippas

Reputation: 60563

If your layout is fixed and you are really using the .narrow class you can do like this,

document.querySelector('.width-button').addEventListener('click', () => {
  document.querySelector('.container').classList.toggle('narrow');
});

document.querySelector('.toggle-button-button').addEventListener('click', () => {
  document.querySelector('.button1').classList.toggle('hide');
});
.container {
  width: 580px;
  background-color: lightgray;
  margin: 40px 0;
  padding: 12px;
  transition: width 200ms ease-in-out;
  display: flex;
  flex-wrap: wrap;
}

.container.narrow {
  width: 320px;
}

.button {
  padding: 12px;
  background-color: green;
  display: inline-block;
  border-radius: 8px;
  margin: 5px
    /* demo */
}

.button1.hide+.button2 {
  display: none;
}

.button3 {
  margin-left: auto;
}


.container.narrow .button1.hide,
.container.narrow .button3 {
  flex: 0 100%;
}
<div class="container">
  <div class="button button1">
    This is a longer button
  </div>
  <div class="button button2">
    Optional Button
  </div>
  <div class="button button3">
    Confirmation button
  </div>
</div>
<button class="width-button">Toggle width</button>
<button class="toggle-button-button">Toggle optional button</button>

otherwise you can change the .narrow class with media queries, something like this:

document.querySelector('.toggle-button-button').addEventListener('click', () => {
  document.querySelector('.button1').classList.toggle('toggle');
});
body {
  margin: 0
}

.container {
  width: 100%;
  background-color: lightgray;
  margin: 40px 0;
  padding: 12px;
  box-sizing: border-box;
  transition: width 200ms ease-in-out;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

@media (min-width: 600px) {
  .container {
    justify-content: flex-start
  }
}

.button {
  padding: 12px;
  background-color: green;
  display: inline-block;
  border-radius: 8px;
  /* demo */
  margin: 5px;
}

.toggle+.button2 {
  display: none;
}

.button1.toggle {
  flex: 1;
}

@media (min-width: 600px) {
  .button1.toggle{
    flex: 0 auto;
  }
}

.button3 {
  flex: 0 100%;
}

@media (min-width: 600px) {
  .button3 {
    margin-left: auto;
    flex: 0 auto;
  }
}
<div class="container">
  <div class="button button1">
    This is a longer button
  </div>
  <div class="button button2">
    Optional Button
  </div>
  <div class="button button3">
    Confirmation button
  </div>
</div>
<button class="toggle-button-button">Toggle optional button</button>

Upvotes: 1

Related Questions