#app.wrap
  .bg
  .colors()
    button.refresh(v-on:click="newColors").
      <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 23 23" version="1.1" x="0px" y="0px"><path d="M 12.109896,2.9653518 C 10.830826,2.9134678 9.5257058,3.132602 8.2817758,3.648946 c -3.9806,1.652399 -6.2540499,5.897846 -5.4179699,10.123046 0.8360799,4.2253 4.5540699,7.274132 8.8613301,7.269532 a 0.9995584,1.0006417 14.999899 1 0 0,-2 c -3.3667302,0 -6.2475202,-2.360557 -6.9004002,-5.660157 -0.65294,-3.2997 1.11025,-6.592765 4.22266,-7.884765 3.1124002,-1.292 6.6825102,-0.213669 8.5488302,2.582031 1.85391,2.77709 1.49946,6.460477 -0.8418,8.845703 l 0.0781,-2.365234 a 1.0001,1.0001 0 0 0 -0.98242,-1.046875 1.0001,1.0001 0 0 0 -1.01758,0.982422 l -0.15235,4.59375 a 1.0001,1.0001 0 0 0 1.03321,1.033203 l 4.5957,-0.152344 a 1.0001,1.0001 0 1 0 -0.0664,-1.998047 l -1.79492,0.06055 c 2.74739,-3.056097 3.10892,-7.618693 0.80859,-11.064453 -1.64326,-2.461525 -4.33252,-3.887808 -7.14648,-4.0019532 z"/></svg>
    button.expand(v-on:click="newColors").
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23" x="0px" y="0px"><defs><style>.cls-1{fill:none;stroke:#000;stroke-linecap:round;stroke-linejoin:round;}</style></defs><polyline class="cls-1" points="18.5 14.5 21.5 11.5 18.5 8.5"/><polyline class="cls-1" points="4.5 8.5 1.5 11.5 4.5 14.5"/><line class="cls-1" x1="1.5" y1="11.5" x2="8.5" y2="11.5"/><line class="cls-1" x1="14.5" y1="11.5" x2="21.5" y2="11.5"/></svg>
    color(v-for="(c, i) in colors", v-bind:name="names[i]" v-bind:color="c")

a.fulllink(href="https://farbvelo.elastiq.ch/", target="_blank")
  strong
    span F
    span u
    span l
    span l 
    span
    span V
    span e
    span r
    span s
    span i
    span o
    span n 
View Compiled
@import url('https://fonts.googleapis.com/css?family=Space+Mono');

button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;

  background: transparent;

  /* inherit font & color from ancestor */
  color: inherit;
  font: inherit;

  /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
  line-height: normal;

  /* Corrects font smoothing for webkit */
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;

  /* Corrects inability to style clickable `input` types in iOS */
  -webkit-appearance: none;
}

html {
  font-size: calc(.75rem + 1.15vh);
  font-family: 'Space Mono', monospace;
}

body {
  height: 100vh;
  background: #212121;
  overflow: hidden;
}

input {
  position: absolute;
  z-index: 2;
}

.wrap, .bg {
  position: absolute;
  top: 0; left: 0; bottom: 0; right: 0;
}

.bg {
  //opacity: .9;
  //clip-path: inset(12vmin 26% 12vmin 26%);
}

.colors {
  position: absolute;
  left: 50%;
  bottom: 10vmin; top: 10vmin;
  display: flex;
  flex-direction: column;
  cursor: pointer;

  transform: translateX(-50%);
  width: 52%;
  @media (max-width: 700px){
    width: 75%;
  }
}

.colors .color {
  position: relative;
  display: flex;
  @media (max-width: 700px){
    display: block;
    padding: 1rem;
  }
  align-items: center;
  padding: 0 1rem;
  flex-grow: 1;

  &::after {
    opacity: .5;
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0; top: 0;
    box-shadow: 0 0 10rem currentColor;
  }
  /*
  border: solid currentColor;
  border-width: 0 2px;
  */
  transition: 200ms color linear, 200ms background-color linear, 300ms padding-bottom cubic-bezier(.7, .3, .3, 1) 100ms;
  min-height: 0.1vmax;
  
  &:last-child {
    padding-bottom: #{1.61803 * 4rem};
  }
  &:hover {
    padding-bottom: #{1.61803 * 4rem};
  }
}

.colors:hover .color {
  &:last-child {
    padding-bottom: 1rem;
  }
  &:hover {
    padding-bottom: #{1.61803 * 4rem};
  }
}

.label {
  flex-grow: 1;
  padding-right: 1rem;
  font-size: .75em;
  @media (max-width: 700px) { 
    font-size: .6em;
    margin-bottom: .15em;
  }
}
.name {
  font-size: .8em;
  text-align: right;
  @media (max-width: 700px){
    text-align: left;
  }
}
.refresh, .expand {
  z-index: 1;
  position: absolute;
  top: 100%; left: 100%;
  margin: 0;
  padding: 0;
  transform: translate(-60%,-60%);
  font-size: 2em;
  width: 1.2em; height: 1.2em;
  background: #000;
  color: #fff;
  
  border-radius: 50%;
  outline: 0;
  box-shadow: 0 0 2rem rgba(#000, .2);
  cursor: pointer;
  svg {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%,-50%);
    animation-name: rotate; 
    animation-duration: 3s; 
    width: .6em; height: .6em;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
    animation-play-state: paused;
    line-height: 1;
    fill: #fff;
  }
  &:hover {
    svg {
      animation-play-state: running;
    }
  }
}

.expand {
  display: none;
  margin-left: -1.4em;
  svg {
    fill: none;
    stroke: #fff;
    stroke-width: 1.5;
    animation: none;
    width: .75em; height: .75em;
  }
}

@keyframes rotate {
  from {transform: translate(-50%,-50%) rotate(0deg);}
  to {transform: translate(-50%,-50%) rotate(360deg);}
}

.fulllink {
  position: fixed;
  top: 0;
  right: 0;
  font-size: .65em;
  text-decoration: none;
  padding: 1.2em 3.5em;
  width: 10ex;
  text-align: center;
  transform: translateX(30%) rotate(45deg);
  
  span {
    position: relative;
    animation: .5s ease-in 1s infinite alternate jumpup;
    animation-duration: 1s;
    
    @for $i from 1 through 20 {
      &:nth-child(#{$i}) {
        animation-delay: 1000ms + $i * 100ms;
      }
    }
  }
}

@keyframes jumpup {
  100% {
    opacity: .1;
  }
}
View Compiled
// full version: https://farbvelo.elastiq.ch/
// color-names: https://github.com/meodai/color-nameshttps://github.com/meodai/color-names

const shuffleArray = arr => arr
  .map(a => [Math.random(), a])
  .sort((a, b) => a[0] - b[0])
  .map(a => a[1]);

const random = (min, max) => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

var generateRandomColors = (total, mode = 'lab', padding = .175, parts = 4) => {
  
  let colors = [];
  const part = Math.floor(total / parts);
  const reminder = total % parts;

  // hues to pick from
  const baseHue = random(0, 360);
  const hues = [0, 60, 120, 180, 240, 300].map(offset => {
    return (baseHue + offset) % 360;
  });

  //  low saturated color
  const baseSaturation = random(5, 40);
  const baseLightness = random(0, 20);
  const rangeLightness = 90 - baseLightness;

  colors.push( HUSL.toHex(
    hues[0],
    baseSaturation,
    baseLightness * random(.25, .75)
  ) );

  for (let i = 0; i < (part - 1); i++) {
    colors.push( HUSL.toHex(
      hues[0],
      baseSaturation,
      baseLightness + (rangeLightness * Math.pow(i/(part - 1), 1.5))
    ) );
  }

  // random shades
  const minSat = random(50, 70);
  const maxSat = minSat + 30;
  const minLight = random(45, 80);
  const maxLight = Math.min(minLight + 40, 95);

  for (let i = 0; i < (part + reminder - 1); i++) {
    colors.push( HUSL.toHex(
      hues[random(0, hues.length - 1)],
      random(minSat, maxSat),
      random(minLight, maxLight) 
    ) )
  }
  
  colors.push( HUSL.toHex(
    hues[0],
    baseSaturation,
    rangeLightness
  ) );
  
  //colors = shuffleArray(colors);
  
  return chroma.scale(colors).padding(padding).mode(mode).colors(total);
}

function getContrastColor(color) {
  let currentColor = chroma( color );
  let lum = currentColor.luminance();
  let contrastColor;
  if ( lum < 0.15 ) {
    contrastColor = currentColor.set('hsl.l', '+.25');  
  } else {
    contrastColor = currentColor.set('hsl.l', '-.35');
  }
  return contrastColor;
}

Vue.component('color', {
  props: ['color', 'name'],
  template: `<div class="color" v-bind:style="{background: color, color: textColor}">
              <div class="label">{{ color }}</div>
              <div class="name">{{ name.name }}</div>
             </div>`,
  computed: {
    /*name: function () {
      console.log(name)
      return getClosestNamedColor( this.color ).name;
    },*/
    textColor: function () {
      return getContrastColor(this.color);
    }
  }
});

let colors = new Vue({
  el: '#app',
  data: () => {
    return {
      colors: [],
      names: [],
      amount: 6,
    }
  },
  methods: {
    getNames: function () {
      fetch(`https://api.color.pizza/v1/${this.colors.join().replace(/#/g, '')}?noduplicates=true&goodnamesonly=true`)
      .then(data => data.json())
      .then(data => {
        this.names = data.colors;
      });
    },
    newColors: function () {
      let colorArr = generateRandomColors(this.amount)
      this.colors = colorArr;
      this.getNames();
      let gradient = [...colorArr];
      gradient[0] += ' 12vmin'
      gradient[gradient.length - 1] += ' 69%'
      //url("https://www.transparenttextures.com/patterns/concrete-wall.png"),  
      document.querySelector('.bg').style['background-image'] = `
        linear-gradient(to bottom, ${gradient.join(',')})
      `;
      document.querySelector('.refresh').style.background = this.colors[this.colors.length - 1];
      document.querySelector('.expand').style.background = this.colors[this.colors.length - 1];
      
      document.querySelector('.fulllink').style.background = this.colors[1];
      document.querySelector('.fulllink').style.color = getContrastColor(this.colors[1]);
    }
  },
  mounted: function () {
    this.newColors();
  }
});
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/husl/6.0.1/husl.js
  3. https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.2.2/chroma.min.js