<div class="Demo" id="app">
  <div class="Demo-preview">
    <svg ref="avatar" class="Demo-result" width="512" height="512" viewBox="0 0 512 512">
      <rect class="Demo-bg" width="512" height="512" :fill="baseColor"/>
      <path d="M129.75,353.71a65.76,65.76,0,1,1,21.73-127.84,105.71,105.71,0,0,1,193.35-33.35,82,82,0,1,1,23,161.17v0Zm-9.66-155.33a21.57,21.57,0,1,0-21.57-21.57A21.57,21.57,0,0,0,120.09,198.38Zm79.82,88.72c12,0,16-10.74,16-24s-4-24-16-24-16,10.75-16,24S187.91,287.1,199.91,287.1Zm107.87,0c12,0,16-10.74,16-24s-4-24-16-24-16,10.75-16,24S295.78,287.1,307.78,287.1Z" fill="#fff"/>
    </svg>
  </div>
  <div class="Demo-controls">
    <div class="Demo-primaryControls">
      <h2 class="u-hiddenVisually">Color</h2>
      <div class="Swatches">
        <label class="Swatch" v-for="color in baseColors" :style="{ backgroundColor: color }">
          <input class="Swatch-input" type="radio" :value="color" v-model="baseColor">
          <svg class="Swatch-state" width="24" height="24" viewBox="0 0 24 24">
            <polyline points="4 12 9.08 18 20 6" fill="none" stroke="#0F1C3F" stroke-linecap="round" stroke-linejoin="round" stroke-width="7" opacity="0.3"></polyline>
            <polyline points="4 12 9.08 18 20 6" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"></polyline>
          </svg>
          <span class="u-hiddenVisually">{{color}}</span>
        </label>
      </div>
    </div>
    <label>
      Size (Pixels)
      <input class="Input" type="number" min="1" v-model="size">
    </label>
    <button class="Button Button--default" @click="download">
      Download
    </button>
  </div>
</div>
.Demo {
  background: #fff;
  display: grid;
  grid-template-rows: 1fr auto;
  min-height: 100vh;
}

@media (orientation: landscape) and (min-width: 40em) {
  .Demo {
    grid-template-columns: 1fr auto;
    grid-template-rows: auto;
  }
}

.Demo-preview {
  align-items: center;
  display: flex;
  justify-content: center;
}

.Demo-result {
  display: block;
  height: auto;
  margin: auto;
  max-height: 75vh;
  max-width: 16em;
  width: 100%;
}

.Demo-bg {
  transition: fill 0.2s ease;
}

.Demo-controls {
  align-self: center;
}

.Demo-controls {
  align-items: end;
  display: grid;
  grid-gap: 1em;
  grid-template-columns: 1fr 1fr;
  margin-left: auto;
  margin-right: auto;
  padding: 1.5em;
}

.Demo-primaryControls {
  grid-column: span 2;
}

.Swatches {
  display: grid;
  grid-auto-flow: column;
  grid-gap: 0.75em;
}

.Swatch {
  border: 3px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  display: block;
  line-height: normal;
  padding: 0.5em;
  position: relative;
}

.Swatch::before {
  content: "\00a0";
}

.Swatch-input {
  cursor: pointer;
  height: 100%;
  left: 0;
  opacity: 0;
  position: absolute;
  top: 0;
  width: 100%;
}

.Swatch-state {
  height: 1.5em;
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  transition: transform 0.2s ease, opacity 0.2s ease;
  width: 1.5em;
}

.Swatch-input:not(:checked) ~ .Swatch-state {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.5);
}
const app = new Vue({
  el: '#app',
  data: function(){
    return {
      size: 512,
      baseColors: [
        '#456BD9',
        '#F26941',
        '#068362',
        '#F14CA3',
        '#0F1C3F'
      ],
      baseColor: '#F26941'
    }
  },
  methods: {
    download(){
      saveSvgAsPng(this.$refs.avatar, 'avatar.png', {
        scale: this.size / 512 / window.devicePixelRatio
      });
    }
  }
});
View Compiled

External CSS

  1. https://cloudfour-patterns.netlify.com/assets/toolkit/styles/toolkit.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js
  2. https://unpkg.com/save-svg-as-png@1.4.14/lib/saveSvgAsPng.js