ARTLUNG LAB Share

Created September 2010. jQuery removed, replaced with vanilla JS 2024.

CSS3 Smiley

 
 
 
 

Figure

Ground

HTML

    <div id="smiley-wrapper" class="figure">
        <div class="smiley ground">
            <div class="left-eye eye figure">&nbsp;</div>
            <div class="right-eye eye figure">&nbsp;</div>
            <div class="mouth figure">&nbsp;</div>
            <div class="mouth-cover ground">&nbsp;</div>
        </div>
    </div>

    <div id="controls">
        <div class="segment">
            <h2>Figure <code id="figureText"></code></h2>
            <div class="r">
                <label><input type="radio" value="0" name="fr" checked> 0</label>
                <label><input type="radio" value="3" name="fr"> 3</label>
                <label><input type="radio" value="6" name="fr"> 6</label>
                <label><input type="radio" value="9" name="fr"> 9</label>
                <label><input type="radio" value="C" name="fr"> C</label>
                <label><input type="radio" value="F" name="fr"> F</label>
            </div>
            <div class="g">
                <label><input type="radio" value="0" name="fg" checked> 0</label>
                <label><input type="radio" value="3" name="fg"> 3</label>
                <label><input type="radio" value="6" name="fg"> 6</label>
                <label><input type="radio" value="9" name="fg"> 9</label>
                <label><input type="radio" value="C" name="fg"> C</label>
                <label><input type="radio" value="F" name="fg"> F</label>
            </div>
            <div class="b">
                <label><input type="radio" value="0" name="bg" checked> 0</label>
                <label><input type="radio" value="3" name="bg"> 3</label>
                <label><input type="radio" value="6" name="bg"> 6</label>
                <label><input type="radio" value="9" name="bg"> 9</label>
                <label><input type="radio" value="C" name="bg"> C</label>
                <label><input type="radio" value="F" name="bg"> F</label>
            </div>
        </div>
        <div class="segment">
            <h2>Ground <code id="groundText"></code></h2>
            <div class="r">
                <label><input type="radio" value="0" name="gr"> 0</label>
                <label><input type="radio" value="3" name="gr"> 3</label>
                <label><input type="radio" value="6" name="gr"> 6</label>
                <label><input type="radio" value="9" name="gr"> 9</label>
                <label><input type="radio" value="C" name="gr"> C</label>
                <label><input type="radio" value="F" name="gr" checked> F</label>
            </div>
            <div class="g">
                <label><input type="radio" value="0" name="gg"> 0</label>
                <label><input type="radio" value="3" name="gg"> 3</label>
                <label><input type="radio" value="6" name="gg"> 6</label>
                <label><input type="radio" value="9" name="gg"> 9</label>
                <label><input type="radio" value="C" name="gg"> C</label>
                <label><input type="radio" value="F" name="gg" checked> F</label>
            </div>
            <div class="b">
                <label><input type="radio" value="0" name="gb"> 0</label>
                <label><input type="radio" value="3" name="gb"> 3</label>
                <label><input type="radio" value="6" name="gb"> 6</label>
                <label><input type="radio" value="9" name="gb"> 9</label>
                <label><input type="radio" value="C" name="gb"> C</label>
                <label><input type="radio" value="F" name="gb" checked> F</label>
            </div>
        </div>
    </div>

SCSS

div#smiley-wrapper {
  box-sizing: border-box;
  width: 200px;
  padding: 50px;
  margin: 0 auto;
}

.smiley {
  height: 100px;
  width: 100px;
  position: relative;
  border-radius: 50px;

  .eye {
    height: 12px;
    width: 12px;
    border-radius: 6px;
    position: absolute;
    overflow: hidden;
  }

  .left-eye {
    top: 20px;
    left: 30px;
  }

  .right-eye {
    top: 20px;
    right: 30px;
  }

  .mouth {
    border-bottom-right-radius: 25px;
    border-bottom-left-radius: 25px;
    height: 25px;
    position: absolute;
    width: 50px;
    left: 25px;
    top: 60px;

  }

  .mouth-cover {
    border-bottom-right-radius: 25px;
    border-bottom-left-radius: 25px;
    height: 25px;
    position: absolute;
    width: 40px;
    left: 30px;
    top: 50px;
  }

}

/* controls */
div#controls {
  text-align: center;
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  justify-content: center;
  align-items: center;
  gap: 1rem;

  > * {
    padding: 1rem;

    a {
      display: inline-block;
    }
  }

  h2 {
    margin: 0;
  }

  .segment {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    width: 20rem;

    > * {
      width: 100%;
    }
  }

  label {
    background-color: #666;
    color: #fff;
    padding: 5px 10px;
    cursor: pointer;

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

    &:has(input[type="radio"]:checked) {
      background-color: #0f0;
      color: #fff;
    }
  }
}

.figure {
  background-color: #000;
}

.ground {
  background-color: #fff;

}

#wrapper, #controls, #credits {
  width: 300px;
  margin: 0 auto;
}

JavaScript

document.addEventListener('DOMContentLoaded', function () {
    const labels = document.querySelectorAll('#controls div.segment div label');
    for (var i = 0; i < labels.length; i++) {
        labels[i].addEventListener('click', updateColors);
        labels[i].addEventListener('mouseover', function () {
            this.click();
        });
        labels[i].addEventListener('touchstart', function () {
            this.click();
        });
    }
    if (document.location.hash.length > 1 && document.location.hash.split('#')[1].length === 6) {
        updateInputsFromHash(document.location.hash);
    }
    updateColors();
});

// listen for popstate event
window.addEventListener('popstate', function (event) {
    if (event.state && event.state.color) {
        updateInputsFromHash('#' + event.state.color);
        updateColors();
    }
});


const updateColors = function () {
    var figureColor = '#';
    var groundColor = '#';
    var inputs = document.querySelectorAll('#controls div.segment div input[type="radio"]:checked');
    for (var i = 0; i < inputs.length; i++) {
        if (i < 3) {
            figureColor += String(inputs[i].value);
        } else {
            groundColor += String(inputs[i].value);
        }
    }
    document.querySelectorAll('.ground').forEach(function (element) {
        element.style.backgroundColor = groundColor;
        element.style.color = groundColor;
    });
    document.querySelectorAll('.figure').forEach(function (element) {
        element.style.backgroundColor = figureColor;
        element.style.color = figureColor;
    });
    document.querySelector('#figureText').textContent = figureColor;
    document.querySelector('#groundText').textContent = groundColor;
    // new pushState
    var combinedHexValue = '#' + figureColor.replace('#', '') + groundColor.replace('#', '');
    var newUrl = document.location.href.split('#')[0] + combinedHexValue;
    history.pushState({
        color: combinedHexValue
    }, 'Color State', newUrl);

}

const updateInputsFromHash = function (colorUrl) {
    if (!colorUrl) {
        colorUrl = document.location.href;
    }
    if (colorUrl.length && colorUrl.split('#')[1].length > 1) {
        let colors = colorUrl.split('#')[1].toUpperCase();
        colors = colors.replace(/[^0369CF]/g, '0');
        var divs = document.querySelectorAll('#controls div.segment div');
        for (var i = 0; i < colors.length; i++) {
            divs[i].querySelector('input[type="radio"][value="' + colors[i] + '"]').checked = true;
        }
    }
};