JSFiddle - React, Tailwind, and code Playground

by tlgreg

HTML

<div id="exercise">
  <!-- 1) Start the Effect with the Button. The Effect should alternate the "highlight" or "shrink" class on each new setInterval tick (attach respective class to the div with id "effect" below) -->
  <div>
    <button @click="toggleEffect">Toggle Effect</button>
    <div id="effect" :class="{highlight: highlighted, shrink: shrinked}"></div>
  </div>
  <!-- 2) Create a couple of CSS classes and attach them via the array syntax -->
  <div :class="[foo,bar,baz]">I got no class :(</div>
  <!-- 3) Let the user enter a class (create some example classes) and attach it -->
  <div>
    <input type="text" v-model="dynClass">
    <div style="min-height: 1rem" :class="dynClass"></div>
  </div>
  <!-- 4) Let the user enter a class and enter true/ false for another class (create some example classes) and attach the classes -->
  <div>
    <input type="text" v-model="dynClass">
    <input type="checkbox" v-model="isRounded">
    <div style="min-height: 1rem" :class="[dynClass, {rounded: isRounded}]"></div>
  </div>
  <!-- 5) Repeat 3) but now with values for styles (instead of class names). Attach the respective styles.  -->
  <div>
    <input type="text" v-model="dynBg">
    <div style="width: 5rem; height: 1rem" :style="{backgroundColor: dynBg}"></div>
  </div>
  <!-- 6) Create a simple progress bar with setInterval and style bindings. Start it by hitting the below button. -->
  <div>
    <button @click="startProgress">Start Progress</button>
    <div class="progress-bar" :class="{loading: progressLoading, load: progressLoad}"></div>
  </div>
</div>

SCSS

#effect {
  width: 100px;
  height: 100px;
  border: 1px solid black;
  transition: all .25s ease;
}

#effect.highlight, .highlight {
  background-color: red;
  width: 200px;
}

#effect.shrink, .shrink {
  background-color: gray;
  width: 50px;
}

.foo { color: papayawhip }
.bar {
  --pad: calc(1em/3);
  text-shadow: 0 0 var(\--pad) darkred;
  padding: var(\--pad);
}
.baz {
  font-weight: bold;
  font-family: sans-serif;
}

.rounded {
  border-radius: 1rem;
}

.progress-bar {
  --duration: 2s;
  --outro: .5s;
  position: relative;
  width: 0rem;
  height: 1rem;
  background-color: skyblue;
  margin: .2rem 0;
  border-radius: .2rem;
  border-width: 1px;
  border-style: none;
  border-color: slateblue;
  transition: width var(\--duration) linear .01s,
              background-color var(\--outro) ease calc(var(\--duration) + var(\--outro));
  &::after {
    content: "";
    display: block;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-image: repeating-linear-gradient(-45deg, skyblue, skyblue 1rem, slateblue 1rem, slateblue 2rem);
    transition: opacity var(\--outro) ease calc(var(\--duration) + var(\--outro));
  }
  &.loading {
    cursor: progress;
  }
  &.load {
    background-color: chartreuse;
    width: 30rem;
    border-style: solid;
    &::after {
      opacity: 0;
    }
  }
}

Babel + JSX

const ONE_SECOND = 1000

new Vue({
  el: '#exercise',
  timer: null,
  data: {
		highlighted: true,
    shrinked: false,
    foo: 'foo',
    bar: 'bar',
    baz: 'baz',
    dynClass: '',
    isRounded: false,
    dynBg: 'transparent',
    progressLoading: false,
    progressLoad: false,
  },
  methods: {
    toggleEffect() {
    	if (!this.timer) {
      	this.timer = setInterval(() => {
          this.highlighted = !this.highlighted
          this.shrinked = !this.shrinked
        }, ONE_SECOND)
      } else {
      	clearInterval(this.timer)
        this.timer = null
      }
    },
    startProgress() {
    	if (!this.progressLoad) {
      	this.progressLoading = true
        this.progressLoad = true
        setTimeout(() => { this.progressLoading = false }, 3010)
      }
    },
  },
});