Calculator keypad layout with flexbox

css, flexbox, html

Solution

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:

- Include padding and borders in `width` / `height` calculations.

- Wrap uneven keys in a separate flex container (with defaults `flex-direction: row` and `flex-wrap: nowrap`)

- Wrap long key in a separate flex container with wrapping enabled (and take enough siblings to create equal height with tall key).

- Force three keys per row max.

- Make long key twice the width of siblings. (Didn't use simpler `long` class selector due to weaker specificity.)

- Wrap tall key in a separate flex container with vertical orientation.

- 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).

Problem

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> ```

Original source

Related problems