%main
  %input
  %table.keys
    %tr
      %td
        %button.opkey{op: "clear"} AC
      %td
        %button.opkey{op: "negate"} +/-
      %td
        %button.opkey{op: "mod"} %
      %td
        %button.opkey{op: "divide"} /
    %tr
      %td
        %button.numkey 7
      %td
        %button.numkey 8
      %td
        %button.numkey 9
      %td
        %button.opkey{op: "times"} X
    %tr
      %td
        %button.numkey 4
      %td
        %button.numkey 5
      %td
        %button.numkey 6
      %td
        %button.opkey{op: "minus"} -
    %tr
      %td
        %button.numkey 1
      %td
        %button.numkey 2
      %td
        %button.numkey 3
      %td
        %button.opkey{op: "add"} +
    %tr
      %td{colspan: 3}
        %button.numkey 0
      %td
        %button.eqkey =
View Compiled
@import url('https://fonts.googleapis.com/css?family=Fira+Mono&display=swap')

*
  margin: 0
  padding: 0
  box-sizing: border-box

  background: #eee

  font-family: "Fira Mono", Inconsolata, monospace
  font-size: 20pt

body
  height: 100vh
  width: 100vw
  
  display: flex
  align-items: center
  justify-content: center
  
  background: #eee

table.keys td
    padding: 10px
    text-align: left

button.numkey
  box-shadow: -6px -6px 9px 0px white, 6px 6px 9px rgba(0, 0, 0, 0.4)
  
  font-size: 20pt
  
  width: 60px
  height: 60px
  
  border: none
  border-radius: 30px
  
  background: #eee
  
  cursor: pointer
  
  &:hover, &:active
    background: #aaa
  
  &:active
    box-shadow: inset -6px -6px 9px 0px rgba(255,255,255,0.4), inset 6px 6px 9px rgba(0, 0, 0, 0.4)

    
.opkey
  box-shadow: -6px -6px 9px 0px white, 6px 6px 9px rgba(0, 0, 0, 0.4)
  
  font-size: 20pt
  
  width: 60px
  height: 60px
  
  border: none
  border-radius: 30px
  
  cursor: pointer
  
  background: #aaa

  &:hover, &:active
    color: #eee
    background: #666
  
  &:active
    box-shadow: inset -6px -6px 9px 0px rgba(255,255,255,0.4), inset 6px 6px 9px rgba(0, 0, 0, 0.4)
  
.eqkey
  box-shadow: -6px -6px 9px 0px white, 6px 6px 9px rgba(0, 0, 0, 0.4)
  
  font-size: 20pt
  
  width: 60px
  height: 60px
  
  border: none
  border-radius: 30px
  
  cursor: pointer
  
  background: darkorange
    
  &:hover, &:active
    background: orange
  
  &:active
    box-shadow: inset -6px -6px 9px 0px rgba(255,255,255,0.4), inset 6px 6px 9px rgba(0, 0, 0, 0.4)
    
main
  text-align: center
    
input
  -webkit-appearance: none
  -mox-appearance: none
  appearance: none
  
  width: 80px * 4
  height: 60px
  
  padding: 16px 32px
  
  text-align: right
  
  border: none
  border-radius: 30px
  
  box-shadow: inset -6px -6px 9px 0px rgba(255,255,255,1), inset 6px 6px 9px rgba(0, 0, 0, 0.4)
  
  text-shadow: 6px 6px 9px rgba(0,0,0,0.3)
View Compiled
const $input = document.querySelector("input")

document.querySelectorAll("button.numkey").forEach(
  elem => {
    const n = elem.innerText
    elem.onclick = event => $input.value = $input.value + n
  }
)


const buffer = []

const evaluate = buffer => {
  // note(jrm) Since pop() removes the _last_ element of an array,
  // we use the following order instead of starting with the first operand.
  const secondOperand = buffer.pop().value
  const operator = buffer.pop().value
  const firstOperand = buffer.pop().value
  
  if (operator === "add") {
    return firstOperand + secondOperand
  }
  else if (operator === "minus") {
    return firstOperand - secondOperand
  }
  else if (operator === "times") {
    return firstOperand * secondOperand
  }
  else if (operator === "divide") {
    return firstOperand / secondOperand
  }
  else if (operator === "mod") {
    return firstOperand % secondOperand
  }
}

const opCallback = opname =>
  event => {
    if (buffer && buffer.length) {
      buffer.push({ type: "value", value: parseInt($input.value, 10) })
      
      const result = evaluate(buffer)
      
      buffer.push({ type: "value", value: result })
      buffer.push({ type: "op", value: opname })
      
      $input.value = ""
    }
    else {
      buffer.push({ type: "value", value: parseInt($input.value, 10) })
      buffer.push({ type: "op", value: opname })
      $input.value = ""
    }
  }

for (const opname of [ "add", "minus", "times", "divide", "mod" ]) {
  document.querySelector(`button.opkey[op=${opname}]`).onclick =
    opCallback(opname)
}


document.querySelector("button.eqkey").onclick =
  event => {
    buffer.push({ type: "value", value: parseInt($input.value, 10) })
    $input.value = evaluate(buffer).toString()
  }

document.querySelector("button.opkey[op=clear]").onclick =
  event => {
    $input.value = ""
    while (buffer.length) buffer.pop()
  }

document.querySelector("button.opkey[op=negate]").onclick =
  event => $input.value = -parseInt($input.value, 10)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.