<main>
  <div class="settings">
    <div class="choices -emoji">
      <input type="radio" name="image" value="italy" id="italy" checked>
      <label tabindex="0" for="italy"> 🇮🇹</label>
      <input type="radio" name="image" value="france" id="france">
      <label tabindex="0" for="france"> 🇫🇷</label>
      <input type="radio" name="image" value="germany" id="germany">
      <label tabindex="0" for="germany">🇩🇪</label>
      <input type="radio" name="image" value="uk" id="uk">
      <label tabindex="0" for="uk">🇬🇧</label>
      <input type="radio" name="image" value="sweden" id="sweden">
      <label tabindex="0" for="sweden">🇸🇪</label>
      <input type="radio" name="image" value="iceland" id="iceland">
      <label tabindex="0" for="iceland">🇮🇸</label>
    </div>
    <div class="choices -opacity">
      <input type="radio" name="opacity" value="0" id="opacity-0">
      <label tabindex="0" for="opacity-0">0%</label>
      <input type="radio" name="opacity" value="0.25" id="opacity-25">
      <label tabindex="0" for="opacity-25">25%</label>
      <input type="radio" name="opacity" value="0.50" id="opacity-50" checked>
      <label tabindex="0" for="opacity-50">50%</label>
      <input type="radio" name="opacity" value="0.75" id="opacity-75">
      <label tabindex="0" for="opacity-75">75%</label>
      <input type="radio" name="opacity" value="1.0" id="opacity-100">
      <label tabindex="0" for="opacity-100">100%</label>
    </div>
    <div class="choices -text">
      <input type="radio" name="greyscale" value="true" id="greyscale-yes" checked>
      <label tabindex="0" for="greyscale-yes">Greyscale</label>
      <input type="radio" name="greyscale" value="false" id="greyscale-no">
      <label tabindex="0" for="greyscale-no">Color</label>
    </div>
    <div class="choices -text">
      <input type="radio" name="blend-mode" value="normal" id="blend-none">
      <label tabindex="0" for="blend-none">None</label>
      <input type="radio" name="blend-mode" value="multiply" id="blend-multiply" checked>
      <label tabindex="0" for="blend-multiply">Multiply</label>
      <input type="radio" name="blend-mode" value="screen" id="blend-screen">
      <label tabindex="0" for="blend-screen">Screen</label>
      <input type="radio" name="blend-mode" value="overlay" id="blend-overlay">
      <label tabindex="0" for="blend-overlay">Overlay</label>
      <input type="radio" name="blend-mode" value="darken" id="blend-darken">
      <label tabindex="0" for="blend-darken">Darken</label>
      <input type="radio" name="blend-mode" value="lighten" id="blend-lighten">
      <label tabindex="0" for="blend-lighten">Lighten</label>
      <input type="radio" name="blend-mode" value="color-dodge" id="blend-color-dodge">
      <label tabindex="0" for="blend-color-dodge">Dodge</label>
      <input type="radio" name="blend-mode" value="color-burn" id="blend-color-burn">
      <label tabindex="0" for="blend-color-burn">Burn</label>
      <input type="radio" name="blend-mode" value="hard-light" id="blend-hard-light">
      <label tabindex="0" for="blend-hard-light">Hard-light</label>
      <input type="radio" name="blend-mode" value="soft-light" id="blend-soft-light">
      <label tabindex="0" for="blend-soft-light">Soft-light</label>
      <input type="radio" name="blend-mode" value="difference" id="blend-difference">
      <label tabindex="0" for="blend-difference">Difference</label>
      <input type="radio" name="blend-mode" value="exclusion" id="blend-exclusion">
      <label tabindex="0" for="blend-exclusion">Exclusion</label>
      <input type="radio" name="blend-mode" value="hue" id="blend-hue">
      <label tabindex="0" for="blend-hue">Hue</label>
      <input type="radio" name="blend-mode" value="saturation" id="blend-saturation">
      <label tabindex="0" for="blend-saturation">Saturation</label>
      <input type="radio" name="blend-mode" value="color" id="blend-color">
      <label tabindex="0" for="blend-color">Color</label>
      <input type="radio" name="blend-mode" value="luminosity" id="blend-luminosity">
      <label tabindex="0" for="blend-luminosity">Luminosity</label>
    </div>
  </div>
  <figure>
    <div class="output"></div>
    <div class="credit"></div>
  </figure>
</main>

<footer>
  Built with ♥️ by <a href="https://twitter.com/glenmaddern">@glenmaddern</a>
  for <a href="https://frontend.center/ep13-blend-modes-background">Episode 13</a>
  of <a href="https://frontend.center/">Front End Center</a>.
</footer>
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
  margin: 0;
}

main {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.choices {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  margin: 1rem auto 0.5rem;
  max-width: 50rem;
}
.choices:first-child {
  margin-top: 0;
}

.choices input[type="radio"] {
  display: none;
}

.choices label {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0.5rem;
  border: 1px solid #eee;
  border-radius: 0.5rem;
  font-size: 1.75rem;
}

.choices.-emoji label {
  font-size: 3rem;
  height: 5rem;
  width: 5rem;
  padding-top: 0.15em;
}

.choices.-opacity label {
  font-size: 1.5rem;
  height: 3rem;
  width: 5rem;
}

.choices.-text label {
  font-size: 0.9rem;
  width: 6em;
  height: 2.5em;
  margin: 0.25rem 0.33rem;
}

.choices label:hover {
  border-color: #ccc;
}

.choices input[type="radio"]:checked + label {
  border-color: #aaa;
}

figure {
  width: calc(100vw - 4rem);
  max-width: 80rem;
  margin: 2rem 0;
}

.output {
  width: 100%;
  padding-top: 56.25%; /*16x9*/
}

.credit {
  text-align: right;

  margin: 0.25em auto;
  color: #444;
  font-size: 0.8rem;
  max-width: 1280px;
}

a {
  color: #277AFA;
  text-decoration: none;
  cursor: pointer;
}

footer {
  padding: 2rem 0.5rem;
  text-align: center;
}


@media (min-width: 1160px) and (max-height: 960px) {
  main {
    flex-direction: row;
    min-height: calc(100vh - 5.5rem);
  }
  .settings {
    width: 37rem;
  }
  figure {
    width: calc(100vw - 38rem);
    margin: 1rem 0 0;
  }
}

const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
const main = $('main')
const output = $('.output')
const credit = $('.credit')

const getRandomImage = images => images[Math.floor(Math.random() * images.length)]

const flags = opacity => ({
  france: `
    linear-gradient(to right,
      hsla(226, 100%, 29%, ${opacity}) 33%,
      hsla(0, 0%, 100%, ${opacity}) 33%,
      hsla(0, 0%, 100%, ${opacity}) 66%,
      hsla(355, 84%, 55%, ${opacity}) 66%
    )
  `,
  italy: `
    linear-gradient(to right,
      rgba(18, 143, 68, ${opacity}) 33%,
      hsla(0, 0%, 100%, ${opacity}) 33%,
      hsla(0, 0%, 100%, ${opacity}) 66%,
      hsla(355, 84%, 55%, ${opacity}) 66%
    )
  `,
  germany: `
    linear-gradient(to bottom,
      rgba(0, 0, 0, ${opacity}) 33%,
      rgba(218, 16, 23, ${opacity}) 33%,
      rgba(218, 16, 23, ${opacity}) 66%,
      rgba(251, 201, 10, ${opacity}) 66%
    )
  `,
  uk: `
    url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' opacity='${opacity}' viewBox='0 0 60 30'%3E%3CclipPath id='t'%3E%3Cpath d='M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z'/%3E%3C/clipPath%3E%3Cpath d='M0,0 v30 h60 v-30 z' fill='%2300247d'/%3E%3Cpath d='M0,0 L60,30 M60,0 L0,30' stroke='%23fff' stroke-width='6'/%3E%3Cpath d='M0,0 L60,30 M60,0 L0,30' clip-path='url%28%23t%29' stroke='%23cf142b' stroke-width='4'/%3E%3Cpath d='M30,0 v30 M0,15 h60' stroke='%23fff' stroke-width='10'/%3E%3Cpath d='M30,0 v30 M0,15 h60' stroke='%23cf142b' stroke-width='6'/%3E%3C/svg%3E%0A") no-repeat center / cover
  `,
  sweden: `
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' opacity='${opacity}' viewBox='0 0 16 10'%3E%3Crect width='16' height='10' fill='%23006aa7'/%3E%3Crect width='2' height='10' x='5' fill='%23fecc00'/%3E%3Crect width='16' height='2' y='4' fill='%23fecc00'/%3E%3C/svg%3E") no-repeat center / cover
  `,
  iceland: `
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' opacity='${opacity}' viewBox='0 0 500 360'%3E%3Cpath d='M0,0H500V360H0' fill='%23003897'/%3E%3Cpath d='M140,0h80V360h-80M0,140H500v80H0' fill='%23FFFFFF'/%3E%3Cpath d='M160,0h40V360h-40M0,160H500v40H0' fill='%23d72828'/%3E%3C/svg%3E") no-repeat center / cover
  `
})

const current = {}
const currentImage = () => `${current.image.urls.regular.replace(/&w=\d+|&fit=\w+/g, '')}&w=1280&h=720&fit=crop&auto=format${current.greyscale ? '&sat=-100' : ''}`
const draw = () => {
  const { opacity, country, image, blendMode } = current
  if (!image) return
  output.style.background = `
    ${flags(opacity)[country]},
    url(${currentImage()}) no-repeat center / cover
  `
  output.style.backgroundBlendMode = blendMode
  credit.innerHTML = `
    Photo by
    <a href="${image.user.portfolio_url}" target="_blank">${image.user.name}</a>.
    Sourced from <a href="${image.links.html}" target="_blank">Unsplash</a>.`
}

/* For keyboard accessibility */
document.addEventListener('keyup', e => {
  console.log(e)
  if (e.target.tagName === "LABEL" && (e.keyCode === 32 || e.keyCode === 13)) {
    e.target.click()
    e.preventDefault()
  }
})

/* Country selector */
const changeCountry = country => {
  current.country = country
  fetch(`https://frontend.center/ep13/${country}.json`)
    .then(response => response.json())
    .then(json => {
      if (current.country === country) {
        current.image = getRandomImage(json)
        draw()
      }
    })
}

changeCountry($('main input[name="image"][checked]').value)

$$('main input[name="image"]').forEach(input => {
  input.addEventListener('change', () => changeCountry(input.value))
  input.addEventListener('click',
    () => current.country === input.value && changeCountry(input.value))
})

/* Opacity selector */
const changeOpacity = opacity => {
  current.opacity = opacity
  draw()
}

changeOpacity($('main input[name="opacity"][checked]').value)

$$('main input[name="opacity"]').forEach(input => {
  input.addEventListener('change', e => changeOpacity(input.value))
})

/* Opacity selector */
const changeGreyscale = greyscale => {
  current.greyscale = (greyscale === "true")
  draw()
}

changeGreyscale($('main input[name="greyscale"][checked]').value)

$$('main input[name="greyscale"]').forEach(input => {
  input.addEventListener('change', e => changeGreyscale(input.value))
})

/* Blend mode selector */
const changeBlendMode = blendMode => {
  current.blendMode = blendMode
  draw()
}

changeBlendMode($('main input[name="blend-mode"][checked]').value)

$$('main input[name="blend-mode"]').forEach(input => {
  input.addEventListener('change', e => changeBlendMode(input.value))
})

View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.