Calculator keypad layout with flexbox

I am building a calculator with flexbox. I want one of its keys twice the height and another key twice the width.

I googled much about it but couldn't find both cases together.

For twice height key, only answers I found was to make flex-direction as column. But in that case I will not be able to make double width key.

Here is my code (on codepen.io). Please help.

$(function() {
  var curr = "",
    prev = "";
  var updateView = function() {
    $('#curr').html(curr);
    $('#prev').html(prev);
  };
  $('.keysNum').on('click', function(e) {
    curr += $(this).html();
    console.log(this);
    updateView();
  });
  $('.keysOp').on('click', function(e) {

  });
});
.flexBoxContainer {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  display: flex;
  justify-content: space-around;
  align-items: center;
  width: 100%;
  min-height: 100vh;
}

.calculator {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  width: 100%;
  min-height: 100vh;
}

@media (min-width: 321px) {
  .calculator {
    width: 320px;
  }
}

.calculator .keys {
  border: #d3d2cb 0.5px solid;
  background: #fefdff;
  color: #33393d;
  height: 50px;
  height: 14.2857142857vh;
  width: 25%;
  line-height: 14.2857142857vh;
  text-align: center;
  font-size: 1.4rem;
  font-weight: bold;
  transition: background 0.2s linear;
}

.calculator .keysOp {
  background: #f1f1ef;
}

.calculator .keysC {
  color: #f94913;
}

.calculator .keys:hover {
  background: #d3d2cb;
  transition: background 0s linear;
}

.calculator .keys:focus {
  outline: none;
}

.calculator .keys:active {
  background: #93938E;
}

.calculator .screen {
  background: #e9e8e5;
  height: 14.2857142857vh;
  width: 100%;
  line-height: 14.2857142857vh;
  direction: rtl;
}

.calculator .screen:last-child {
  font-size: 4rem;
}

.calculator #anomaly-keys-wrapper {
  display: flex;
  width: 100%;
}

.calculator #anomaly-keys-wrapper>section:first-child {
  display: flex;
  flex-wrap: wrap;
  width: 75%;
}

.calculator #anomaly-keys-wrapper>section:first-child>div.keys {
  flex: 1 0 33.33%;
}

.calculator #anomaly-keys-wrapper>section:first-child>div.long {
  flex-basis: 66.67%;
}

.calculator #anomaly-keys-wrapper>section:last-child {
  width: 25%;
  display: flex;
  flex-direction: column;
}

.calculator #anomaly-keys-wrapper>section:last-child>.tall {
  background: #f94913;
  color: #fefdff;
  width: 100%;
  line-height: 28.5714285714vh;
  flex: 1;
}

.calculator #anomaly-keys-wrapper>section:last-child>.tall:hover {
  background: #c73a0f;
}

.calculator #anomaly-keys-wrapper>section:last-child>.tall:focus {
  outline: none;
}

.calculator #anomaly-keys-wrapper>section:last-child>.tall:active {
  background: #8b280a;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="flexBoxContainer">
  <div class="calculator">
    <div class="screen" id="prev"></div>
    <div class="screen" id="curr"></div>
    <!--     <div class="keys keysC keysOp" tabindex="2">C</div> -->
    <div class="keys keysC keysOp" tabindex="2">C</div>
    <div class="keys keysOp" tabindex="3"><i class="icon ion-backspace-outline"></i></div>
    <div class="keys keysOp" tabindex="4">&divide</div>
    <div class="keys keysOp" tabindex="5">&times</div>
    <div class="keys keysNum" tabindex="6">7</div>
    <div class="keys keysNum" tabindex="7">8</div>
    <div class="keys keysNum" tabindex="8">9</div>
    <div class="keys keysOp" tabindex="9">-</div>
    <div class="keys keysNum" tabindex="10">4</div>
    <div class="keys keysNum" tabindex="11">5</div>
    <div class="keys keysNum" tabindex="12">6</div>
    <div class="keys keysOp" tabindex="13">+</div>
    <section id="anomaly-keys-wrapper">
      <section>
        <div class="keys keysNum" tabindex="14">1</div>
        <div class="keys keysNum" tabindex="15">2</div>
        <div class="keys keysNum" tabindex="16">3</div>
        <div class="keys long keysNum" tabindex="17">0</div>
        <div class="keys" tabindex="18">.</div>
      </section>
      <section>
        <div class="keys tall" tabindex="19">=</div>
      </section>
    </section>
  </div>
</div>

Wrap the uneven keys in their own flex containers and go from there...

* { box-sizing: border-box; }                                      /* 1 */

.flexBoxContainer {
    display: flex;
    justify-content: space-around;
    align-items: center;
    width: 100%;
}

.calculator {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-content: center;
    width: 100%;
}

.calculator .keys {
    border: red 1px solid;
    height: 50px;
    width: 25%;
    break-inside: avoid;
}

.calculator input {
    height: 100px;
    width: 100%;
    direction: rtl;
}

#anomaly-keys-wrapper {                                            /* 2 */
    display: flex;
    width: 100%; 
}

#anomaly-keys-wrapper > section:first-child {                      /* 3 */
    display: flex;
    flex-wrap: wrap;
    width: 75%;
}

#anomaly-keys-wrapper > section:first-child > div {                /* 4 */
    flex: 1 0 33.33%;
}

#anomaly-keys-wrapper > section:first-child > div:nth-child(4) {   /* 5 */
    flex-basis: 66.67%;
}

#anomaly-keys-wrapper > section:last-child {                       /* 6 */
    width: 25%;
    display: flex;
    flex-direction: column;
}

#anomaly-keys-wrapper .tall {                                      /* 7 */
    width: 100%;
    flex: 1;
}

@media (min-width: 321px) {
    .calculator {
        width: 320px;
    }
}
<div class="flexBoxContainer">
    <div class="calculator">
        <input />
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <div class="keys"></div>
        <section id="anomaly-keys-wrapper">
            <section>
                <div class="keys"></div>
                <div class="keys"></div>
                <div class="keys"></div>
                <div class="keys long"></div>
                <div class="keys"></div>
            </section>
            <section>
                <div class="keys tall"></div>
            </section>
        </section>
    </div>
</div>

Revised Codepen (with compiled CSS)

Notes:

  1. Include padding and borders in width / height calculations.
  2. Wrap uneven keys in a separate flex container (with defaults flex-direction: row and flex-wrap: nowrap)
  3. Wrap long key in a separate flex container with wrapping enabled (and take enough siblings to create equal height with tall key).
  4. Force three keys per row max.
  5. Make long key twice the width of siblings. (Didn't use simpler long class selector due to weaker specificity.)
  6. Wrap tall key in a separate flex container with vertical orientation.
  7. Make tall key consume all available width and height of container.

UPDATE

From the comments:

Hi, 1. Can you explain me how flex basis works? and why did you use it instead of giving width to long button. 2. Why is it necessary to give flex: 1; to tall button, as i have read it is the default value.

Question #1:

The keys in the first sub-section container (containing .long) are sized with flex: 1 0 33.33%.

This is shorthand for flex-grow: 1, flex-shrink: 0, and flex-basis: 33.33%.

For the .long key we are simply overriding the flex-basis component with 66.67%. (there's no need to re-declare the other two components).

Also, there's really no difference in this case between width and flex-basis, but since we're overriding flex-basis, I used flex-basis.

Using width would leave the original flex-basis: 33.33% intact, creating two width rules which may, therefore, fail to expand the .long key, depending on which rule prevails in the cascade.

For a complete explanation of flex-basis vs. width, see What are the differences between flex-basis and width?

Question #2:

Because the initial value of the flex-grow component is 0 (source).


There isn't a generic answer I can come up with, but this is as close as I can get.

I have modified your code to use floating instead of the flexbox layout model. This is less modern, but seems to be a viable workaround to this particular problem, with little to no negative effects.

Please note that I have also modified the width of the buttons to actually take up all the space they can, and gave them a box-sizing of border-box. More on this here.

https://codepen.io/anon/pen/VjOKGX

.calculator {
    // Required to keep the buttons inside
    overflow: hidden;
    width: 100%;
    @media #{$gtphone} {
        width: 320px;
    }
    .keys {
        // Changes the box model so that width includes the borders too
        box-sizing: border-box;
        border: red 1px solid;
        height: 50px;
        // The width is modified to take up all the space
        width: 25%;
        // Floating is defined
        float: left;
        break-inside: avoid;
    }
    .long {
        // This one needs float to be set to right,
        // so that other elements may be on its left in multiple rows
        float: right;
        height: 100px !important;
    }
    .big {
        // Again, width is modified
        width: 50% !important;
    }
    input {
        height: 100px;
        width: 100%;
        direction: rtl;
    }
}