x-height calculator

by Robert Mohns

HTML

<form action="" id="xHeightCalculator" onsubmit="return false;">
  <fieldset>
    <legend>x-height Readability Calculator</legend>
    <label for="fontPointSize">Font size (points):</label>
    <input type="number" step="1" id="fontPointSize" name="fontPointSize" value="16" max="360" min="1" />
    <br />
    <label for="fontXHeightRatio">x-height of your font (ratio):</label>
    <input type="number" step="0.01" id="fontXHeightRatio" name="fontXHeightRatio" value=".45" max="1" min="0" />
    <br />
    <label for="screenResActual">Actual screen resolution (ppi):</label>
    <input type="number" step="1" id="screenResActual" name="screenResActual" value="131" max="500" min="1" />
    <br />
    <label for="distanceFromScreen">Distance from Screen (inches):</label>
    <input type="number" step="1" id="distanceFromScreen" name="distanceFromScreen" value="16" max="240" min="1" />
    <br />

    <p>
      <input id='submit' type='submit' value='Calculate' />
    </p>
    <p id="resultpara">
      <span id="resultLabel">Visual Arc:</span>
      <span id="perceivedSize">result</span> <span id="adviceTextHere"></span>
    </p>
  </fieldset>
</form>

CSS

* {
  font-family: 'Hind', sans-serif;
}

body {
  font-size: 1em;
  background-color: #ff675b;
  color: #fefefe;
}

label,
span#resultLabel {
  display: inline-block;
  width: 16em;
}

#fontPointSize,
#fontXHeightRatio,
#screenResActual,
#distanceFromScreen {
  width: 4em;
  text-align: right;
}

fieldset {
  margin: 0.5em 0.5em 0.5em 0;
}

legend {
  padding: 0em 0.5em 0 0.5em;
  font-weight: 600;
  border-radius: 4px;
}

input[type=number] {
  font-size: 1.1em;
  height: 1.3em;
}

.subtotal {
  font-size: smaller;
  color: grey;
}

#resultpara {
  font-weight: 600;
}

#perceivedSize {
  font-size: large;
  font-weight: bold;
}


#totalHours {
  font-weight: bold;
  font-size: 1.5em;
  text-align: right;
  padding: 0.5em;
  border-radius: 4px;
}

@keyframes noticeThisThing {
  0% {
    transform: scale(1)
  }
  25% {
    color: #41322f;
    background-color: #fbce78;
  }
  100% {
    transform: scale(1)
  }
}

.noticeMe {
  -webkit-animation: noticeThisThing 0.4s infinite ease;
  -moz-animation: noticeThisThing 0.4s infinite ease;
  -o-animation: noticeThisThing 0.4s infinite ease;
  animation: noticeThisThing 0.4s infinite ease;
}

JavaScript

var theForm = document.forms.xHeightCalculator;
function toggleAnimation(onOffState) {
  if (onOffState === "on") {
    document.getElementById("perceivedSize").classList.add("noticeMe");
    return true;
  } else {
    document.getElementById("perceivedSize").classList.remove("noticeMe");
    return false;
  }
}

function getValue(lineItem) {
  var theValue = theForm.elements[lineItem];
  if (theValue.value !== "") {
    return parseFloat(theValue.value);
  } else {
    return null;
  }
}

function updateForm() {
  // get the values
  var fontPointSize = getValue("fontPointSize");
  var fontXHeightRatio = getValue("fontXHeightRatio");
  var screenResActual = getValue("screenResActual");
  var distanceFromScreen = getValue("distanceFromScreen");

  // do the math
	var pixelRatio = 96 / screenResActual; // OS assumed resolution vs reality
  var distanceFromScreenPoint = distanceFromScreen * 72; // 72 points per inch
	var actualXheightPointSize = fontPointSize * fontXHeightRatio * pixelRatio;
  var perceivedSizeArctan = Math.atan2(actualXheightPointSize, distanceFromScreenPoint);
  var perceivedSizeDegrees = perceivedSizeArctan / (Math.PI / 180);
  var prettyDegrees = perceivedSizeDegrees.toPrecision(2);
  
  // update the result
  document.getElementById('perceivedSize').innerHTML = prettyDegrees + "º";

	// Now for some commentary on the result
	var adviceText = "And the verdict is…";
	if(perceivedSizeDegrees < 0.2) {
  	adviceText = "Too small!"
  }
	if(perceivedSizeDegrees > 0.2 && perceivedSizeDegrees < 0.24) {
  	adviceText = "A little small, but serviceable."
  }
	if(perceivedSizeDegrees >= 0.24 && perceivedSizeDegrees < 0.27) {
  	adviceText = "Not bad."
  }
	if(perceivedSizeDegrees >= 0.27 && perceivedSizeDegrees < .31 ) {
  	adviceText = "Nicely readable."
  }
	if(perceivedSizeDegrees >= 0.31) {
  	adviceText = "Very, very readable. Nice!"
  }
	if(perceivedSizeDegrees >= 0.5) {
  	adviceText = "Now you're just getting silly."
  }
	if(perceivedSizeDegrees...