Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <main>
  <transition name="modal">
    <div class="cue" @click="toggleModal()" v-show="!showModal">
      <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="403.98px" height="403.98px" viewBox="0 0 403.98 403.98" style="enable-background:new 0 0 403.98 403.98;" xml:space="preserve">
        <path d="M334.131,220.651c-25.174,0-45.66,20.478-45.66,45.652s20.486,45.66,45.66,45.66c25.179,0,45.653-20.485,45.653-45.66    C379.792,241.129,359.31,220.651,334.131,220.651z M334.131,305.231c-21.463,0-38.928-17.456-38.928-38.912    c0-21.468,17.465-38.932,38.928-38.932c21.46,0,38.933,17.464,38.933,38.932C373.063,287.771,355.599,305.231,334.131,305.231z"></path>
        <path d="M67.75,311.973c25.183,0,45.657-20.479,45.657-45.653c0-25.179-20.474-45.66-45.657-45.66    c-25.176,0-45.659,20.481-45.659,45.66C22.091,291.479,42.573,311.973,67.75,311.973z M67.75,227.388    c21.466,0,38.928,17.456,38.928,38.916c0,21.468-17.462,38.928-38.928,38.928c-21.456,0-38.924-17.46-38.924-38.928    C28.825,244.844,46.286,227.388,67.75,227.388z"></path>
        <rect x="179.475" y="260.629" width="37.039" height="11.361"></rect>
        <path d="M233.557,258.514c4.188,0,7.574,3.397,7.574,7.582c0,4.18-3.387,7.573-7.574,7.573c-4.18,0-7.574-3.394-7.574-7.573    C225.982,261.911,229.377,258.514,233.557,258.514z"></path>
        <path d="M163.279,258.514c4.188,0,7.58,3.397,7.58,7.582c0,4.18-3.392,7.573-7.58,7.573c-4.182,0-7.574-3.394-7.574-7.573    C155.705,261.911,159.097,258.514,163.279,258.514z"></path>
        <path d="M67.75,252.205c7.789,0,14.102,6.309,14.102,14.099S75.538,280.41,67.75,280.41c-7.787,0-14.1-6.316-14.1-14.106    S59.963,252.205,67.75,252.205z"></path>
        <path d="M334.131,252.205c7.783,0,14.091,6.309,14.091,14.099s-6.308,14.106-14.091,14.106c-7.786,0-14.105-6.316-14.105-14.106    S326.345,252.205,334.131,252.205z"></path>
        <path d="M21.049,351.521c0,3.719,3.014,6.732,6.727,6.732h39.56c3.721,0,6.727-3.014,6.727-6.732v-7.213h258.8v7.213    c0,3.719,3.025,6.732,6.736,6.732h39.553c3.723,0,6.736-3.014,6.736-6.732v-7.213h18.094V181.515h-16.827v-39.982    c0-10.203-8.312-18.51-18.515-18.51h-19.925l33.808-61.626c4.139-0.223,7.445-3.619,7.445-7.819c0-4.332-3.519-7.851-7.858-7.851    s-7.858,3.519-7.858,7.851c0,2.008,0.789,3.821,2.016,5.214l-35.233,64.225H34.504c-10.213,0-18.51,8.301-18.51,18.51v39.98H0    V344.3h21.049V351.521z M237.765,297.658h-79.537v-4.585l9.161-4.252h61.215l9.161,4.252V297.658z M259.016,297.658h-14.519    v-8.877l-14.415-6.685h-64.188l-14.405,6.685v8.877h-14.521v-63.125h122.04v63.125H259.016z M383.795,226.959    c0,4.003-3.129,7.253-7.061,7.521c6.648,8.877,10.632,19.877,10.632,31.823c0,13.333-4.938,25.483-13.032,34.828    c0.336-0.052,0.677-0.116,1.029-0.116c4.185,0,7.591,3.406,7.591,7.574c0,4.188-3.406,7.582-7.591,7.582    c-4.172,0-7.573-3.395-7.573-7.582c0-0.381,0.06-0.766,0.128-1.15c-9.193,7.555-20.967,12.099-33.787,12.099    c-12.831,0-24.593-4.544-33.782-12.099c0.052,0.385,0.112,0.77,0.112,1.15c0,4.188-3.395,7.582-7.574,7.582    c-4.184,0-7.574-3.395-7.574-7.582c0-4.168,3.391-7.574,7.574-7.574c0.353,0,0.689,0.064,1.042,0.116    c-8.103-9.345-13.032-21.495-13.032-34.828c0-13.276,4.901-25.407,12.94-34.729c-0.036,0-0.076,0.008-0.104,0.008    c-4.188,0-7.582-3.39-7.582-7.573c0-4.188,3.395-7.574,7.582-7.574c4.176,0,7.574,3.386,7.574,7.574    c0,0.16-0.049,0.312-0.049,0.465c9.049-7.13,20.438-11.405,32.873-11.405c13.232,0,25.308,4.845,34.608,12.831    c0.513-3.679,3.646-6.531,7.466-6.531C380.401,219.385,383.795,222.775,383.795,226.959z M343.172,133.117h25.467    c4.646,0,8.416,3.779,8.416,8.416v39.56h-34.512v-1.475c0-3.605-3.014-6.524-6.725-6.524h-8.54l13.005-29.244l-2.409-1.078    L343.172,133.117z M26.09,141.532c0-4.637,3.775-8.416,8.414-8.416h300.978l-3.787,6.911l-3.71-1.661l-15.441,34.718h-19.656    c-3.719,0-6.732,2.919-6.732,6.524c0,0.509,0,0.996,0,1.475H176.327v-6.306c0-3.721-3.02-6.726-6.734-6.726h-12.615    c-3.721,0-6.735,3.014-6.735,6.726v6.314h-7.153v-6.314c0-3.721-3.012-6.726-6.735-6.726h-12.621    c-3.723,0-6.732,3.014-6.732,6.726v6.314h-7.149v-6.314c0-3.721-3.023-6.726-6.734-6.726H90.5c-3.719,0-6.737,3.014-6.737,6.726    v6.314H76.61v-6.314c0-3.721-3.011-6.726-6.726-6.726H57.262c-3.723,0-6.729,3.014-6.729,6.726v6.314H26.09V141.532z     M18.937,308.59c0-4.168,3.4-7.574,7.574-7.574c0.353,0,0.697,0.064,1.036,0.116c-8.093-9.345-13.022-21.495-13.022-34.828    c0-13.276,4.899-25.407,12.938-34.729c-0.04,0-0.07,0.008-0.112,0.008c-4.182,0-7.574-3.39-7.574-7.573    c0-4.188,3.392-7.574,7.574-7.574c4.178,0,7.578,3.386,7.578,7.574c0,0.16-0.042,0.312-0.05,0.465    c9.055-7.13,20.45-11.405,32.871-11.405c13.235,0,25.309,4.845,34.612,12.831c0.519-3.679,3.651-6.531,7.476-6.531    c4.173,0,7.574,3.39,7.574,7.573c0,4.004-3.116,7.254-7.057,7.522c6.658,8.876,10.63,19.881,10.63,31.822    c0,13.333-4.929,25.483-13.026,34.829c0.339-0.052,0.684-0.116,1.036-0.116c4.178,0,7.578,3.406,7.578,7.578    c0,4.184-3.4,7.582-7.578,7.582c-4.182,0-7.574-3.398-7.574-7.582c0-0.385,0.064-0.777,0.118-1.15    c-9.191,7.55-20.953,12.095-33.789,12.095c-12.828,0-24.587-4.545-33.779-12.095c0.055,0.373,0.114,0.766,0.114,1.15    c0,4.184-3.388,7.582-7.574,7.582C22.337,316.18,18.937,312.794,18.937,308.59z"></path>
      </svg>
      <!-- icon from: http://www.flaticon.com/free-icon/boom-box-radio-with-antenna_26604 -->
    </div>
  </transition>
  
  <!--  The Modal -->
  <modal class="skin" v-if="showModal" :show-mask="showMask" :show-x="showX" :flip-me="flipMe" @toggle="toggleModal()">

    <!-- front - side A -->
    <header slot="front-header">
      <div class="model row">
        <span>BG MixMaster - 90</span>
        <small>CSS • HTML • JS <br />Vue Components</small>
      </div>
      <section class="params">
        <div>
          <label class="switch"><input type="checkbox" v-model="track.isolate" @click="setSize(track)" /> <span>Isolate</span></label>
          <label class="switch"><input type="checkbox" v-model="track.lineY" @click="setLineY(track)" /> <span>line A</span></label>
          <label class="menu">
            <select v-model="track.unit_y" @change="setXYUnit(track, 'y')">
              <option v-for="unit in units">{{unit}}</option>
            </select>
          </label>
          <label class="switch"><input type="checkbox" v-model="track.lineX" @click="setLineX(track)" /> <span>line B</span></label>
          <label class="menu">
            <select v-model="track.unit_x" @change="setXYUnit(track, 'x')">
              <option v-for="unit in units">{{unit}}</option>
            </select>
          </label>
          <label class="row flex-1 space-around align-center">
            <span><input type="range" v-model="track.l" :disabled="!track.lineY && !track.lineX" min="0" max="100" @input="setLine(track)" /></span>
          </label>
          <label class="menu flex-1">
            <select v-model="track.unit_l" @change="setLUnit(track, 'l')" :disabled="!track.lineY && !track.lineX">
              <option v-for="unit in units" :value="unit">{{track.l}} {{unit}}</option>
            </select>
          </label>
          <label class="switch"><input type="checkbox" v-model="track.lockstep" /><span><i class="fa fa-fw" :class="track.lockstep ? 'fa-lock' : 'fa-unlock'"></i> a / b</span></label>
        </div>
      </section>
    </header>
    <div slot="front-body" class="controls">
      <datalist id="grid">
        <option label="2">2</option>
        <option label="5">5</option>
        <option label="10">10</option>
        <option label="20">20</option>
        <option label="25">25</option>
        <option label="33">33</option>
        <option label="50">50</option>
        <option label="75">75</option>
        <option label="100">100</option>
      </datalist>
      <section class="case flex-1">
        <div class="flex-1 row space-around">
          <rad-slider class="spool" name="a" :degree="track.a" lock-with="b" event-name="set-degree-a" @set-degree-a="rotate">
            <div>
              <input type="range" v-model="track.y" min="1" max="100" @input="setY(track)" list="grid" />
              <span>A:{{track.y}}</span>
            </div>
          </rad-slider>
          <rad-slider class="spool" name="b" :degree="track.b" lock-with="a" event-name="set-degree-b" @set-degree-b="rotate">
            <div>
              <input type="range" v-model="track.x" min="1" max="100" @input="setX(track)" list="grid" />
              <span>B:{{track.x}}</span>
            </div>
          </rad-slider>
        </div>
      </section>

      <!-- palette -->
      <section class="palette" :class="{open: isPaletteOpen}" :style="{'--color': currentColor}">
        <div class="flex-1">
          <div class="flex-1 row align-center space-around">
            <span>
              Saturation {{color.s}}%<br />
              <input type="range" v-model="color.s" min="0" max="100" />
            </span>
          </div>
          <div class="flex-1 row align-center space-around">
            <rad-slider class="color-wheel" name="h" :degree="color.h" event-name="set-degree-h" @set-degree-h="updateHue">
            <span>{{currentColor}}</span>
            </rad-slider>
          </div>
          <div class="flex-1 row align-center space-around">
            <span>
              Luminance {{color.l}}%<br />
              <input type="range" v-model="color.l" min="0" max="100" />
            </span>
          </div>
          <div class="palette__key">{{translator[currentKey]}}</div>
          <div class="palette__x" @click="hidePalette">&times;</div>
        </div>
      </section>
    </div>
    <footer slot="front-footer">
      <button @click="flipModal"><i class="fa fa-arrow-right xfa-2x"></i></button>
      <div class="color-well color-bg" @click="togglePalette('bg')"></div>
      <div class="color-well color-b" @click="togglePalette('c1')"></div>
      <div class="color-well color-a" @click="togglePalette('c2')"></div>
      <div class="trap"></div>
      <div class="side">A</div>
    </footer>
 
    <!-- back - side B -->
    <header slot="back-header">
      <div class="model row">
        <span>BG MM-90 Code</span>
        <small>CSS4 <br />Custom Properties</small>
      </div>
      <div class="params">
<!--         <div> -->
          <form action="https://codepen.io/pen/define" method="POST" target="_blank">
            <span class="now-playing">Now Playing:</span>
            <input class="input-text" type="text" v-model="params.title" placeholder="name this track..." />
            <input type="hidden" name="data" v-model="params" />
            <span class="switch">
              <button type="submit" value="Save"><i class="fa fa-fw fa-codepen"></i> Save</button>
            </span>
          </form>
<!--         </div> -->
      </div>
    </header>
    <div slot="back-body" class="controls">
      <section class="case flex-1">
        <textarea class="height-100" name="" id="" rows="5" readonly>/* copy & paste this CSS code into your project */
{{params.css}}</textarea>
      </section>
    </div>
    <footer slot="back-footer">
      <div>
        <button @click="flipModal"><i class="fa fa-arrow-left"></i></button>
      </div>
      <div class="trap"></div>
      <div class="side">B</div>
    </footer>
  </modal>

  <!--   Track List -->
  <div class="track-list">
    <h4 drawer @click="togglePlaylist">Playlist</h4>
    <ul>
      <li v-for="item in tracks" @click="playTrack(item.name)">
        <i class="fa fa-check-circle" v-if="item.name == track.name"></i>
        <i class="fa fa-circle-thin" v-else></i>
        <span>{{item.name}}</span>
      </li>
    </ul>
  </div>

</main>
              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css?family=Pontano+Sans|Covered+By+Your+Grace');

:root {
  --base: #363639;
  --base-light: #464649;
  --base-dark: #262629;
  --base-shade: #303033;
  --base-text: #7db69d;
  --label: #e9dfde;
  --label-text: #b18373;
  --accent: rgba(35,35,35,.7);
  --button: rgba(40,40,43,.7);
}

// the thing
body {
  background-image: 
    repeating-linear-gradient(var(--a), 
      unquote('rgba(var(--bg-rgb), 0)') 0,
      var(--line-y),
      var(--c2) calc(var(--y) - var(--l)),
      var(--c2) var(--y)
    ),
    repeating-linear-gradient(var(--b), 
      unquote('rgba(var(--bg-rgb), 0)') 0, 
      var(--line-x),
      var(--c1) calc(var(--x) - var(--l)),
      var(--c1) var(--x)
    );
  background-color: var(--bg);
  background-size: var(--size);
}

// defaults
*, *::before, *::after {box-sizing: border-box;}

body {
  margin: 0;
  height: 100vh;
  font-family: 'Pontano Sans', Helvetica, sans-serif;
  display: flex;
}

main {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

// inputs and buttons
button,
select {
  border: none;
  border: 1px inset var(--button);
  padding: .7em 1em;
  border-radius: .35em;
  color: #fff;
  font: inherit;
  background: var(--button);
  cursor: pointer;
  outline: none;
  will-change: transform;
  transition: transform 200ms ease-out;
  &:active {
    transform: scale(.9);
  }
  &[disabled] {
    opacity: .7;
    cursor: not-allowed;
  }
}

::placeholder {
  opacity: .4;
  font-family: 'Pontano Sans', Helvetica, sans-serif;
  font-size: .7em;
  color: var(--label-text);
}
.menu {
  select {
    appearance: none;
    display: inline-block;
    padding: .4em;
    font-size: inherit;
    text-transform: uppercase;
    color: inherit;
    border: none;
    background: rgba(0,0,0,.1);
    border-radius: .2em;
  }
}

.cue {
  width: 7em;
  height: 7em;
  padding: 1em;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  will-change: transform;
  transition: transform 200ms cubic-bezier(1, 0, 0.2, 1.25);
  &:active {
    transform: scale(.9);
  }
  svg {
    fill: var(--c2-contrast);
  }
}
label {
  display: block;
  padding: .3em;
}
textarea {
  font-size: 1.2em;
  font-family: monospace;
  padding: .5em;
  width: 100%;
  height: 100%;
  border: 1px solid #000;
  resize: none;
  flex: 1;
  outline: none;
  background: var(--accent);
  color: rgba(255, 255, 255, .7);
}
.input-text {
  width: 100%;
  font-size: 2em;
  font-family: 'Covered By Your Grace', fantasy, serif;
  color: #5c69c2;
  border: 0;
  background: transparent;
  padding: 0em .5em;
  line-height: 1;
  outline: none;
}

.color-well {
  width: 2.75em;
  height: 2.75em;
  padding: 0;
  border-radius: 50%;
  overflow: hidden;
  border: .3em solid var(--base-shade);
  box-shadow: 0 0 0 1px #000 inset, 0 .3em 1em rgba(0,0,0,.8) inset;
  cursor: pointer;
  transform: translateZ(1em);
  &.color-bg {
    background: var(--bg);
  }
  &.color-a {
    background: var(--c2);
  }
  &.color-b {
    background: var(--c1);
  }
  input[type=color] {
    opacity: 0;
    font-size: 0;
  }
}

// structure & layout
header {
  font-size: 1.3em;
  font-weight: 200;
  flex: 1;
}

.controls {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  border: 3px solid var(--base);
  border-color: var(--base-dark) var(--base-light) var(--base-light) var(--base-dark);
  box-shadow: 0 0 1.5em -.5em #000 inset;
  overflow: hidden;
  section {
    z-index: 1;
  }
}

section {
  display: flex;
  align-items: center;
  > * {
    display: flex;
  }
}
.height-100 {
  height: 100%;
}
.row {
  display: flex;
}
.flex-1 {
  flex: 1;
}
.flex-2 {
  flex: 2;
}
.column {
  display: flex;
  flex-direction: column;
}
.space-around {
  justify-content: space-around;
}
.center {
  justify-content: center;
}
.align-center {
  align-items: center;
}
.switch {
  cursor: pointer;
  input {
    opacity: 0;
    position: absolute;
  }
  input + * {
    display: inline-block;
    padding: .4em;
    color: inherit;
    background: rgba(0,0,0,.1);
    border-radius: .2em;
  }
  input:checked + * {
    background: var(--accent);
  }
  button {
    display: inline-block;
    text-transform: uppercase;
    padding: .4em;
    margin: .3em;
    color: inherit;
    background: rgba(0,0,0,.1);
    border-radius: .2em;
    border: none;
    white-space: nowrap;
  }
}

.rad-slider {
  --degree: 0deg;
  --degree-reverse: calc(0deg - var(--degree));
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0em;
  border-radius: 50%;
  transform: rotate(var(--degree));
  .label {
    user-select: none;
    pointer-events: none;
    transform: rotate(var(--degree-reverse));
  }
  &:hover {
    cursor: -webkit-grab;
  }
  &.moving {
    cursor: -webkit-grabbing;
  }
}

.rad-slider {
  &:not(.dirty) .label {
    opacity: 1;
  }
  .label {
    position: absolute;
    left: 50%;
    top: 5%;
    width: 6em;
    height: 6em;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 2000ms linear 2000ms;
    opacity: .5;
    transform: translate(-50%, -100%) rotate(var(--degree-reverse));
  }
  &:hover .label {
    transition-duration: 600ms;
    transition-delay: 0ms;
    opacity: 1;
  }
  &[name=a] {
    border-top-color: var(--c2);
  }
  &[name=b] {
    border-top-color: var(--c1);
  }
}

.spool {
  width: 12em;
  height: 12em;
  box-shadow: 0 0 0 .15em inset, 0 0 0 .15em, 0 0 0 4.5em #26292c;
  border: .9em double;
  border-top: .9em solid orange;
}
.color-wheel {
  width: 12em;
  height: 12em;
  border: .9em solid;
  box-shadow: 0 0 .3em -.15em #000, 0 0 .3em -.15em #000 inset;
}

// inner bits
.color-wheel {
  span {
    pointer-events: none;
    user-select: none;
    position: absolute;
    text-align: center;
    top: 50%;
    left: 50%;
    width: 80%;
    transform: translate(-50%, -50%) rotate(var(--degree-reverse));
  }
  &::before {
    content: '';
    position: absolute;
    top: .5em;
    left: 50%;
    transform: translate(-50%, -1em);
    width: .65em;
    height: 2em;
    border-radius: 1em;
    background: currentColor;
    // box-shadow: 0 0 .3em -.15em #000;
  }
}
.spool {
  &[name=a] {
    --handle-color: var(--c2);
  }
  &[name=b] {
    --handle-color: var(--c1);
  }
  > div {
    width: 65%;
    pointer-events: none;
  }
  span {
    pointer-events: none;
    user-select: none;
    position: absolute;
    top: 35%;
    left: 65%;
    transform: rotate(var(--degree-reverse));
  }
  input[type="range"] {
    transform: rotate(-90deg);
    pointer-events: all;
  }
}

// range slider
input[type="range"] {
	appearance: none;
	outline: none;
  padding: 0;
  border: 0;
	width: 100%;
	height: .5em;
  border-radius: 1em;
  cursor: pointer;
  box-shadow: 0 0 .3em -.15em #000;
  // MOZILLA
  &::-moz-range-track {
    appearance: none;
    background: #fff;
    outline: none;
  }
  &::-moz-focus-outer {
    border: 0;
  }
  &::-moz-range-thumb {
    appearance: none;
    width: 2em;
    height: 2em;
    border: none;
    box-shadow: 0 0 .3em -.15em #000, 0 0 0 .5em var(--handle-color);
    border-radius: 1em;
    background: #fff;
    transform: scale(.7);
    transition: .3s ease-out;
  }
  &::-moz-range-thumb:focus,
  &::-moz-range-thumb:active {
    appearance: none;
    transform: scale(1.1);
  }
  // BLINK/WEBKIT
  &::-webkit-slider-thumb {
    appearance: none;
    width: 2em;
    height: 2em;
    border: none;
    box-shadow: 0 0 .3em -.15em #000, 0 0 0 .5em var(--handle-color);
    border-radius: 1em;
    background: #fff;
    transform: scale(.8);
    transition: .3s ease-out;
  }
  &::-webkit-slider-thumb:focus,
  &::-webkit-slider-thumb:active {
    appearance: none;
    transform: scale(1.1);
  }
  &[disabled] {
    opacity: .3;
    cursor: not-allowed;
    &::-moz-range-thumb {
      box-shadow: none;
    }
    &::-webkit-slider-thumb {
      box-shadow: none;
    }
  }
}

.params input[type=range] {
  background:  var(--label-text);
  &::-moz-range-track {
    background: var(--label-text);
  }
  &::-moz-range-thumb {
    background: var(--label-text);
    box-shadow: 0 0 0 .25em var(--handle-color);
  }
  &::-webkit-slider-thumb {
    background: var(--label-text);
    box-shadow: 0 0 0 .25em var(--handle-color);
  }
}

.palette input[type=range] {
  background:  var(--contrast);
  &::-moz-range-track {
    background: var(--contrast);
  }
  &::-moz-range-thumb {
    background: var(--contrast);
    box-shadow: 0 0 0 .25em var(--handle-color);
  }
  &::-webkit-slider-thumb {
    background: var(--contrast);
    box-shadow: 0 0 0 .25em var(--handle-color);
  }
}

@keyframes show-off {
  to {
    transform: rotate3D(.5, .5, .3, -360deg);
  }
}
.skin {
  .modal__wrapper {
    background: none;
    font-size: 12px;
    animation: show-off 900ms cubic-bezier(1, 0, 0.2, 1.25);
  }
  .modal__header {
    background: var(--base);
    color: var(--base-text);
    border-radius: 1em 1em 0 0;
  }
  .modal__body {
    border-left: 3em solid var(--base);
    border-right: 3em solid var(--base);
    padding: 0;
    display: flex;
  }
  .modal__footer {
    background: var(--base);
    border-radius: 0 0 1em 1em;
    padding: 4.5em 2em 2em 2em;
    perspective: 20em;
    &::before,
    &::after {
      content: '';
      position: absolute;
      width: 1em;
      height: 10em;
      bottom: 4em;
      background: var(--base-shade);
    }
    &::before {
      right: 100%;
      border-radius: 1em 0 0 1em;
    }
    &::after {
      left: 100%;
      border-radius: 0 1em 1em 0;
    }
  }
  .model {
    font-size: 2em;
    font-weight: 900;
    letter-spacing: -1px;
    text-transform: uppercase;
    padding-left: 1ch;
    display: flex;
    align-items: center;
    small {
      font-size: 45%;
      letter-spacing: 0;
      line-height: 1;
      margin-left: 1ch;
    }
  }

  .params {
    --handle-color: var(--label);
    border: 1px inset #555;
    margin: .3em;
    padding: .2em;
    border-radius: .3em;
    text-transform: uppercase;
    font-size: .85em;
    > * {
      flex: 1;
      display: flex;
      background: var(--label);
      color: var(--label-text);
      border-radius: .2em;
      box-shadow: -1px -1px 0 0 rgba(0,0,0,.3) inset;
    }
    // form {
    //   position: absolute;
    //   top: 0;
    //   right: 0;
    // }
  }
  .front {
    .params > * {
      margin-left: .3em;
      transform: rotate(.35deg);
    }
    .case {
      flex-direction: row;
    }
  }
  .back {
    .params > * {
      margin-right: .3em;
      transform: rotate(-.25deg);
    }
    .case {
      flex-direction: column;
    }
    .now-playing {
      position: absolute;
      top: .5em;
      left: .5em;
    }
    .input-text {
      padding-left: 4em;
      padding-right: 2.5em;
    }
  }
}

.palette {
  --handle-color: var(--color); 
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: var(--color);
  color: var(--contrast);
  transform: translateY(100%);
  transition: transform 300ms ease-out, background 300ms linear;
  display: flex;
  align-items: stretch;
  // text-shadow: 1px 1px .1em rgba(#000, .35);
  &.open {
    transform: translateY(0%);
  }
  &__x {
    cursor: pointer;
    font-size: 4em;
    line-height: 0;
    opacity: .4;
    position: absolute;
    top: 0.35em;
    right: 0.35em;
  }
  &__key {
    position: absolute;
    top: 2em;
    left: 2em;
    text-transform: uppercase;
    font-weight: bold;
  }
}

// Modal
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  &__mask {
    background: rgba(#fc1770, .5);
    position: absolute;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
  }
  &__wrapper {
    font-size: 1vmax;
    position: relative;
    width: 52em;
    height: 36em;
    background: rgba(0,0,0,.3) url(https://s-media-cache-ak0.pinimg.com/originals/60/da/b5/60dab5db582f2a48dd55955dc4297080.jpg) no-repeat 50% 50%;
    background-size: cover;
    color: #fff;
    display: flex;
    border-radius: 1em;
    box-shadow: 0 2em 5em -1em #000;
  }
  &__container {
    flex: 1;
    display: flex;
    flex-direction: column;
  }
  &__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1em 1.5em;
    * {
      margin: 0;
    }
  }
  &__body {
    flex: 1;
    overflow: auto;
  }
  &__footer {
    position: relative;
    > * {
      flex-direction: row-reverse;
      display: flex;
      justify-content: space-between;
    }
  }
  &__x {
    cursor: pointer;
    font-size: 4em;
    line-height: 0;
    opacity: .4;
    position: absolute;
    top: 0.35em;
    right: 0.35em;
  }
}

.front .modal__body {
  overflow: hidden;
  .controls {
    background: rgba(0,0,0,.5);
    &::before,
    &::after {
      content: '';
      position: absolute;
      top: 0;
      bottom: 0;
      z-index: 0;
    }
    &::before {
      background: rgba(0,0,0,.2);
      width: 11em;
      left: 50%;
      transform: translateX(-50%)
    }
    &::after {
      left: 0;
      right: 0;
      border-left: 5.5em solid rgba(0,0,0,.2);
      border-right: 5.5em solid rgba(0,0,0,.2);
    }
  }
}

.button-leave-active,
.modal-enter-active {
  transition: transform 500ms cubic-bezier(1, 0, 0.2, 1.5);
}
.modal-leave-active,
.button-enter-active {
  transition: transform 500ms cubic-bezier(1, -0.5, 0.2, 1);
}

.mask-enter-active, .mask-leave-active {
  transition: opacity 300ms linear;
}
.mask-enter, .mask-leave-to {
  opacity: 0;
}

.button-enter, .button-leave-to,
.modal-enter, .modal-leave-to {
  transform: scale(0);
}


// flipper
.flip-container {
  perspective: 85vmax;
  &.flip-me .flipper {
    transform: rotateY(-180deg);
  }
}
.flipper {
  transition: 1000ms cubic-bezier(1, -0.2, 0.2, 1.2);
  transform-style: preserve-3d;
  position: relative;
  .front,
  .back {
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
  }
  .front {
    transform: rotateY(0deg)
  }
  .back {
    transform: rotateY(-180deg);
  }
}

.side {
  margin-top: -.7em;
  width: 1.3em;
  height: 1.3em;
  border: 1px inset #555;
  border-radius: .1em;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 4em;
  font-weight: 900;
  color: var(--base-dark);
  opacity: .5;
}
.trap {
  position: absolute;
  z-index: -1;
  bottom: 0;
  border: .7em solid var(--base);
  border-color: var(--base-light) var(--base-dark) var(--base-dark) var(--base-light);
  border-bottom-width: 0;
  width: 65%;
  height: 8em;
  left: 50%;
  transform-style: preserve-3d;
  transform: translateX(-50%) rotateX(30deg);
  background-image: 
    repeating-linear-gradient(135deg, 
      rgba(54, 54, 57, 0) 0,
      #222 7px,
      #222 6px,
      #222 7px
    ),
    repeating-linear-gradient(45deg, 
      rgba(54, 54, 57, 0) 0, 
      #666 7px,
      #666 6px,
      #666 7px
    );
  background-color: var(--base);
  background-size: 100%;
}

.track-list {
  position: absolute;
  top: 0;
  right: 0;
  color: #fff;
  cursor: pointer;
  border-radius: 0 0 0 .4em;
  overflow: hidden;
  h4 {
    margin: 0;
    padding: 1ch;
    font-weight: 100;
    background: rgba(0, 0, 0, 0.4);
  }
  ul {
    margin: 0;
    padding: 0;
    box-shadow: 0 -1px 0 0 rgba(0,0,0,.1);
    background: rgba(0,0,0, .3);
    box-shadow: 0 0 1.5em -.5em rgba(0, 0, 0, 0.2);
  }
  li {
    list-style: none;
    margin: 0;
    padding: 1ch;
    display: flex;
    align-items: baseline;
    white-space: nowrap;
    &:hover {
      box-shadow: 0 0 2em 0 rgba(0,0,0,.2) inset;
    }
    .fa {
      margin-right: 1ch;
    }
  }
}

[drawer] + * {
  max-height: 0;
  max-width: 0;
  overflow: hidden;
  transition: 270ms ease-out;
  opacity: 0;
}

[drawer].open + * {
  opacity: 1;
  max-height: 500px;
  max-width: 500px;
}

              
            
!

JS

              
                Vue.component('modal', {
  template: `
    <transition name="modal">
      <div class="modal flip-container" :class="{'flip-me': flipMe}">
        <div class="modal__mask" v-if="showMask"></div>
        <div class="modal__wrapper flipper">
          <div class="modal__container front">
            <div class="modal__header">
              <slot name="front-header"></slot>
              <div class="modal__x" v-if="showX" @click="$emit('toggle')">&times;</div>
            </div>
            <div class="modal__body">
              <slot name="front-body"></slot>
            </div>
            <div class="modal__footer">
              <slot name="front-footer"></slot>
            </div>
          </div>
          <div class="modal__container back">
            <div class="modal__header">
              <slot name="back-header"></slot>
              <div class="modal__x" v-if="showX" @click="$emit('toggle')">&times;</div>
            </div>
            <div class="modal__body">
              <slot name="back-body"></slot>
            </div>
            <div class="modal__footer">
              <slot name="back-footer"></slot>
            </div>
          </div>
        </div>
      </div>
    </transition>
  `,
  props: [
    'showX',
    'showMask',
    'flipMe'
  ]
})

Vue.component('rad-slider', {
  template: `
    <div 
        class="rad-slider"
        :name="name"
        :class="{moving: moving, dirty: dirty}" 
        @mousedown.self="start" 
        @mousemove="move" 
        @mouseup="stop"
        @touchstart.self="start" 
        @touchmove="move" 
        @touchend="stop"
        >
      <slot></slot>
      <div class="label">{{degree}}&deg;</div>
    </div>
  `,
  props: {
    name: {
      type: String,
      required: true
    },
    degree: {
      type: Number,
      default: 0
    },
    lockWith: {
      type: String
    },
    eventName: {
      type: String,
      required: true
    }
  },
  data: function(){
    return {
      moving: false,
      dirty: false,
      delta: 0,
      buddy: null,
      previousDegree: this.degree
    }
  },
  mounted: function(){
    this.processDegree(this.degree, this.degree)
    if(this.lockWith){
      this.buddy = document.querySelector('.rad-slider[name="'+this.lockWith+'"]')
    }
  },
  updated: function(){
    this.processDegree(this.degree, this.degree)
  },
  methods: {
    getPointerDeg: function(e){
      let x = e.touches && e.touches.length ? e.touches[0].clientX : e.clientX
      let y = e.touches && e.touches.length ? e.touches[0].clientY : e.clientY
      return Math.floor(Math.atan2(x - (this.metrics.left + this.metrics.width/2), y - (this.metrics.top + this.metrics.height/2))*(-180/Math.PI)) + 180
    },
    processDegree: function(value, previousValue){
      this.degree = (value >= 360 ? value - 360 : (value < 0 ? value + 360 : value))
      this.$el.style.setProperty('--degree', this.degree + 'deg')
      this.delta = this.degree - previousValue
      this.$emit(this.eventName, this.name, this.degree, this.delta, this.lockWith, this.buddy)
    },
    start: function(e){
      this.moving = true
      this.dirty = true
      this.metrics = e.target.getBoundingClientRect()
      this.pointer = this.getPointerDeg(e) - this.degree
      this.previousDegree = this.degree
    },
    move: function(e){
      if(this.moving){
        let step = 1
        let input = this.getPointerDeg(e)
        if(e.shiftKey && e.metaKey){
          step = 45
        } else if(e.shiftKey){
          step = 15
        } else if(e.metaKey){
          step = 30
        }
        this.degree = Math.ceil((this.getPointerDeg(e) - this.pointer) / step) * step
        if(this.previousDegree !== this.degree){
          this.processDegree(this.degree, this.previousDegree)
        }
        this.previousDegree = this.degree
      }
    },
    stop: function(e){
      this.moving = false
    }
  }
})

let _units = [
  'vmax',
  'vmin',
  'vw',
  'vh',
  '%',
  'em',
  'px'
]

let _translator = {
  c1: 'color B',
  c2: 'color A',
  bg: 'background'
}

let _tracks = [
  {
    name: 'Basket Case',
    a: 305,
    b: 220,
    x: 75,
    y: 75,
    l: 5,
    lockstep: false,
    lineY: false,
    lineX: false,
    isolate: false,
    c1: '#9959cf',
    c2: '#40ccde',
    bg: '#e783b1',
    size: '100% 100%',
    line_y: 'var(--c2) var(--y)',
    line_x: 'var(--c1) var(--x)',
    unit_x: 'vmax',
    unit_y: 'vmax',
    unit_l: 'px'
  },
  {
    name: 'Because I Said So',
    a: 0,
    b: 90,
    x: 2,
    y: 2,
    l: 2,
    lockstep: true,
    lineY: true,
    lineX: true,
    isolate: true,
    c1: '#dbf7ff',
    c2: '#dbf7ff',
    bg: '#ffffff',
    size: 'var(--x) var(--y)',
    line_y: 'var(--c2) var(--y)',
    line_x: 'var(--c1) var(--x)',
    unit_x: 'em',
    unit_y: 'em',
    unit_l: 'px'
  },
  {
    name: 'College Rules',
    a: 0,
    b: 270,
    x: 85,
    y: 2,
    l: 3,
    lockstep: true,
    lineY: true,
    lineX: true,
    isolate: false,
    c1: '#ffebf3',
    c2: '#dbf7ff',
    bg: '#ffffff',
    size: '100% 100%',
    line_y: 'rgba(0,0,0, 0) calc(var(--y) - var(--l))',
    line_x: 'rgba(0,0,0, 0) calc(var(--x) - var(--l))',
    unit_x: 'vw',
    unit_y: 'em',
    unit_l: 'px'
  }
]

let vm = new Vue({
  el: 'main',
  data: {
    showModal: true,
    showMask: false,
    showX: true,
    flipMe: false,
    isPaletteOpen: false,
    currentKey: null,
    currentColor: '',
    color: {
      h: 0,
      s: 0,
      l: 0
    },
    tracks: _tracks,
    units: _units,
    translator:_translator,
    track: {},
    params: {}
  },
  mounted: function(){
    this.playTrack(this.tracks[0].name)
  },
  methods: {
    setSize: function(track){
      track.size = (track.isolate ? `${track.x}${track.unit_x} ${track.y}${track.unit_y}`:'100% 100%')
      this.foo('size', track.size, '')
    },
    setLineY: function(track){
      let bg = this.hex2rgb(track.bg)
      if(track.lineY) {
        track.line_y = `rgba(${bg.r},${bg.g},${bg.b}, 0) calc(${track.y}${track.unit_y} - ${track.l}${track.unit_l})` //, rgba(${bg.r},${bg.g},${bg.b}, 0) ${track.y}${track.unit_y}`
      } else {
        track.line_y = `${track.c2} ${track.y}${track.unit_y}`
      }
      this.foo('line-y', track.line_y, '')
    },
    setLineX: function(track){
      let bg = this.hex2rgb(track.bg)
      if(track.lineX) {
        track.line_x = `rgba(${bg.r},${bg.g},${bg.b}, 0) calc(${track.x}${track.unit_x} - ${track.l}${track.unit_l})` //, rgba(${bg.r},${bg.g},${bg.b}, 0) ${track.x}${track.unit_x}`
      } else {
        track.line_x = `${track.c1} ${track.x}${track.unit_x}`
      }
      this.foo('line-x', track.line_x, '')
    },
    setY: function(track){
      this.foo('y', track.y, track.unit_y)
      this.setSize(track)
      this.setLineY(track)
    },
    setX: function(track){
      this.foo('x', track.x, track.unit_x)
      this.setSize(track)
      this.setLineX(track)
    },
    setLine: function(track){
      this.foo('l', track.l, track.unit_l)
      this.setLineY(track)
      this.setLineX(track)
    },
    setXYUnit: function(track, key){
      this.foo('unit-'+key, track.unit+"_"+key, '')
      this.setX(track)
      this.setY(track)
    },
    setLUnit: function(track, key){
      this.foo('unit-'+key, track.unit+"_"+key, '')
      this.setLine(track)
    },
    setParams: function(track){
      let bg = this.hex2rgb(track.bg)
      return {
        title: '',
        editors: '010',
        css: `body {
  min-height: 100vh;
  background-image: 
    repeating-linear-gradient(${track.a}deg, 
      rgba(${bg.r},${bg.g},${bg.b}, 0) 0,
      ${track.line_y},
      ${track.c2} calc(${track.y}${track.unit_y} - ${track.l}${track.unit_l}),
      ${track.c2} ${track.y}${track.unit_y}
    ),
    repeating-linear-gradient(${track.b}deg, 
      rgba(${bg.r},${bg.g},${bg.b}, 0) 0,
      ${track.line_x},
      ${track.c1} calc(${track.x}${track.unit_x} - ${track.l}${track.unit_l}),
      ${track.c1} ${track.x}${track.unit_x}
    );

  background-color: ${track.bg};
  background-size: ${track.size};
}`
      }
    },
    togglePlaylist: function(event){
      event.target.classList.toggle('open')
    },
    playTrack: function(name){
      this.hidePalette()
      let track = this.tracks.find(function(item){
        return item.name === name
      })
      this.track = JSON.parse(JSON.stringify(track)) // cheap clone
      this.foo('a', this.track.a, 'deg')
      this.foo('b', this.track.b, 'deg')
      this.foo('c1', this.track.c1, '')
      this.foo('c2', this.track.c2, '')
      this.foo('bg', this.track.bg, '')
      this.foo('x', this.track.x, this.track.unit_x)
      this.foo('y', this.track.y, this.track.unit_y)
      this.foo('l', this.track.l, this.track.unit_l)
      this.setSize(this.track)
      this.setLineY(this.track)
      this.setLineX(this.track)
    },
    foo: function(key, value, unit){
      this.track[key] = value
      document.documentElement.style.setProperty('--' + key, value + unit)
      if(key === 'bg') {
        let rgbObject = this.hex2rgb(value)
        let rgb = `${rgbObject.r}, ${rgbObject.g}, ${rgbObject.b}`
        document.documentElement.style.setProperty('--' + key + '-rgb', rgb)
      }
      if(key == 'c1' || key === 'c2' || key === 'bg') {
        let contrast = this.brightness(value) < 165 ? '#fff' : '#212126';
        document.documentElement.style.setProperty('--contrast', contrast)
        document.documentElement.style.setProperty('--' + key + '-contrast', contrast)
      }
    },
    toggleModal: function(){
      this.hidePalette()
      this.showModal = !this.showModal
      if(!this.showModal && this.flipMe){
        this.flipModal()
      }
    },
    flipModal: function(){
      this.flipMe = !this.flipMe
      if(this.flipMe) {
        this.params = this.setParams(this.track)
      }
    },
    rotate: function(name, degree, delta, lockWith, buddy){
      this.foo(name, degree, 'deg')
      if(this.track.lockstep && buddy) {
        this.track[lockWith] += delta
        this.track[lockWith] = (this.track[lockWith] >= 360 ? this.track[lockWith] - 360 : (this.track[lockWith] < 0 ? this.track[lockWith] + 360 : this.track[lockWith]))
        buddy.style.setProperty('--degree', this.track[lockWith] + 'deg')
        this.foo(lockWith, this.track[lockWith], 'deg')
      }
    },
    showPalette: function(key){
      this.isPaletteOpen = true
      this.currentKey = key
      let hex = this.track[this.currentKey]
      this.color = this.hex2hsl(hex)
      this.updateColor(this.color)
    },
    hidePalette: function(){
      this.isPaletteOpen = false
      this.currentKey = null
    },
    togglePalette: function(key){
      if(key !== this.currentKey || !this.isPaletteOpen) {
        this.showPalette(key)
      } else {
        this.hidePalette()
      }
    },
    updateColor: function(hsl){
      this.currentColor = this.hsl2hex(parseInt(hsl.h), parseInt(hsl.s), parseInt(hsl.l))
      this.foo(this.currentKey, this.currentColor, '')
      this.setLineY(this.track)
      this.setLineX(this.track)
    },
    updateHue: function(name, degree){
      this.color.h = degree
      this.updateColor(this.color)
    },
    hex2rgb: function(hex){
      if(hex) {
        hex = hex.replace('#', '')
        var r = (parseInt(hex.substring(0, 2), 16)|0)
        var g = (parseInt(hex.substring(2, 4), 16)|0)
        var b = (parseInt(hex.substring(4, 6), 16)|0)
        return {r:r, g:g, b:b}
      }
    },
    rgb2hsl: function(r, g, b) {
      r /= 255, g /= 255, b /= 255
      let max = Math.max(r, g, b),
          min = Math.min(r, g, b)
      let h, 
          s, 
          l = (max + min) / 2

      if(max == min) {
          h = s = 0; // achromatic
      } else {
          let d = max - min
          s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
          switch(max) {
              case r: h = (g - b) / d + (g < b ? 6 : 0); break;
              case g: h = (b - r) / d + 2; break;
              case b: h = (r - g) / d + 4; break;
          }
      }
      h = Math.floor(h*60+0.5)|0
      s = Math.floor(s*100+0.5)|0
      l = Math.floor(l*100+0.5)|0
      return {h:h, s:s, l:l};
    },
    hex2hsl: function(hex) {
      let rgb = this.hex2rgb(hex)
      let hsl = this.rgb2hsl(rgb.r, rgb.g, rgb.b)
      return hsl
    },
    hue2rgb: function(p, q, t){
      if(t < 0) t += 1
      if(t > 1) t -= 1
      if(t < 1/6) return p + (q - p) * 6 * t
      if(t < 1/2) return q
      if(t < 2/3) return p + (q - p) * (2/3 - t) * 6
      return p
    },    
    hsl2rgb: function(h, s, l){
      let r, g, b, q, p
      h /= 360
      s /= 100
      l /= 100
      if(s === 0) {
          r = g = b = l // achromatic
      } else {
        q = l < 0.5 ? l * (1 + s) : l + s - l * s
        p = 2 * l - q
        r = this.hue2rgb(p, q, h + 1/3)
        g = this.hue2rgb(p, q, h)
        b = this.hue2rgb(p, q, h - 1/3)
      }
      r = Math.min(Math.floor(r*256), 255)
      g = Math.min(Math.floor(g*256), 255)
      b = Math.min(Math.floor(b*256), 255)

      return {r:r, g:g, b:b}
    },
    rgb2hex: function(r, g, b){
      r = ('0' + parseInt(r).toString(16)).substr(-2)
      g = ('0' + parseInt(g).toString(16)).substr(-2)
      b = ('0' + parseInt(b).toString(16)).substr(-2)
      return '#'+r+''+g+''+b
    },
    hsl2hex: function(h, s, l){
     let rgb = this.hsl2rgb(h, s, l)
     return this.rgb2hex(rgb.r, rgb.g, rgb.b)
    },
    brightness: function(hex) {
      let r = parseInt(hex.slice(1, 3), 16)
      let g = parseInt(hex.slice(3, 5), 16)
      let b = parseInt(hex.slice(5, 7), 16)
      return (r * 299 + g * 587 + b * 114) / 1000
    }
  }
})

              
            
!
999px

Console