<div class="container">
  <h1>
    Binary Search Example
  </h1>
  <div class="description">
    The number in the middle represents our current opacity guess.
  </div>
  <div class="range-container">
    <div class="current-range"></div>
    <div class="full-range"></div>
    <div class="start-marker"></div>
    <div class="end-marker"></div>
    <div class="guess-marker"></div>
    <div class="guess-number-container">
      <div class="guess-number">0.50</div>
    </div>
  </div>

  <div class="buttons">
    <button id="guess_btn">Next Guess</button>
    <button id="reset_btn">Reset</button>
  </div>

  <div class="banner">
    <div class="overlay"></div>
    <div class="banner-text">To begin, click the &ldquo;next guess&rdquo; button.</div>
  </div>

  <div class="disclaimer">
    This demo is for illustrative purposes only, so it uses pretend numbers. In the real version, we would check our guesses against a target contrast. For the actual opacity checker, <a href="https://codepen.io/yaphi1/live/oNbEqGV">click here instead</a>.
  </div>

</div>
body {
  margin: 0px;
  font-family: Montserrat, sans-serif;
  color: #345;
}
h1 {
  margin: 0px 0px 4px;
  font-size: 30px;
}
.description {
  margin-bottom: 16px;
}
.disclaimer {
  margin-top: 20px;
  font-size: 14px;
  line-height: 1.4;
}
a {
  color: #0098db;
  font-weight: bold;
}
a:hover {
  color: #c80;
}
.buttons {
  display: flex;
  justify-content: space-between;
}
button {
  font-family: Montserrat, sans-serif;
  padding: 10px 20px;
  font-size: 16px;
  font-weight: bold;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 4px;
  border: none;
  background-color: #eee;
  color: #456;
  transition: all 0.2s;
}
button:hover {
  color: #eee;
  background-color: #333;
}
.container {
  max-width: 800px;
  padding: 20px;
  margin: auto;
}
.range-container {
  width: 100%;
  height: 60px;
  position: relative;
  margin-bottom: 40px;
}
.full-range {
  position: absolute;
  left: 0px;
  top: calc(50% - 2px);
  width: 100%;
  height: 4px;
  background-color: #333;
}
.current-range {
  height: 100%;
  width: 100%;
  background-color: #0098db;
  position: absolute;
  left: 0px;
  top: 0px;
  transform-origin: 0 0;
  transition: all 0.5s;
}

.guess-marker {
  width: 0px;
  background-color: gold;
  height: 100%;
  position: absolute;
  left: 50%;
  top: 0px;
  transition: all 0.2s;
}
.guess-marker::before {
  content: "";
  border: 2px solid gold;
  position: absolute;
  left: -2px;
  top: -2px;
  height: 100%;
  width: 100%;
}
.guess-number-container {
  position: absolute;
  left: 50%;
  top: calc(100% + 10px);
  transition: all 0.2s;
}
.guess-number {
  transform: translateX(-50%);
  font-size: 20px;
}

.start-marker,
.end-marker {
  width: 3px;
  background-color: #333;
  height: 100%;
  position: absolute;
  left: 0%;
  top: 0px;
}
.end-marker {
  left: auto;
  right: 0%;
}
.start-marker::after,
.end-marker::after {
  position: absolute;
  top: calc(100% + 5px);
}
.start-marker::after {
  content: "0";
  left: 0px;
}
.end-marker::after {
  content: "1";
  right: 0px;
}

.banner {
  margin-top: 20px;
  background-color: #333;
  color: #fff;
  font-size: 20px;
  text-align: center;
  --overlay-opacity: 0.5;
  background-image: url(https://assets.codepen.io/246719/valley-pexels-photo-414171.jpeg?width=567&height=290&format=auto);
  background-size: cover;
  position: relative;
  height: 180px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.overlay {
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  height: 100%;
  background-color: #000;
  opacity: 0.5;
  transition: opacity 0.2s;
}

.banner-text {
  position: relative;
  padding: 20px;
  max-width: 460px;
  line-height: 1.4;
}
const reset_btn = document.getElementById("reset_btn");
const guess_btn = document.getElementById("guess_btn");
const range_bar = document.querySelector(".current-range");
const guess_marker = document.querySelector(".guess-marker");
const guess_number_container = document.querySelector(
  ".guess-number-container"
);
const guess_number = document.querySelector(".guess-number");
const overlay = document.querySelector(".overlay");
const banner_text = document.querySelector(".banner-text");
let guessInProgress = false;

guess_btn.addEventListener("click", makeGuess);

const guessRange = {
  lowerBound: 0,
  midpoint: 50,
  upperBound: 100
};
const target = 33;

function makeGuess() {
  if (guessInProgress) {
    // console.log("in progress");
    return;
  }
  guessInProgress = true;

  let guess = guessRange.midpoint;
  let opacity = (guess / 100).toFixed(2);
  if (Math.round(guess) === target) {
    banner_text.innerHTML = `We found our optimal opacity of ${opacity}!`;
    guessInProgress = false;
    return;
  } else if (guess > target) {
    banner_text.innerHTML = `Our opacity guess of ${opacity} was too large, so we eliminated it and everything ABOVE it.`;
    guessRange.upperBound = guess;
  } else if (guess < target) {
    banner_text.innerHTML = `Our opacity guess of ${opacity} was too small, so we eliminated it and everything BELOW it.`;
    guessRange.lowerBound = guess;
  }

  const rangeSize = guessRange.upperBound - guessRange.lowerBound;
  guessRange.midpoint = rangeSize / 2 + guessRange.lowerBound;

  range_bar.style.transform = `
    translateX(${guessRange.lowerBound}%)
    scaleX(${rangeSize}%)
  `;
  showGuess(guessRange.lowerBound, rangeSize);
  // console.log(guessRange);
}

function reset() {
  guessRange.lowerBound = 0;
  guessRange.midpoint = 50;
  guessRange.upperBound = 100;
  showGuess(guessRange.lowerBound, 100);
  banner_text.innerHTML = `Retry as many times as you'd like!`;
}

function showGuess(lowerBound, rangeSize) {
  range_bar.style.transform = `
    translateX(${lowerBound}%)
    scaleX(${rangeSize}%)
  `;

  setTimeout(() => {
    const opacity = (guessRange.midpoint / 100).toFixed(2);
    guess_marker.style.left = guessRange.midpoint + "%";
    guess_number_container.style.left = guessRange.midpoint + "%";
    guess_number.innerHTML = opacity;
    overlay.style.opacity = opacity;
    setTimeout(() => {
      guessInProgress = false;
    }, 250);
  }, 500);
}

reset_btn.addEventListener("click", reset);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.