Membuat Toucan Palette Generator

CSS Membuat Toucan Palette Generator
Membuat Toucan Palette Generator
Membuat Toucan Palette Generator

Candil.eu.org.- Hallo Programmer kembali lagi di blog saya yang sangat sederhana ini, hari ini kita akan belajar web design mebuat tampilan Toucan Palette Generator, sebuah tutorial pemrograman dasar hanya menggunakan CSS dan HTML yang bisa dijadikan refferensi pembelajaran untuk anda para pengunjung setia blog dan juga youtube channel Candil Code, Silahkan di simak dan perhatikan baik-baik code HTML, CSS dan Javascript yang sudah saya sediakan di bawah ini.

Cara Membuat Toucan Palette Generator

<div id="app">
        <pages-nav></pages-nav>
       
        <div id='main-page' data-title='Color wheel'>
            <div id='demo-wrapper'>
                <h1>Demo </h1>
                <div class='demo-container' :data-hide-text="(hide_text == true) ? 'on' : 'off'">
                    <demo></demo>
                </div>
            </div>
            <div id='color-wheel-container'>
                <color-wheel></color-wheel>

                <div class='switch-mode'>
                    <color-theory-wheel :mode='selectedPickerMode' :static='true' wheel-style="white"
                        @click.native='modeSpinner()'> </color-theory-wheel>
                    <span class='mode-text'>{{(isCustom) ? "Custom" : pickerModeTitle}}</span>
                </div>
                <colors-display></colors-display>
            </div>
        </div>
        <div id='color-theory-page' data-title='Color theory'>

            <div class='inner-container'>
                <div class='title'>
                    <h1>Color Theory</h1>
                </div>
                <div class='mixing'>
                    <h4>Mixing Colors</h4>
                    <div class='mixing-wrapper'>
                        <div class='additive-wrapper'>

                            <div class='additive-mixing color-mixing'>
                                <span class='cyan'></span>
                                <span class='yellow'></span>
                                <span class='magenta'></span>
                            </div>
                            <h5>CMYK</h5>
                            <h6>Additive</h6>
                        </div>
                        <div class='subtractive-wrapper'>
                            <div class='subtractive-mixing color-mixing'>
                                <span class='red'></span>
                                <span class='blue'></span>
                                <span class='green'></span>
                            </div>
                            <h5>RGB</h5>
                            <h6>Subtractive</h6>
                        </div>
                    </div>
                </div>
                <div class='relationships'>
                    <h4>Relationships</h4>
                    <div class='color-relations-wrapper'>
                        <color-theory-wheel @click.native='wheel_mode = "primary"' mode='primary' title='Primary Colors'
                            :static='true'>
                        </color-theory-wheel>
                        <color-theory-wheel @click.native='wheel_mode = "secondary"' mode='secondary'
                            title='Secondary Colors' :static='true'>
                        </color-theory-wheel>
                        <color-theory-wheel @click.native='wheel_mode = "tertiary"' mode='tertiary'
                            title='Tertiary Colors' :static='true'>
                        </color-theory-wheel>
                        <color-theory-wheel @click.native='wheel_mode = "temperature"' mode='temperature'
                            title='Temperature' :static='true'>
                        </color-theory-wheel>
                    </div>
                </div>
                <div class='modes'>
                    <h4>Modes</h4>
                    <div class='color-modes-wrapper'>
                        <color-theory-wheel @click.native='wheel_mode = m.name' :title='m.title' :mode='m.name'
                            :static='true' v-for='m in modes'></color-theory-wheel>
                    </div>
                </div>
                <div class='wheel'>
                    <color-theory-wheel :static="isStatic" :mode='wheel_mode' title='' :static='true'
                        data-labels='on'>
                    </color-theory-wheel>
                </div>
            </div>
        </div>

        <div id='about-page' data-title='About'>
            <about></about>
        </div>
    </div>



    <template id='color-wheel-template'>
        <div id='color-wheel-wrapper'>
            <div id='color-wheel-lightness' :style='lightnessStyle' draggable="false">
                <div id='lightness-picker' draggable="false" :style='lightnessRotation'></div>
            </div>

            <div id='color-wheel' draggable="false" :data-moving='(pointerMoving) ? "yes" : "no"'>
                <pointer v-for='(p, i) in pointerCount' :key="'pointer-'+i"
                    :class='{selected: selectedPointerIndex == i}' :index='i' :color-wheel='color_wheel'></pointer>
            </div>
        </div>
    </template>

    <template id='pointer-template'>
        <div class='pointer' draggable="false" :data-index='index'
            :style="{left: left, top: top, background: background}" :class="{enlarge: enlarge == true}"></div>
    </template>

    <template id='colors-display-template'>
        <div id='colors-display'>
            <div class='colors-containers'>
                <div v-for='(c, i) in colors' class='color-container' :class="{selected:  selectedPointerIndex == i }">

                    <div class='color-bg' :class='{entered: entered == i}' :style="getBackground(i)" draggable="true"
                        @dragstart="dragstart(i, $event)" @dragend="dragend" @dragenter="dragenter(i)">
                        <i class="fas fa-grip-horizontal"></i>
                        <div class='color-grads'>
                            <div v-for='(g, i) in c.palette' :style="{'background-color': g}"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class='color-variations'>
                <span class='arrow' :style='arrowStyle'></span>
                <span class='title' @click='colorModelSpinner'>{{currentModel.name}} <i class="fas fa-sort"></i></span>
                <span class='color-value' id='color-value-to-copy'>{{currentModel.value}}</span>
                <span class='copy-value' data-clipboard-target="#color-value-to-copy" :data-copy-result='copy_message'
                    @mouseout="copy_message = ''" :data-message='(copy_message != "") ? "on" : "off"'>
                    <i class="far fa-copy"></i>
                </span>
            </div>
        </div>
    </template>

    <template id='color-theory-wheel-template'>
        <div class='color-theory-wheel' :data-mode='mode' :data-style='wheelStyle'>
            <div class='colors-wrapper'>
                <div class='colors-sections-wrapper'>
                    <div class='color-section' v-for='(c, i) in count' @mouseover='processHover(i)'
                        :class="{hover: highlightIndices.indexOf(i) > -1}"></div>
                </div>

                <div class='color-borders-wrapper'>
                    <div class='color-border' v-for='b in 6'></div>
                </div>
            </div>
            <div class='color-theory-wheel-title'>{{title}}</div>
        </div>
    </template>


    <template id='pages-nav-template'>
        <nav id='pages-nav'>
            <div class='page-dots'>
                <div class='page-dot' v-for='(d, i) in pages' @mouseover='dotHover(i, $event)' @click='current_page = i'
                    :class="{active: current_page == i}"></div>
            </div>
            <div class='page-title' :style="{top: title_position +'px'}">{{pageTitle}}</div>
        </nav>

    </template>

    <template id='about-template'>
        <div id='about-container'>
            <div class='color-wheel-title'>
                <div id='logo'>
                    <div class='beak'>
                        <span></span>
                    </div>
                    <div class='body'>
                    </div>
                    <div class='eye'></div>
                </div>
                <div class='title'>Toucan Palatte Generator</div>

            </div>
            <div class='info-wrapper'>
                <div class='about-container'>
                    <h4>About</h4>
                    <div class='about-wrapper'>
                        <p v-for='i in info'>{{i}}</p>
                    </div>
                </div>
                <div class='shortcuts-container'>
                    <h4>Shortcuts</h4>
                    <div class='shortcuts-wrapper'>
                        <div class='shortcut-row' v-for='s in shortcuts'>
                            <div class='shortcut-combination'>
                                <template v-for='(k, i) in s.keys'>
                                    <kbd class='shortcut-key'
                                        v-if='k.indexOf("mouse") == -1 && k != "+" && k != "/"'>{{k}}</kbd>
                                    <span class='shortcut-plus' v-if='k == "+"'>+</span>
                                    <span class='shortcut-plus' v-if='k == "/"'>/</span>
                                    <span class='shortcut-mouse' :class='k' v-if='k.indexOf("mouse") > -1'>
                                        <i></i>
                                    </span>
                                </template>
                            </div>
                            <div class='shortcut-desc'>{{s.desc}}</div>
                        </div>
                    </div>
                </div>
                <div class='made-with-container'>
                    <h4>Made with</h4>
                    <div class='made-with-wrapper'>
                        <a :href="l.link" target='_blank' v-for='l in made_with'>{{l.title}}</a>
                    </div>
                </div>
            </div>
        </div>

    </template>


    <template id='demo-template'>
        <div class="d-flex" id="demo" :style='getNewStyle()' :data-darkmode="(settings.darkmode) ? 'on' : 'off'" :data-hide-text="(settings.hide_text) ? 'on' : 'off'">
            <!-- Sidebar -->
            <div class="border-right d-flex flex-column" id="sidebar-wrapper">
                <div class="sidebar-heading">Dashboard </div>
                <div class="list-group list-group-flush d-flex flex-column flex-grow-1">
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="fas fa-home"></i><span>Home</span></a>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="fas fa-globe"></i>
                        <span>Overview</span></a>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="far fa-money-bill-alt"></i>
                        <span>Sales</span>
                        <span class="badge badge-primary badge-pill ml-auto">14</a>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="fas fa-user-circle"></i>
                        <span>Profile</span>
                    </a>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="fas fa-thermometer-quarter"></i>
                        <span>Status</span>
                        <span class="badge badge-success badge-pill ml-auto">2</span>
                    </a>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center">
                        <i class="fas fa-table"></i>
                        <span>Tabels</span></a>
                    <hr class='my3'>
                    <a href="#" class="list-group-item list-group-item-action d-flex align-items-center mt-auto">
                        <i class="fas fa-level-up-alt"></i>
                        <span>Upgrade</span>
                    </a>
                </div>


            </div>
            <!-- /#sidebar-wrapper -->

            <!-- Page Content -->
            <div id="page-content-wrapper">
                <nav class="navbar navbar-expand-lg navbar-light" id='top-navbar'>
                    <form class="form-inline  mr-auto">
                        <input class="form-control mr-sm-2" type="search" placeholder="Search">
                    </form>


                    <ul class="navbar-nav">
                        <li class="nav-item mx-2">
                            <a class="nav-link" href="#">
                                <i class="far fa-bell"></i>
                            </a>
                        </li>
                        <li class="nav-item mx-2">
                            <a class="nav-link" href="#"><i class="fas fa-th-large"></i></a>
                        </li>
                        <li class="nav-item mx-2">
                            <a class="nav-link" href="#"><i class="fas fa-user"></i></a>
                        </li>
                    </ul>

                </nav>

                <div class="header bg-primary pb-6">
                    <div class="container-fluid">
                        <div class="header-body">
                            <!-- Card stats -->
                            <div class="row">
                                <div class="col-xl-4 col-md-6" v-for='card in admin_cards'>
                                    <div class="card card-stats mb-4">
                                        <!-- Card body -->
                                        <div class="card-body">
                                            <div class="row">
                                                <div class="col">
                                                    <h5 class="card-title text-uppercase text-mute mb-0">{{card.title}}
                                                    </h5>
                                                    <div class="h2 font-weight-bold mb-0 mt-1">{{card.number}}</div>
                                                </div>
                                                <div class="col-auto">
                                                    <div class="icon icon-shape bg-gradient-red rounded-circle">
                                                        <i :class="card.icon_class"></i>
                                                    </div>
                                                </div>
                                            </div>
                                            <p class="mt-3 mb-0 text-sm">
                                                <span class="string-success mr-2"><i class="fa fa-arrow-up"></i>
                                                    {{card.percentage}}%</span>
                                                <span class="text-nowrap">Since last month</span>
                                            </p>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <!-- /header -->
                <div class="container-fluid">
                    <div class="row mb-4" id='graphs'>
                        <div class="col-xl-8">
                            <div class="card bg-default shadow-sm border-0">
                                <div class="card-header bg-transparent">
                                    <div class="row align-items-center">
                                        <div class="col">
                                            <h6 class="text-light text-uppercase ls-1 mb-1">Overview</h6>
                                            <h3 class="text-white mb-0">Visitors / Sale</h3>
                                        </div>
                                        <div class="col">
                                            <ul class="nav nav-pills justify-content-end">
                                                <li class="nav-item mr-2 mr-md-0" data-toggle="chart">
                                                    <a href="#" class="btn btn-primary btn-sm py-1 px-3"
                                                        data-toggle="tab" @click.stop.prevent='updateData("traffic")'
                                                        :class="{active: line_chart.source == 'traffic'}">
                                                        <span class="d-none d-md-block">Visitors</span>
                                                        <span class="d-md-none">V</span>
                                                    </a>
                                                </li>
                                                <li class="nav-item" data-toggle="chart">
                                                    <a href="#" class="btn btn-primary btn-sm py-1 px-3 mx-2"
                                                        data-toggle="tab" @click.stop.prevent="updateData('sales')"
                                                        :class="{active: line_chart.source == 'sales'}">
                                                        <span class="d-none d-md-block">Sales</span>
                                                        <span class="d-md-none">S</span>
                                                    </a>
                                                </li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>

                                <div class="card-body">

                                    <!-- Chart -->
                                    <div class="chart">
                                        <div class="chartjs-size-monitor">
                                            <div class="chartjs-size-monitor-expand">
                                                <div class=""></div>
                                            </div>
                                            <div class="chartjs-size-monitor-shrink">
                                                <div class=""></div>
                                            </div>
                                        </div>

                                        <chart :datasets='linechartDataset' :labels="line_chart.labels"
                                            :options="line_chart.options"></chart>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="col-xl-4" id='dashboard-settings'>
                            <div class="card shadow-sm border-0">
                                <div class="card-header bg-transparent">
                                    <div class="row align-items-center">
                                        <div class="col">
                                            <h3 class="mb-0">Settings</h3>
                                        </div>
                                    </div>
                                </div>
                                <div class="card-body">
                                    <div class='settings-row'>
                                        <label class="switch">
                                            <input type="checkbox" v-model='settings.darkmode'>
                                            <span class='switch-toggle'></span>
                                            <span class="text">Darkmode</span>
                                        </label>
                                    </div>
                                    <div class='settings-row'>
                                        <label class="switch">
                                            <input type="checkbox" v-model='settings.default_sidebar_bg'>
                                            <span class='switch-toggle'></span>
                                            <span class="text">Default Sidebar Background</span>
                                        </label>
                                    </div>
                                     <div class='settings-row hide-text'>
                                        <label class="switch">
                                            <input type="checkbox" v-model='settings.hide_text'>
                                            <span class='switch-toggle'></span>
                                            <span class="text">Hide Text</span>
                                        </label>
                                    </div>
                                </div>
                            </div>
                        </div>

                    </div>
                    <!-- /#graphs -->


                    <div class="row" id='stats'>
                        <div class="col-xl-7">
                            <div class="card border-0 shadow">
                                <div class="card-header border-0 bg-white">
                                    <div class="row align-items-center">
                                        <div class="col">
                                            <h3 class="mb-0 text-uppercase">Page visits</h3>
                                        </div>
                                        <div class="col text-right">
                                            <a href="#!" class="btn btn-sm btn-primary shadow-sm">See all</a>
                                        </div>
                                    </div>
                                </div>
                                <div class="table-responsive">
                                    <!-- Projects table -->
                                    <table class="table align-items-center table-flush">
                                        <thead class="thead-light">
                                            <tr>
                                                <th class='border-bottom-0' scope="col">Page name</th>
                                                <th class='border-bottom-0' scope="col">Visitors</th>
                                                <th class='border-bottom-0' scope="col">Unique users</th>
                                                <th class='border-bottom-0' scope="col">Bounce rate</th>
                                            </tr>
                                        </thead>
                                        <tbody>

                                            <tr v-for='row in page_visits'>
                                                <th scope="row"> {{row.name}} </th>
                                                <td> {{row.visitors}} </td>
                                                <td> {{row.unique}} </td>
                                                <td>
                                                    <i class="fas fa-arrow-up text-success mr-3"
                                                        v-if='row.direction == "up"'></i>
                                                    <i class="fas fa-arrow-down text-warning mr-3"
                                                        v-if='row.direction == "down"'></i>
                                                    {{row.rate}}
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                        <div class="col-xl-5 social-ref">
                            <div class="card border-0 shadow">
                                <div class="card-header border-0 bg-white">
                                    <div class="row align-items-center">
                                        <div class="col">
                                            <h3 class="mb-0 text-uppercase">Social traffic</h3>
                                        </div>
                                        <div class="col text-right">
                                            <a href="#!" class="btn btn-sm btn-primary shadow-sm">See all</a>
                                        </div>
                                    </div>
                                </div>
                                <div class="table-responsive">
                                    <!-- Projects table -->
                                    <table class="table align-items-center table-flush">
                                        <thead class="thead-light">
                                            <tr>
                                                <th class='border-bottom-0' scope="col">Referral</th>
                                                <th class='border-bottom-0' scope="col">Visitors</th>
                                                <th class='border-bottom-0' scope="col"></th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <tr v-for='row in social_ref'>
                                                <th scope="row">
                                                    {{row.name}}
                                                </th>
                                                <td>
                                                    {{row.visitors}}
                                                </td>
                                                <td>
                                                    <div class="d-flex align-items-center">

                                                        <div class="progress">
                                                            <div class="progress-bar" role="progressbar"
                                                                :style="{width: row.percentage + '%'}"
                                                                :aria-valuenow="row.percentage" aria-valuemin="0"
                                                                aria-valuemax="100">{{row.percentage}}%
                                                            </div>

                                                        </div>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                    <!-- /#stats -->
                </div>

                <footer class="footer pt-0">
                    <div class="row align-items-center justify-content-lg-between">
                        <div class="col-lg-6">
                            <div class="copyright text-center  text-lg-left  text-muted">
                                © 2021 <a href="#" class="font-weight-bold ml-1" target="_blank">Color wheel</a>
                            </div>
                        </div>
                        <div class="col-lg-6">
                            <ul class="nav nav-footer justify-content-center justify-content-lg-end">
                                <li class="nav-item">
                                    <a href="#" class="nav-link" target="_blank">About Us</a>
                                </li>
                                <li class="nav-item">
                                    <a href="#" class="nav-link" target="_blank">Blog</a>
                                </li>
                                <li class="nav-item">
                                    <a href="#" class="nav-link" target="_blank">MIT License</a>
                                </li>
                            </ul>
                        </div>
                    </div>
                </footer>
            </div>
            <!-- /#page-content-wrapper -->

        </div>
    </template>
   
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Skranji:wght@400;700&display=swap");

:root {
  --bg-color: #ecf0f1;
  --dark-accent: rgba(180, 180, 180, 0.2);
  --darker-accent: lightgray;
  --light-accent: rgba(180, 180, 180, 0.5);
  --light-color: white;
  --dark-color: black;

  --site-main-color: #21749a;
  --scrollbar-foreground: #b3cde0;
  --scrolbar-background: rgba(255, 255, 255, 0);
}

:root {
  --demo-bg-color: white;
  --demo-sidebar-bg: white;
  --demo-sidebar-hover: #{rgba(gray, 0.1)};
  --demo-sidebar-hover-color: black;

  --demo-primary-bg: #5e72e4;
  --demo-primary-color: white;
  --demo-sidebar-text-color: black;

  --demo-btn-border: #465cda;
  --demo-btn-bg: #{lighten(#5e72e4, 5%)};
  --demo-btn-color: black;
  --demo-btn-active-color: #fff;
  --demo-btn-active: #{lighten(#5e72e4, 10%)};
  --demo-btn-active-border: #{darken(#5e72e4, 10%)};

  --demo-header-card: white;
  --demo-header-card-text: #8898aa;

  --demo-text-success: #28a745;

  --demo-scrollbar-foreground: #b3cde0;
  --demo-scrolbar-background: rgba(255, 255, 255, 0);
}

body {
  background-color: var(--bg-color);
}

@mixin scrollbars(
  $size: 4px,
  $foreground-color: var(--scrollbar-foreground),
  $background-color: var(--scrollbar-background)
) {
  // For Google Chrome
  &::-webkit-scrollbar {
    width: $size;
    height: $size;
  }

  &::-webkit-scrollbar-thumb {
    background: $foreground-color;
    border-radius: 0.3rem;
  }

  &::-webkit-scrollbar-track {
    background: $background-color;
  }

  // For Internet Explorer
  & {
    scrollbar-face-color: $foreground-color;
    scrollbar-track-color: $background-color;
    // firefox
    scrollbar-width: thin;
    scrollbar-color: $foreground-color $background-color;
  }
}

@mixin ribbon($size: 0.4em) {
  padding-bottom: 0.6em;
  background-color: #21749a;
  padding: 0.4em 2.3em;
  font-size: 1.3rem;
  color: white;
  width: 70%;
  margin-left: -0.5em;
  margin-bottom: 1.2em;
  margin-top: 0.6em;
  position: relative;
  white-space: nowrap;
}

#app {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  width: 100%;
  overflow: auto;
  box-sizing: border-box;
  font-family: "Open Sans", sans-serif;
  user-select: none;
  overflow: hidden;

  > div {
    height: inherit;
    width: inherit;
    border-bottom: 1px solid;
    box-sizing: border-box;
    overflow: auto;
    position: relative;
    background: url("https://www.toptal.com/designers/subtlepatterns/patterns/frenchstucco.png");
  }

  #pages-nav {
    position: fixed;
    left: 0;
    // border: 1px solid;
    top: 50%;
    z-index: 10;
    transform: translateY(-50%);
    margin-left: 0.4rem;

    .page-dots:hover + .page-title {
      opacity: 1;
    }

    .page-dot {
      width: 0.5rem;
      height: 0.5rem;
      background-color: purple;
      border-radius: 50%;
      margin-bottom: 0.5rem;
      cursor: pointer;
      transition: all 0.3s;
      opacity: 0.8;

      &:hover {
        opacity: 1;
      }

      &.active {
        height: 1rem;
        border-radius: 0.2rem;
      }
    }

    .page-title {
      position: absolute;
      top: 0;
      left: 1rem;
      border-radius: 0.3em;
      padding: 0.2em 1em;
      background-color: purple;
      color: white;
      transform: translateY(-50%);
      font-size: 0.7rem;
      pointer-events: none;
      transition: all 0.3s;
      white-space: nowrap;
      opacity: 0;
    }
  }

  #main-page {
    #color-wheel-container {
      position: absolute;
      top: 50%;
      right: 3%;
      transform: translateY(-50%);
      border-radius: 0.3rem;
      background-color: white;
      //background-color: rgba(180, 180, 180, 0.2);
      padding: 2rem;
      box-shadow: 0 0 1em 0 rgba(0, 0, 0, 0.3);

      .color-theory-wheel {
        margin: 0;
        width: auto;
        left: initial;
        top: initial;

        .colors-wrapper {
          height: 2rem;
          width: 2rem;
          border-radius: 50%;
        }
      }

      .switch-mode {
        padding: 0.2em 0.5em 0.2em 1em;
        border-radius: 0.2em;
        margin: 1em 0;
        display: flex;
        font-size: 1rem;
        justify-content: center;
        align-items: center;
        cursor: pointer;
        position: absolute;
        top: 1%;
        right: 2%;

        &:hover {
          .mode-text {
            opacity: 1;
            bottom: 115%;
          }
        }

        .mode-text {
          font-size: 0.6rem;
          position: absolute;
          bottom: 95%;
          padding: 0.2rem 0.5rem;
          background-color: lightgray;
          border-radius: 0.2rem;
          white-space: nowrap;
          pointer-events: none;
          transition: all 0.3s;
          opacity: 0;

          &::before {
            top: 100%;
            left: 50%;
            height: 0.5rem;
            width: 0.5rem;
            background-color: lightgray;
            transform: translate(-50%, -70%) rotate(45deg);
            content: "";
            position: absolute;
          }
        }
      }
    }

    #colors-display {
      display: flex;
      flex-direction: column;
      margin-top: 2rem;

      .colors-containers {
        display: flex;

        > div {
          flex-grow: 1;
          min-width: 20%;
          height: 2em;
          position: relative;

          .color-bg {
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: flex-end;

            color: rgba(black, 0.5);

            transition: opacity 0.3s;
            box-sizing: border-box;

            &.entered {
              border: 0.1em dashed gray;
              opacity: 0.5;
            }

            &:hover i {
              opacity: 1;
            }

            i {
              opacity: 0;
              transition: all 0.3s;
              position: absolute;
              bottom: 100%;
              left: 50%;
              transform: translate(-50%, -10%);
              cursor: grab;
            }

            .color-grads {
              display: flex;
              height: 30%;
              width: 100%;

              div {
                display: block;
                width: 25%;
                height: 100%;
              }
            }
          }
        }
      }

      .color-variations {
        display: flex;

        font-size: 0.7rem;
        margin-top: 2em;

        background-color: var(--dark-accent);
        border-radius: 0.3em;
        position: relative;
        color: var(--dark-color);

        .arrow {
          position: absolute;
          bottom: 100%;
          left: 50%;
          transform: translateX(-50%);
          height: 0;
          width: 0;
          border-bottom: solid 0.6rem var(--dark-accent);
          border-left: solid 0.6rem transparent;
          border-right: solid 0.6rem transparent;
        }

        .title {
          width: 25%;
          display: block;
          padding: 1em;
          text-transform: uppercase;
          cursor: pointer;
        }

        .color-value {
          padding: 1em 0;
          user-select: all;
        }

        .copy-value {
          display: flex;
          justify-content: center;
          align-items: center;
          font-size: 1.1em;
          margin-left: auto;
          cursor: pointer;
          transition: all 0.3s;
          padding: 1em;
          background-color: var(--dark-accent);
          position: relative;

          &:hover {
            background-color: var(--light-accent);
          }

          &[data-message="on"] {
            &::after,
            &::before {
              opacity: 1;
            }
          }

          &::after {
            content: attr(data-copy-result);
            padding: 0.5em 1em;
            white-space: nowrap;
            position: absolute;
            top: 120%;
            left: 50%;
            transform: translatex(-50%);
            background-color: var(--darker-accent);

            pointer-events: none;
            border-radius: 0.3em;
            font-size: 0.7rem;
            opacity: 0;
          }

          &::before {
            content: "";
            position: absolute;
            top: 115%;
            left: 50%;
            transform: translatex(-50%) rotate(45deg);
            background-color: var(--darker-accent);

            pointer-events: none;
            height: 1em;
            width: 1em;
            opacity: 0;
          }
        }
      }
    }
  }

  #color-theory-page {
    display: flex;
    justify-content: center;
    align-items: center;

    flex-direction: column;

    h1 {
      text-transform: uppercase;
      font-weight: bold;
      margin: 1.6em 0;
      font-family: "Skranji", cursive;
      line-height: 0;
    }

    .inner-container {
      max-width: 1200px;
      display: grid;
      grid-template-columns: 0.7fr 1.3fr;
      grid-template-areas:
        "title title"
        "mixing wheel"
        "relationships wheel "
        "modes wheel";
      grid-row-gap: 1em;

      h4 {
        @include ribbon;
        margin-bottom: 0.6em;
      }

      > div:not(.wheel):not(.title) {
        font-size: 0.4em;

        .color-theory-wheel {
          cursor: pointer;
        }
      }

      .title {
        grid-area: title;
        font-size: 1.5em;
        font-weight: bold;
        text-align: center;
        text-transform: uppercase;
      }

      .mixing {
        grid-area: mixing;
        background-color: white;
        border-radius: 0.3em;
        box-shadow: 0 0 0.6em 0 rgba(180, 180, 180, 0.8);

        .mixing-wrapper {
          justify-content: space-around;

          > div {
            justify-content: center;
            display: flex;
            flex-direction: column;
            align-items: center;
          }

          h5 {
            font-weight: bold;
            margin: 0;
          }

          h6 {
            font-size: 1.6em;
            margin: 0;
          }
        }
      }

      .relationships {
        grid-area: relationships;
        background-color: white;
        border-radius: 0.3em;
        box-shadow: 0 0 0.6em 0 rgba(180, 180, 180, 0.8);
      }

      .modes {
        grid-area: modes;
        background-color: white;
        border-radius: 0.3em;
        box-shadow: 0 0 0.6em 0 rgba(180, 180, 180, 0.8);
      }

      .terms {
        grid-area: terms;
      }

      .meaning {
        grid-area: meaning;
      }

      .wheel {
        grid-area: wheel;
        font-size: 3em;

        display: flex;
        flex-direction: column;
      }

      .mixing-wrapper,
      .color-modes-wrapper,
      .color-relations-wrapper {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
      }

      .color-mixing {
        height: 12em;
        width: 12em;
        position: relative;
        isolation: isolate;

        span {
          border-radius: 50%;
          height: inherit;
          width: inherit;
          display: block;
          position: absolute;
          left: 50%;
          top: 50%;
          width: 50%;
          height: 50%;

          transform: translate(-50%, -50%);
          mix-blend-mode: screen;
        }

        &.subtractive-mixing span {
          &.green {
            background-color: lime;
            transform: translate(-20%, -30%);
          }

          &.red {
            background-color: red;
            transform: translate(-50%, -70%);
          }

          &.blue {
            background-color: blue;
            transform: translate(-80%, -30%);
          }
        }

        &.additive-mixing span {
          mix-blend-mode: multiply;

          &.cyan {
            background-color: cyan;
            transform: translate(-20%, -30%);
          }

          &.magenta {
            background-color: magenta;
            transform: translate(-50%, -70%);
          }

          &.yellow {
            background-color: yellow;
            transform: translate(-80%, -30%);
          }
        }
      }
    }
  }

  #about-page {
    #about-container {
      margin: 0 auto;
      padding: 0 2em;
      max-width: 1350px;
    }

    .color-wheel-title {
      display: flex;
      justify-content: center;
      padding: 2rem;
      margin: 3rem 0;

      .title {
        font-size: 2.6rem;
        font-weight: bold;
        font-family: "Skranji", cursive;
      }
    }

    .info-wrapper {
      $shortcut-color: #888888;

      display: flex;
      justify-content: space-around;

      > div {
        font-size: 0.8em;
        line-height: 2;
        text-align: justify;
        background-color: white;
        box-shadow: 0 0 0.3em 0 rgba(128, 128, 128, 0.45098039215686275);
        border-radius: 0.3em;

        h4 {
          @include ribbon;
        }

        > div {
          padding: 0 3em;
        }
      }

      .shortcuts-container {
        width: 45%;
        font-size: 0.9em;

        .shortcut-row {
          margin-bottom: 1em;
          display: flex;

          .shortcut-combination {
            width: 35%;
            display: flex;
            align-items: center;
            margin-right: 1em;
            justify-content: flex-start;

            .shortcut-key {
              color: $shortcut-color;

              box-shadow: 0.05em 0.05em 0 rgba(0, 0, 0, 0.2);
              border: 1px solid #ccc;
              margin: 0 0.1em;
              padding: 0.1em 0.5em;
              border-radius: 0.25em;
              background: #f3f3f3;
              min-height: 2em;
              min-width: 2em;
              display: flex;
              justify-content: center;
              align-items: center;
            }
          }

          .shortcut-desc {
            font-size: 0.8em;
          }
        }

        .shortcut-plus {
          color: $shortcut-color;
          margin: 0 0.4em;
        }

        .shortcut-mouse {
          height: 0.5rem;
          width: 0.5rem;
          border-radius: 50%;
          background: darken(#d2d2d2, 5%);
          position: relative;

          &::after,
          &:before {
            position: absolute;
            bottom: 105%;
            left: 50%;
            transform: translateX(-50%) rotate(45deg);
            border: 0.1em solid transparent;
            border-left: 0.1em solid $shortcut-color;
            border-top: 0.1em solid $shortcut-color;
            height: 0.35em;
            width: 0.35em;
          }

          &::after {
            top: 105%;
            transform: translateX(-50%) rotate(225deg);
          }

          &.mouse-wheel-up {
            &::before {
              content: "";
            }
          }

          &.mouse-wheel {
            &::after,
            &::before {
              content: "";
            }
          }

          &.mouse-click-left {
            height: 1.5em;
            width: 0.8em;
            border-radius: 0.3em;
            overflow: hidden;

            &::before {
              content: "";
              top: 0;
              transform: unset;
              left: 0;
              border: 0;
              background-color: $shortcut-color;
              border-radius: 0 0 0.2em 0;
            }
          }
        }
      }

      .made-with-container {
        width: 20%;

        a {
          display: block;
          font-size: 1.1em;
          color: rgba(0, 0, 0, 0.7);
          margin-bottom: 1em;
          text-decoration: none;
          padding: 0.3em;
          position: relative;
          transition: all 0.3s;

          &::before {
            position: absolute;
            top: 100%;
            width: 0;
            transition: all 0.5s ease-in-out;
            content: "";
            height: 0.3em;
            background-color: rgba(180, 180, 180, 0.2);
          }

          &:hover {
            color: black;

            &::before {
              width: 100%;
            }
          }
        }
      }

      .about-container {
        width: 25%;
      }
    }

    #logo {
      height: 7em;
      width: 7em;
      position: relative;
      font-size: 0.4em;
      $dark: #000923;
      margin: 0 3em;

      .beak {
        height: 30%;
        width: 60%;
        position: absolute;
        right: 0;
        border-radius: 50% 51% 100% 0% / 0% 100% 0% 0%;
        background-color: #fa9c21;
        background-image: radial-gradient(circle at 80% 80%, #ed3027, #fa9c21);
        overflow: hidden;

        span {
          position: absolute;
          bottom: 0;
          right: 0;
          border-radius: 50%;
          background-color: $dark;
          height: 2em;
          width: 2em;
          transform: translate(50%, 50%);
        }
      }

      .body {
        height: 100%;
        width: 40%;
        position: absolute;
        left: 0;
        top: 0;
        border-radius: 100% 0% 100% 0% / 40% 60% 50% 60%;

        background-image: linear-gradient($dark, lighten($dark, 10%));
      }

      .eye {
        border-radius: 2em 0 0 2em;
        background-color: #525252;
        position: absolute;
        top: 0%;
        right: 60%;
        height: 30%;
        width: 20%;
      }
    }
  }
}

#color-wheel-wrapper {
  height: 15rem;
  width: 15rem;
  position: relative;

  #color-wheel-lightness {
    --selected-color: gray;

    position: absolute;
    bottom: 0;
    height: 100%;
    width: 100%;
    background-color: green;
    z-index: 0;
    border-radius: 50%;
    box-shadow: 0 0 1em 0.1em rgba(180, 180, 180, 0.5);

    #lightness-picker {
      height: 100%;
      width: 100%;

      &::before {
        content: "";
        position: absolute;
        bottom: 100%;
        left: 50%;
        transform: translateX(-50%);
        height: 0;
        width: 0;

        border-top: solid 0.5rem black;
        border-left: solid 0.5rem transparent;
        border-right: solid 0.5rem transparent;
      }
    }
  }

  #color-wheel {
    border-radius: 50%;
    height: calc(98% - 0.5em);
    width: calc(98% - 0.5em);
    position: absolute;
    left: calc(1% + 0.25em);
    top: calc(1% + 0.25em);
    z-index: 1;
    box-sizing: border-box;
    box-shadow: 0 0 0 0.1em white;
    overflow: hidden;

    --level: 50%;
    background-image: linear-gradient(red, transparent var(--level)),
      linear-gradient(240deg, yellow, transparent var(--level)),
      linear-gradient(-60deg, green, transparent var(--level)),
      linear-gradient(0deg, cyan, transparent var(--level)),
      linear-gradient(60deg, blue, transparent var(--level)),
      linear-gradient(120deg, magenta, transparent var(--level)),
      radial-gradient(gray, white 70%);

    &[data-moving="yes"] {
      cursor: none !important;

      .pointer {
        cursor: none !important;

        &.selected {
          height: 8%;
          width: 8%;
        }
      }
    }

    .pointer {
      border-radius: 50%;
      height: 3%;
      width: 3%;
      position: absolute;
      left: 50%;
      top: 50%;
      box-shadow: inset 0 0 5px 5px white;
      cursor: pointer;
      z-index: 0;
      transition: width 0.2s, height 0.2s;
      transform: translate(-50%, -50%);

      &.selected {
        box-shadow: inset 0 0 0 3px white, 0 0 0px 2px rgba(black, 0.5);
        z-index: 1;
        height: 6%;
        width: 6%;
      }

      &.enlarge {
        width: 15%;
        height: 15%;
        box-shadow: inset 0 0 0 1px rgba(white, 0.5),
          0 0 0px 1px rgba(black, 0.1);
      }
    }
  }
}

.color-theory-wheel {
  left: 10%;
  top: 10%;
  margin: 2em;
  width: 10em;

  .color-theory-wheel-title {
    color: gray;
    font-size: 1.3em;
    text-align: center;
  }

  .colors-wrapper {
    height: 10em;
    width: 10em;
    border-radius: 50%;
    background-color: white;
    position: relative;
    border: 1px solid gray;

    &::before {
      background-color: white;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      content: "";
      height: 15%;
      width: 15%;
      border-radius: 50%;
      z-index: 5;
    }
  }

  .color-borders-wrapper {
    width: 98%;
    height: 98%;
    position: relative;
    position: absolute;
    top: 1%;
    left: 1%;
    overflow: hidden;
    border-radius: 50%;
    pointer-events: none;

    .color-border {
      position: absolute;
      height: 110%;
      width: 0.05em;
      background-color: white;
      top: 50%;
      left: 50%;
      z-index: 3;
      transform: rotate(15deg);

      @for $i from 1 through 6 {
        &:nth-child(#{$i}) {
          $rotate: $i * 30deg + 15;
          transform: translate(-50%, -50%) rotate($rotate);
        }
      }
    }
  }

  &[data-mode="mono"] .color-section.hover::after {
    content: "";
    position: absolute;
    top: 0%;
    left: 50%;
    transform: translateX(-50%);
    background-image: repeating-radial-gradient(
      circle at 50% 50%,
      rgba(white, 0.5) 0 35%,
      rgba(white, 0.3) 30% 53%,
      transparent 50% 100%
    );
    height: 200%;
    width: 373%;
    border-radius: 50%;
  }

  &[data-style="white"] {
    .colors-wrapper {
      background-color: white;
      position: relative;
      border: 0;
      box-shadow: 0 0 0.5rem 0 rgba(gray, 0.6);
    }

    .color-section {
      &:not(.hover) {
        background-color: #eee !important;

        &::before {
          content: unset;
        }
      }

      &.hover {
        background-color: lightgray !important;
      }
    }

    .colors-wrapper::before {
      content: unset;
    }
  }

  &[data-mode="temperature"] .color-border:nth-child(5) {
    width: 0.3em;
    z-index: 5;

    &::after {
      content: "";
      height: 100%;
      width: 54%;
      border-right: 0.05em dashed gray;
      position: absolute;
      left: 0;
      top: 0;
      box-sizing: border-box;
    }
  }

  &[data-labels="on"] .color-section.hover::after {
    opacity: 1;
  }

  .colors-sections-wrapper {
    width: 98%;
    height: 98%;
    position: relative;
    position: absolute;
    top: 1%;
    left: 1%;
    overflow: hidden;
    border-radius: 50%;

    .color-section {
      height: 50%;
      width: 27%;
      position: absolute;
      left: 50%;
      bottom: 50%;
      transform-origin: bottom center;
      transition: background-color 0.3s;
      overflow: hidden;
      box-sizing: border-box;
      clip-path: polygon(50% 100%, 0 0, 100% 0);

      &::before {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        content: "";
        transition: all 0.3s;
        background-image: linear-gradient(
          0deg,
          rgba(lightgray, 0.3),
          rgba(90, 90, 90, 0.2)
        );
      }

      &::after {
        position: absolute;
        top: 0;
        padding: 1em;
        border-radius: 0.3em;
        left: 50%;
        transform: translateX(-50%) rotate(90deg);
        height: 50%;
        font-size: 1rem;
        display: flex;
        justify-content: center;
        align-items: center;
        white-space: pre;
        color: white;
        text-shadow: 0 0 0.2em gray;
        text-transform: capitalize;
        opacity: 0;
        transition: all 0.3s;
      }

      $colors: 0 22 33 49 59 81 117 181 245 266 293 314;
      $light: 50 50 50 50 50 50 41 33 42 50 43 48;
      $names: (
        "red",
        "red-orange",
        "orange",
        "yellow-orange",
        "yellow",
        "yellow-green",
        "green",
        "blue-green",
        "blue",
        "blue-violet",
        "violet",
        "red-violet"
      );

      @for $i from 0 through 11 {
        &:nth-child(#{$i + 1}) {
          $rotate: $i * 30deg;
          $hue: nth($colors, $i + 1);
          $lightness: nth($light, $i + 1);

          transform: translateX(-50%) rotate($rotate);
          background-color: hsla($hue, 100%, 50%, 0.2);

          &.hover {
            background-color: hsla($hue, 100%, $lightness, 1);
          }

          &::after {
            content: nth($names, $i + 1);

            @if ($i < 6) {
              transform: translateX(-50%) rotate(270deg);
            }
          }
        }
      }
    }
  }
}

#demo-wrapper {
  height: 90vh;
  width: 75vw;
  left: 5vw;
  top: 5vh;
  position: relative;
  background-color: white;
  border-radius: 0.2em;
  max-width: 1200px;
  display: flex;
  flex-direction: column;
  box-shadow: 0 0 0.9em 0 rgba(180, 180, 180, 0.6);

  .demo-container {
    padding: 2em;
    overflow: auto;
  }

  h1 {
    display: flex;
    align-items: center;
    @include ribbon();
  }
}

#demo {
  height: 100%;
  width: 100%;
  left: 0;
  top: 0;
  position: relative;
  overflow: hidden;
  border: 1px solid lightgray;
  border-radius: 0.3em;
  background-color: var(--demo-bg-color);
  color: var(--demo-text-color);

  &[data-hide-text="on"] {
    text-indent: -2000px;

    .settings-row.hide-text {
      text-indent: initial;
    }
  }

  &[data-darkmode="on"] {
    --demo-bg-color: #282b34;
    --demo-sidebar-bg: #282b34;
    --demo-sidebar-hover: #{rgba(white, 0.1)};
    --demo-sidebar-hover-color: white;
    --demo-text-color: white;
    --demo-sidebar-text-color: white;

    --demo-card-bg: #1e1e1e;

    .container-fluid {
      .card {
        background-color: var(--demo-card-bg);
        box-shadow: 0 0.125rem 0.25rem rgba(lightgray, 0.1);
      }

      .bg-white {
        background-color: var(--demo-card-bg) !important;
      }

      thead th {
        background-color: var(--demo-card-bg) !important;
      }
    }

    .table th,
    .table td,
    .border-right {
      border-color: rgba(white, 0.2) !important;
    }
  }

  .bg-primary {
    background-color: var(--demo-primary-bg) !important;
  }

  .btn-primary,
  .progress-bar {
    background-color: var(--demo-btn-bg);
    color: var(--demo-btn-color);
  }

  .btn-primary {
    border-color: var(--demo-btn-border);

    &:focus {
      box-shadow: unset !important;
    }

    &.active {
      background-color: var(--demo-btn-active);
      border-color: var(--demo-btn-active-border);
      color: var(--demo-btn-active-color);
    }
  }

  #dashboard-settings {
    .settings-row {
      font-size: 0.8rem;

      .switch {
        display: flex;
        align-items: center;
        cursor: pointer;

        .switch-toggle {
          border-radius: 1rem;
          width: 1.5rem;
          height: 0.8rem;
          background-color: #b0bec5;
          transition: all 0.3s;
          position: relative;
          margin-right: 0.4em;

          &::before {
            width: 0.4rem;
            height: 0.4rem;
            position: absolute;
            left: 0.2rem;
            top: 0.2rem;
            content: "";
            border-radius: 50%;
            background-color: rgba(white, 0.5);
            transition: all 0.3s;
          }
        }

        input[type="checkbox"] {
          display: none;

          &:checked + .switch-toggle {
            background-color: #1976d2;

            &::before {
              left: 100%;
              transform: translateX(calc(-100% - 0.2rem));
              background-color: rgba(white, 0.5);
            }
          }
        }
      }
    }
  }

  #sidebar-wrapper {
    margin-left: -15rem;
    transition: margin 0.25s ease-out;
    background-color: white;
    padding: 0em;
    background-color: var(--demo-sidebar-bg);
    color: var(--demo-sidebar-text-color);

    .sidebar-heading {
      padding: 0;
      font-size: 1.2rem;
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 2em 0;
    }

    .list-group {
      width: 14em;
      font-size: 0.9rem;
      padding: 0.5em;

      .list-group-item {
        padding: 0.7em 1em 0.7em 0.2em;
        background-color: transparent;
        color: var(--demo-sidebar-text-color);
      }

      > a {
        border: 0;
        border-radius: 0.3rem;
        transition: background-color 0.3s;
        font-weight: normal;

        &:hover {
          background-color: var(--demo-sidebar-hover);
          color: var(--demo-sidebar-hover-color);
        }

        i {
          width: 2.5em;
          text-align: center;
          font-size: 1rem;
          margin-right: 0.5rem;
        }
      }
    }
  }

  #page-content-wrapper {
    min-width: 100vw;

    overflow: auto;
    @include scrollbars(
      4px,
      var(--demo-scrollbar-foreground),
      var(--demo-scrollbar-background)
    );

    #top-navbar {
      padding: 1.3em 2em;
      background-color: var(--demo-primary-bg) !important;

      input[type="search"] {
        border-radius: 0.6em;
        outline: 0;
        transition: all 0.3s;
        border: 0;
        background-color: rgba(white, 0.6);

        &:focus {
          width: 20em;
          background-color: white;
        }
      }

      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .nav-link {
      color: var(--demo-primary-color);
    }

    .string-success {
      color: var(--demo-text-success);
    }

    .container-fluid {
      padding: 2em 2em 8em 2em;

      .header-body {
        .card {
          border-radius: 0.375rem;
          background-color: var(--demo-header-card);
          border: 0;
          color: var(--demo-header-card-text-1);

          .text-mute {
            color: var(--demo-header-card-text-2);
          }

          .card-title,
          p {
            font-size: 0.8rem;
          }

          .h2 {
            font-size: 1.5rem;
          }

          .icon {
            height: 3rem;
            width: 3rem;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            font-size: 1.2em;
            background-color: rgba(gray, 0.1);
          }
        }
      }

      h6 {
        font-size: 0.7rem;
      }

      h3 {
        font-size: 1rem !important;
      }

      h5 {
        font-size: 0.7rem !important;
      }

      .btn-sm {
        font-size: 0.75rem;
      }

      .text-mute {
        color: #8898aa;
      }

      .bg-default {
        background-color: #172b4d !important;
      }

      #graphs {
        margin-top: -7em;
      }

      #stats {
        font-size: 0.8rem;

        thead th {
          color: #8898aa;
          background-color: #f6f9fc;
          font-weight: normal;
          text-transform: uppercase;
          font-size: 0.8em;
        }

        .progress {
          width: 100%;
          font-size: 0.7em;
        }

        .social-ref td:last-child {
          width: 50%;
        }
      }
    }

    footer {
      padding: 2em;
      font-size: 0.8rem;

      a {
        color: inherit;
      }
    }
  }
}

@media (min-width: 768px) {
  #demo {
    #sidebar-wrapper {
      margin-left: 0;
    }

    #page-content-wrapper {
      min-width: 0;
      width: 100%;
    }
  }
}

@media (max-width: 700px) {
  html {
    font-size: 12px;
  }
  #app{
    h1, .color-wheel-title .title{
      font-size: 2rem !important;
    }
  #color-wheel-wrapper {
    height: 12rem;
    width: 12rem;
  }

   #color-theory-page {
     h5{
       font-size: 1.4em;
     }
     
     .color-theory-wheel-title{
       font-size: 1.7em;
     }
    .inner-container {
      max-width: 100%;

      grid-template-columns: 1fr 1fr 1fr;
      grid-template-areas:
        "title title title"
        "wheel wheel wheel"
        "mixing relationships modes";
      grid-row-gap: 0.5em;

      .color-theory-wheel {
        margin: 1em auto;
      }
      
      .modes, .relationships{
        .color-theory-wheel{
          margin: 1em 4em;
        }
      }
    }
  }
  
  #about-page{
    .info-wrapper{
      display: block;
      > .about-container, .shortcuts-container, .made-with-container{
        width: 100%;
      }
      > div > div{
        padding: 1em 2em;
      }
    }
  }
}
}
/**
 * Uitlity method to map state from observable to components.
 * The methods will convert snake_case keys to camelCase
 * @param Array state  arary of state keys to be mapped
 */
const mapState = function (state) {
    let returnState = {};
    if (!Array.isArray(state))
        return returnState;

    state.forEach(s => {
        let key = s.split("_").map((k, i) => {
            let replaceKey = k.replace(/^./, (m) => m.toUpperCase());
            return (i > 0) ? replaceKey : k;
        }).join("");

        returnState[key] = function () {
            return store[s];
        }
    });

    return returnState;
}
/**
 * Utility methods to map mutations to compoenent
 * @param Array methods array of methods keys to be mapped
 */
const mapMethods = function (methods) {
    let returnMethods = {};
    if (!Array.isArray(methods))
        return returnMethods;

    methods.forEach(m => {
        returnMethods[m] = mutations[m];
    });

    return returnMethods;
}

/**
 * Observable is used in place to Vuex as a simple state management alterantive
 */
let store = Vue.observable({
    picker_modes: [{
        name: "comple",
        title: "Complementary"
    }, {
        name: "split",
        title: "Split Complementary"
    }, {
        name: "mono",
        title: "Monochromatic"
    }, {
        name: "analog",
        title: "Analogous"
    }, {
        name: "tri",
        title: "Triadic"
    }, {
        name: "square",
        title: "Square"
    }, {
        name: "tetra",
        title: "Tetradic"
    }],
    selected_picker_mode: "single",
    is_custom: false,
    picker_mode_title: function () {
        const currentMode = Object.values(this.picker_modes).filter(m => {
            if (m.name == this.selected_picker_mode && m.name != "custom")
                return m.title;
        });

        if (currentMode[0] == undefined)
            return "";

        return currentMode[0].title;
    },
    pointer_count: function () {
        const mode = this.selected_picker_mode;
        let count = 0;

        switch (mode) {
            case "single":
                count = 1;
                break;

            case "comple":
                count = 2;
                break;

            case "split":
            case "analog":
            case "mono":
            case "tri":
                count = 3;
                break;

            case "square":
            case "tetra":
                count = 4;
                break;

        }
        return count;
    },
    pointer_sepration: {
        "comple": [180, 180],
        "split": [140, 140, 80],
        "analog": [40, 40, 280],
        "mono": [
            [0.3, 0.3, 0.6],
            [-0.3, 0.3, 0.3],
            [-0.6, -0.3, 0.3]
        ],
        "tri": [120, 120, 120],
        "square": [90, 90, 90, 90],
        "tetra": [120, 60, 120, 60],
    },
    wheel_demo_indices: {
        "comple": [0, 6],
        "analog": [0, 1, 11],
        "mono": [0],
        "split": [0, 5, 7],
        "tri": [0, 4, 8],
        "tetra": [0, 4, 6, 10],
        "square": [0, 3, 6, 9],
        "temperature": [...Array(12).keys()],
        "primary": [0, 4, 8],
        "secondary": [2, 6, 10],
        "tertiary": [1, 3, 5, 7, 9, 11]
    },
    selected_pointer_index: 0,
    colors: [{
        degrees: 80,
        saturation: 100,
        lightness: 50,
        lightnessRotate: 90,
        left: 0,
        top: 0,
        palette: {
            darkest: "",
            darker: "",
            lighter: "",
            lightest: ""
        }
    }],
    ctrl_pressed: false,
    pointer_moving: false
});

let mutations = {
    updateMode: function (mode) {
        if (mode == 'custom') {
            store.is_custom = true;
        } else {
            store.is_custom = false;
            store.selected_picker_mode = mode;
            store.selected_pointer_index = 0;
        }
    },
    updateColor: function (index, newColors) {
        const currentColors = store.colors[index];
        const updatedColor = Object.assign({}, currentColors, newColors);
        const {
            degrees: h,
            saturation: s,
            lightness: l
        } = updatedColor;

        const color = tinycolor(`hsl(${h}, ${s}%, ${l}%)`);

        updatedColor.palette = {
            darkest: color.clone().darken(30).toHslString(),
            darker: color.clone().darken(15).toHslString(),
            lighter: color.clone().lighten(15).toHslString(),
            lightest: color.clone().lighten(30).toHslString(),
        }

        updatedColor.hsl = color.toHslString();

        Vue.set(store.colors, index, updatedColor);
    },
    removeColor: function (start) {
        store.colors.splice(start);
    },
    swapColors: function (from, to) {
        const a = store.colors[from];
        const b = store.colors[to];

        Vue.set(store.colors, from, b);
        Vue.set(store.colors, to, a);
    },
    updatePointerIndex: function (newIndex) {
        store.selected_pointer_index = parseInt(newIndex);
    },
    updateCtrl: function (newValue) {
        store.ctrl_pressed = newValue;
    },
    updateWheelMode: function (index) {
        store.picker_modes[index];
        store.wheel_demo_mode = mode;
    },
    updateMoving: function (isMoving) {
        store.pointer_moving = isMoving;
    }
};

Vue.component('pointer', {
    template: "#pointer-template",
    props: ['index', 'color-wheel'],
    data: () => {
        return {
            pointer_clicked: false,
            enlarge: false,
            enlargeTimeout: null
        }
    },
    created: function () {
        if (this.index == this.selectedPointerIndex) {
            window.addEventListener('keydown', event => {
                switch (event.code) {
                    case "ArrowUp":
                    case "ArrowDown":
                        this.changeSaturation(event.code);
                        event.preventDefault();
                        break;

                    case "ArrowLeft":
                    case "ArrowRight":
                        this.changeHue(event.code);
                        event.preventDefault();
                        break;
                }
            });
        }

        // check if lightness change and update class for 1 seconds
        this.$parent.$on("lightnessChange", () => {
            if (this.index == this.selectedPointerIndex) {
                this.enlarge = true;
                clearTimeout(this.enlargeTimeout);
                this.enlargeTimeout = setTimeout(() => {
                    this.enlarge = false;
                }, 700);
            }
        });
    },
    mounted: function () {
        // Create object for the current element
        let newObject = {
            degrees: 260,
            saturation: 100,
            lightness: 50,
            light: 90,
            palette: {
                darkest: "",
                darker: "",
                lighter: "",
                lightest: ""
            }
        }

        // update or add new color item if required
        this.updateColor(this.index, newObject);

        // assign events
        document.addEventListener("mousedown", this.mouseDown);
        document.addEventListener("mouseup", e => this.pointer_clicked = false);
        document.addEventListener("mousemove", this.movePointer);

        // this event is needed when picker  mode changes
        this.$parent.$on('updateFirstPointer', data => {
            if (this.index == 0) {
                const coord = this.getPointerPosition(this.colors[0]);
                const firstColor = Object.assign(this.colors[0], coord);
                this.updateColor(this.index, firstColor);
            }
        });

        this.$parent.$on('updateOtherPointers', data => {
            if (this.isCustom != true)
                this.getstyle();
        });
    },
    methods: {
        ...mapMethods(["updateColor", "removeColor", "updatePointerIndex", "updateMode", "updateMoving"]),
        mouseDown: function (e) {
            if (e.target.id == 'color-wheel') {
                this.pointer_clicked = true;
                if (this.ctrlPressed)
                    this.updateMode("custom");
                else
                    this.updatePointerIndex(0);
            }

            if (e.target.classList.contains("pointer")) {
                this.pointer_clicked = true;
                const pointerIndex = e.target.getAttribute("data-index");
                this.updatePointerIndex(pointerIndex);

                if (this.ctrlPressed)
                    this.updateMode("custom");
            }

        },
        changeSaturation: function (direction) {
            // increase or decrease by 5 if ctrl is pressed, otherwise jsut 1
            let factor = 1;
            if (this.ctrlPressed == true)
                factor = 5;

            // Get current color and update saturation accordingly.
            const currentColor = this.colors[this.selectedPointerIndex];
            let newSaturation = (direction == "ArrowDown") ? currentColor.saturation + factor : currentColor.saturation - factor;

            // value can only be betwen 0 and 100, no need to loop
            newSaturation = Math.max(0, Math.min(100, newSaturation));

            // merge new values with old current and overwrite degrees
            let newColor = Object.assign(currentColor, {
                saturation: newSaturation
            });

            // get position for the new values
            newColor = Object.assign(newColor, this.getPointerPosition(newColor));

            // update color picker then update other pickers
            this.updateColor(this.selectedPointerIndex, newColor);
            
            this.$parent.$emit('updateOtherPointers');
        },
        changeHue: function (direction) {
            // increase or decrease by 5 if ctrl is pressed, otherwise jsut 1
            let factor = 1;
            if (this.ctrlPressed == true)
                factor = 5;

            // Get current color and update hue accordingly.
            const currentColor = this.colors[this.selectedPointerIndex];

            let newHue = (direction == "ArrowLeft") ? currentColor.degrees + factor : currentColor.degrees - factor;

            // value can only be betwen 0 and 360
            if (newHue > 360)
                newHue = 0;

            if (newHue < 0)
                newHue = 360;

            // merge new values with old current and overwrite degrees
            let newColor = Object.assign(currentColor, {
                degrees: newHue
            });

            // get position for the new values
            newColor = Object.assign(newColor, this.getPointerPosition(newColor));

            // update current color  then update other colors
            this.updateColor(this.selectedPointerIndex, newColor);
            this.$parent.$emit('updateOtherPointers');
        },
        getPointerPosition: function (currentColor) {

            const wheel_radius = this.colorWheel.radius;
            const angle = currentColor.degrees;
            const distance = currentColor.saturation * wheel_radius / 100;

            // radians start from right center turing count clockwis.
            // hsl degress start top center turning clockwise.
            // we need to adujst the angle to provide the right measuerments
            const radians = (angle - 90) * (Math.PI / 180);
            const left = wheel_radius + distance * Math.cos(radians);
            const top = wheel_radius + distance * Math.sin(radians);

            return {
                top: top.toFixed(2),
                left: left.toFixed(2)
            }
        },
        movePointer: function (e) {
            if (!this.pointer_clicked || this.index != this.selectedPointerIndex)
                return;

            const wheel = this.colorWheel;
            let left = e.clientX - wheel.x;
            let top = e.clientY - wheel.y;

            // calcuate distance of the picker dot from color wheel
            const dx = wheel.radius - left;
            const dy = wheel.radius - top;
            let distance = Math.sqrt(dx * dx + dy * dy);

            // get angle so we can get coords once mouse outside color wheel
            let angle = Math.atan2(dy, dx);

            angle = angle < 0 ? (angle += Math.PI * 2) : angle;

            if (distance > wheel.radius) {
                left = wheel.radius - wheel.radius * Math.cos(angle);
                top = wheel.radius - wheel.radius * Math.sin(angle);
            }

            // Dont make distance greater than radius
            if (distance >= wheel.radius)
                distance = wheel.radius;

            let saturation = distance * 100 / wheel.radius;

            // -90 is to start from top center to avoid discrepancy between circle start point
            // and HSL start point
            let hslDegree = angle * 180 / Math.PI - 90;

            // after 270 degrees hsl will be a negative value starting from -90 to 0
            if (hslDegree < 0)
                hslDegree = 270 + (90 - Math.abs(hslDegree));

            let color = {
                degrees: Math.round(hslDegree),
                saturation: Math.round(saturation),
                lightness: 50,
                left: left.toFixed(2),
                top: top.toFixed(2)
            }

            this.updateColor(this.selectedPointerIndex, color);
            
            this.$parent.$emit('updateOtherPointers');
        },
        getstyle: function () {
            if (this.colors[this.index] == undefined)
                return;

            const mode = this.selectedPickerMode;
            const selectedIndex = this.selectedPointerIndex;
            const anchorColor = this.colors[selectedIndex];
            const pointer = this.index;
            const separation = this.pointerSepration[mode];

            // We need to loop through the array values. i.e if we reach the end start again to get all values
            let separationValues = [];
            if (this.index < selectedIndex)
                separationValues = [...separation.slice(selectedIndex + 1), ...separation.slice(0, this.index + 1)];
            else
                separationValues = this.pointerSepration[mode].slice(selectedIndex + 1, this.index + 1);

            // Calculate hue degree by accumlating previous values in array
            let seprationDegree = 0;
            if (separationValues.length != 0)
                seprationDegree = separationValues.reduce((acc, value) => acc + value);

            if (pointer != selectedIndex) {

                let newColor = {};
                newColor.seprationDegree = seprationDegree;

                if (mode != 'mono') {
                    // change hue degree and keep within 360 for tinyscript
                    let degrees = anchorColor.degrees + seprationDegree;
                    if (degrees > 360)
                        degrees = degrees - 360;

                    newColor.degrees = degrees;
                    newColor.saturation = anchorColor.saturation;

                } else {
                    // for monochrome change saturation other change hue degree
                    let newSaturation = anchorColor.saturation
                    if (selectedIndex != 0)
                        newSaturation = 100 - anchorColor.saturation;
                    // For mono colors, the factor is third. Once we get it we can subtract 
                    // from anchor color to get new one. (factor is negative to move forward when subtracting
                    // and backgward when positive)
                    const third = newSaturation * separation[selectedIndex][pointer];
                    let saturation = anchorColor.saturation - (third);

                    // Saturation can not exceed 100% or be less than 0
                    if (saturation > 100)
                        saturation = 100;
                    if (saturation < 0)
                        saturation = 0;

                    newColor.degrees = anchorColor.degrees;
                    newColor.saturation = saturation;
                }

                newColor.lightness = this.colors[pointer].lightness;


                let coord = this.getPointerPosition(newColor);
                newColor = Object.assign(newColor, coord);

                this.updateColor(pointer, newColor);
            }
        },
    },
    computed: {
        ...mapState(["selected_picker_mode", "pointer_count", "pointer_sepration", "colors", "ctrl_pressed", "is_custom", "selected_pointer_index"]),
        selectedColor: function () {
            return this.colors[this.selectedPointerIndex];
        },
        left: function () {
            if (this.colors[this.index] == undefined)
                return 0;

            return this.colors[this.index].left + "px";
        },
        top: function () {
            if (this.colors[this.index] == undefined)
                return 0;
            return this.colors[this.index].top + "px";
        },
        background: function () {
            if (this.enlarge == false)
                return '';

            let c = this.selectedColor;
            return `hsl(${c.degrees}, ${c.saturation}%, ${c.lightness}%)`
        }

    },
    watch: {
        selectedPickerMode: function () {
            this.$parent.$emit('updateOtherPointers');
            this.$parent.$emit('updateFirstPointer');
        },
        pointer_clicked: function(isClicked){
            this.updateMoving(isClicked);
        }
    },
    destroyed: function () {
        this.removeColor(this.index);
    }
});

Vue.component('color-wheel', {
    template: "#color-wheel-template",
    data: () => {
        return {
            color_wheel: {},
            lightness_picker_el: null,
            timeout: null
        }
    },
    created: function () {
        // attach events to move picker inside color wheel
        document.addEventListener("mousedown", e => {
            if (e.target.id == 'lightness-picker')
                this.lightness_clicked = true;
        });

        document.addEventListener("mouseup", e => this.lightness_clicked = false);
        document.addEventListener("mousemove", this.moveLightness);

        // update wheel position and size on window resize
        window.addEventListener("resize", this.updateWheelPosition);

        // update selected index when number is pressed
        window.addEventListener('keydown', event => {
            switch (event.code) {
                case "Digit1":
                case "Digit2":
                case "Digit3":
                case "Digit4":
                    const index = parseInt(event.key);

                    if (index <= this.pointerCount)
                        this.updatePointerIndex(index - 1);
                    break;
            }
        });
    },
    mounted: function () {

        // get color wheel dimensions, position and radius
        this.color_wheel = this.$el.querySelector("#color-wheel").getBoundingClientRect();
        this.color_wheel.radius = this.color_wheel.height / 2;

        this.updateMode("comple");

        this.lightness_picker_el = this.$el.querySelector("#lightness-picker");


        this.$el.addEventListener('wheel', this.changeLightness);

        document.querySelector("#app").addEventListener("scroll", this.updateWheelPosition);
    },
    methods: {
        ...mapMethods(["updateColor", "updatePointerIndex", "initColors", "updateMode", "removeColor"]),
        changeLightness: function (e) {
            let rotate = this.currentColor.lightnessRotate;
            let factor = (this.ctrlPressed) ? 5 : 1;

            let newRotate = 0;

            if (e.deltaY < 0)
                newRotate = rotate + factor;
            else
                newRotate = rotate - factor;


            if (newRotate < 0)
                newRotate = 360;

            if (newRotate > 360)
                newRotate = 0;

            // get lightness percentage. if over 180
            const lightnessRotate = (newRotate > 180) ? 360 - newRotate : newRotate;

            const lightness = lightnessRotate * 100 / 180;


            this.updateColor(this.selectedPointerIndex, {
                lightness: Math.round(lightness),
                lightnessRotate: Math.round(newRotate)
            });

            // emit event to enlarge picker
            this.$emit('lightnessChange');

            e.preventDefault();
        },
        moveLightness: function (e) {
            if (!this.lightness_clicked)
                return;

            let left = e.clientX - this.color_wheel.x;
            let top = e.clientY - this.color_wheel.y;

            const dx = this.color_wheel.radius - left;
            const dy = this.color_wheel.radius - top;

            // get angle 
            let angle = Math.atan2(dy, dx);

            // -90 to start from from top center
            let degrees = angle * 180 / Math.PI - 90;

            // the first quarter will be over positive and the rest of the circle will negative degrees.
            // Adjust for that case
            if (degrees < 0)
                degrees = 360 - Math.abs(degrees);

            // get lightness percentage. if over 180
            const lightnessRotate = (degrees > 180) ? 360 - degrees : degrees;

            const lightness = lightnessRotate * 100 / 180;

            // emit event to enlarge picker
            this.$emit('lightnessChange');
            this.updateColor(this.selectedPointerIndex, {
                lightness: Math.round(lightness),
                lightnessRotate: Math.round(degrees)
            });
        },
        updateWheelPosition: function (e) {
            clearTimeout(this.timeout);
            this.timeout = setTimeout(() => {
                this.color_wheel = this.$el.querySelector("#color-wheel").getBoundingClientRect();
                this.color_wheel.radius = this.color_wheel.height / 2;
            }, 500);

        }
    },
    computed: {
        ...mapState(["colors", "pointer_count", "selected_pointer_index", "selected_picker_mode", "ctrl_pressed", "pointer_moving"]),
        currentColor() {
            return this.colors[this.selectedPointerIndex];
        },
        lightnessStyle: function () {
            let c = this.currentColor;

            const start = `hsl(${c.degrees}, ${c.saturation}%, 0%)`;
            const middle = `hsl(${c.degrees}, ${c.saturation}%, 50%)`;
            const end = `hsl(${c.degrees}, ${c.saturation}%, 90%)`;

            return {
                backgroundImage: `linear-gradient(180deg, ${start}, ${middle}, ${end})`
            }
        },
        lightnessRotation: function () {

            const rotate = this.currentColor.lightnessRotate;
            //const rotate = lightness * 180 / 100;
            return {
                transform: `rotate(${rotate}deg)`
            }
        },
        pointerCount() {
            return store.pointer_count();
        }
    }

});

Vue.component('color-theory-wheel', {
    template: "#color-theory-wheel-template",
    props: {
        mode: "split",
        title: "",
        static: false,
        wheelStyle: ""
    },
    data: () => {
        return {
            current_hover: 0,
            count: 12
        }
    },
    methods: {
        processHover: function (index) {
            if (this.static == true)
                return [];

            this.current_hover = index;
        }
    },
    computed: {
        ...mapState(["wheel_demo_indices"]),
        modeIndices: function () {
            return this.wheelDemoIndices[this.mode];
        },
        highlightIndices: function () {
            if (this.modeIndices == undefined)
                return [];

            return this.modeIndices.map(i => {
                let newCount = this.current_hover + i
                if (newCount >= this.count)
                    newCount = newCount - this.count;

                return newCount;
            })
        }
    },
    watch: {
        mode: function (newMode) {
            switch (newMode) {
                case "primary":
                case "secondary":
                case "tertiary":
                case "tempreature":
                    this.current_hover = 0;
                    break;
            }
        }
    }
});

Vue.component('colors-display', {
    template: "#colors-display-template",
    data: () => {
        return {
            entered: -1,
            dragged: -1,
            colors_models: [{
                name: "hsl",
                value: ""
            }, {
                name: "rgb",
                value: ""
            }, {
                name: "hex",
                value: ""
            }, {
                name: "hsv",
                value: ""
            }],
            current_model_index: 0,
            copy_message: ""
        }
    },
    mounted: function () {
        const clipboard = new ClipboardJS('.copy-value');

        clipboard.on('success', e => {
            this.copy_message = "copied!"

            e.clearSelection();
        });

        clipboard.on('error', e => {
            this.copy_message = "fAILED!"
        });
    },
    methods: {
        ...mapMethods(['swapColors']),
        getBackground: function (index) {
            const c = this.colors[index];

            return {
                "background-color": c.hsl
            }
        },

        dragstart: function (index, e) {
            this.dragged = index;
            e.dataTransfer.setData('text/plain', 'dummy');
        },
        dragend: function () {
            this.swapColors(this.dragged, this.entered);
            this.entered = -1;
        },
        dragenter: function (index) {
            this.entered = index;
        },
        colorModelSpinner: function () {
            const currentIndex = this.current_model_index;
            const models = this.colors_models;

            let nextIndex = currentIndex + 1;
            if (nextIndex > models.length - 1)
                nextIndex = 0;

            this.current_model_index = nextIndex;
        },
    },
    computed: {
        ...mapState(["colors", "selected_pointer_index", ]),
        tinycolor: function () {
            const c = this.colors[this.selectedPointerIndex];

            return tinycolor(`hsl(${c.degrees}, ${c.saturation}%, ${c.lightness}%)`);
        },
        currentModel: function () {
            const model_index = this.current_model_index;
            return this.colorsModelsValues[model_index];
        },
        colorsModelsValues: function () {
            //const colorObject = {};
            return this.colors_models.map(c => {
                return {
                    name: c.name,
                    value: this[c.name]
                };
            });
        },
        hsl: function () {
            return this.tinycolor.toHslString();
        },
        rgb: function () {
            return this.tinycolor.toRgbString();
        },
        hex: function () {
            return this.tinycolor.toHexString();
        },
        hsv: function () {
            return this.tinycolor.toHsvString();
        },
        pointerCount: function () {
            return store.pointer_count();
        },
        arrowStyle: function () {
            const pointer = this.selectedPointerIndex;
            const width = 100 / this.pointerCount;
            const left = (width * pointer) + (width / 2);

            return {
                left: `${left}%`
            }
        }
    }
});

Vue.component('pages-nav', {
    template: '#pages-nav-template',
    data() {
        return {
            current_page: 0,
            pages: [],
            hovered_dot_index: -1,
            hovered_dot_position: null,
            title_position: 0,
            container: null
        };
    },
    created: function () {
        window.addEventListener('keydown', event => {
            switch (event.code) {
                case "PageUp":
                    if(this.current_page > 0)
                        this.current_page--;

                    event.preventDefault();
                    break;

                case "PageDown":
                    if(this.current_page < 2)
                        this.current_page++;
                    event.preventDefault();
                    break;
            }
        });
    },
    mounted() {
        this.container = document.querySelector("#app");
        let divs = document.querySelectorAll("#app > div");

        for (i = 0; i < divs.length; ++i) {
            let element = divs[i];
            const obj = {
                index: i,
                title: element.getAttribute("data-title"),
                element: element
            }
            this.pages.push(obj);
        }

        
    },
    methods: {
        dotHover: function (index, event) {
            this.hovered_dot_index = index;

            const offsetTop = event.target.offsetTop;
            const height = event.target.getBoundingClientRect().height;

            this.title_position = offsetTop + (height / 2);
        }
    },
    computed: {
        pageTitle: function () {
            if (this.hovered_dot_index == -1)
                return "";

            return this.pages[this.hovered_dot_index].title;
        }
    },
    watch: {
        current_page: function (newVal) {
            let height = this.container.getBoundingClientRect().height;
            this.container.scrollTo({
                top: height * newVal,
                left: 0,
                behavior: 'smooth'
            });
        }
    }
});

Vue.component('about', {
    template: '#about-template',
    data() {
        return {
            info: ['This project provides a complete, easy and intuitive palette generator. Using the color wheel, you can create any type of palette and with live preview you can see how colors would match (or not) on a real website.',
                'In addition, keyboard shortcuts and mouse wheel are used to fine-tune selection and provide greater control over the end results.',
                'I have added a color theory summary to provide a general understanding of how colors are used.'
            ],
            shortcuts: [{
                    keys: ['up', '/', 'down'],
                    desc: "Decrease / Increase saturation by 1%"
                },
                {
                    keys: ['left', '/', 'right'],
                    desc: "Increase / Decrease hue by 1 degree"
                },
                {
                    keys: ['ctrl', '+', 'up', '/', 'down'],
                    desc: "Decrease / Increase saturation by 5%"
                },
                {
                    keys: ['ctrl', '+', 'left', '/', 'right'],
                    desc: "Increase / Decrease hue by 5 degrees"
                },
                {
                    keys: ["mouse-wheel"],
                    desc: "Increase / decrease lightness by 1 degree"
                },
                {
                    keys: ["Ctrl", "+", "mouse-wheel"],
                    desc: "Increase / decrease lightness by 5 degrees"
                },
                {
                    keys: ["1", "2", "3", "4"],
                    desc: "Select color pointer"
                },
                {
                    keys: ["ctrl", "+", "mouse-click-left"],
                    desc: "Custom color mode"
                },
                {
                    keys: ["page down", "/", "page up"],
                    desc: "Move between sections"
                },
                {
                    keys: ["ctrl", "+", "m"],
                    desc: "Cycle between color modes"
                }
            ],
            made_with: [{
                    title: "vue.js",
                    link: "https://vuejs.org/"
                },
                {
                    title: "Bootstrap",
                    link: "https://getbootstrap.com/"
                },
                {
                    title: "TinyColor",
                    link: "https://bgrins.github.io/TinyColor/"
                },
                {
                    title: "Charts.js",
                    link: "https://www.chartjs.org/"
                },
                {
                    title: "clipboard.js",
                    link: "https://clipboardjs.com/"
                },
                {
                    title: "Font Awesome",
                    link: "https://fontawesome.com/"
                },
                {
                    title: "Google fonts",
                    link: "https://fonts.google.com/"
                }
            ]
        };
    },

    mounted() {

    },
    methods: {

    },
    computed: {

    }
});


Vue.component('chart', {
    template: '<canvas ref="myChart" :width="width" :height="height"></canvas>',
    data() {
        return {
            chart: null
        };
    },
    props: {
        type: {
            type: String,
            default: "line"
        },
        width: {
            type: Number,
            validator: value => value > 0,
            default: 651
        },
        height: {
            type: Number,
            validator: value => value > 0,
            default: 350
        },
        labels: Array,
        datasets: {
            type: Array,
            required: true
        },
        options: Object
    },
    mounted() {
        this.chart = new Chart(this.$refs.myChart, {
            type: this.type,
            data: {
                labels: this.labels,
                datasets: this.datasets
            },
            options: this.options,

        });
    },
    beforeDestroy() {
        if (this.chart) {
            this.chart.destroy()
        }
    },
    watch: {
        datasets(newDatasets) {
            this.chart.data.datasets = newDatasets;
            this.chart.update();
        }
    },
});

Vue.component('demo', {
    template: "#demo-template",
    data: () => {
        return {
            settings: {
                darkmode: false,
                default_sidebar_bg: false,
                hide_text: false
            },
            chart: null,
            admin_cards: [{
                    title: "Total traffic",
                    number: "214,126",
                    percentage: "5.1",
                    icon_class: "fas fa-eye"
                },
                {
                    title: "New users",
                    number: "12,186",
                    percentage: "2.6",
                    icon_class: "fas fa-users"
                },
                {
                    title: "Sales",
                    number: "3,186",
                    percentage: "8.1",
                    icon_class: "fas fa-dollar-sign"
                }
            ],
            page_visits: [{
                name: "/color-wheel/",
                visitors: "7,548",
                unique: "657",
                rate: "31,85%",
                direction: "up"
            }, {
                name: "/color-wheel/theory.html",
                visitors: "4,548",
                unique: "201",
                rate: "40,25%",
                direction: "down"
            }, {
                name: "/color-wheel/index.html",
                visitors: "3,268",
                unique: "321",
                rate: "35,25%",
                direction: "up"
            }, {
                name: "/color-wheel/about.html",
                visitors: "2,548",
                unique: "186",
                rate: "42,25%",
                direction: "down"
            }, ],
            social_ref: [{
                name: "facebook",
                visitors: "7,548",
                percentage: "60"
            }, {
                name: "codepen",
                visitors: "10,548",
                percentage: "70"
            }, {
                name: "google",
                visitors: "6,548",
                percentage: "40"
            }, {
                name: "instagram",
                visitors: "2,548",
                percentage: "25"
            }],
            line_chart: {
                source: "traffic",
                labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
                traffic_chart: [{
                    label: 'Visitors',
                    fill: false,
                    data: [131800, 131150, 141004, 151042, 161074, 141878, 161768, 175987, 181897, 192872, 203224, 214126],
                    borderColor: "white",
                    borderWidth: 5,
                    pointRadius: 0,
                    borderCapStyle: "round"
                }],
                sales_chart: [{
                    label: 'Sales',
                    fill: false,
                    data: [2015, 45897, 4598, 15042, 16074, 45987, 12356, 78971, 87865, 12587, 14963, 12008],
                    borderColor: "white",
                    borderWidth: 5,
                    pointRadius: 0,
                    borderCapStyle: "round"
                }],
                options: {
                    tooltips: {
                        mode: 'index',
                        intersect: false
                    },
                    legend: {
                        display: false
                    },
                    animation: {
                        onComplete: function (ss) {
                            //console.log(ss);
                        }
                    },
                    scales: {
                        yAxes: [{
                            ticks: {
                                callback: function (value, index, values) {
                                    if (value == 0)
                                        return 0;


                                    return value.toString().slice(0, -3) + 'k';
                                },
                                padding: 10,
                                fontColor: "lightgray"
                            },
                            gridLines: {
                                borderDash: [2, 4],
                                zeroLineWidth: 0,
                                drawBorder: false,

                            }
                        }],
                        xAxes: [{
                            ticks: {
                                fontColor: "lightgray"
                            }
                        }]
                    }
                }
            }
        }
    },
    mounted: function () {
        this.style = document.documentElement.style;
    },
    methods: {
        updateData: function (type) {
            Vue.set(this.line_chart, "source", type);
        },
        mostReadable: function (color, grades) {
            const matchAgainst = Object.values(grades);
            return tinycolor.mostReadable(color, matchAgainst).toHslString();
        },
        getNewStyle: function () {
            if (this.selectedPickerMode == "single" || this.colors.length < 2)
                return;

            const newColors = {};
            const first = this.colors[0];
            const second = this.colors[1];
            const third = this.colors[2] || {};
            const fourth = this.colors[3] || {};

            let firstColor = tinycolor(first.hsl);

            // Header bg color and text color
            newColors['--demo-primary-bg'] = first.hsl;
            newColors['--demo-primary-color'] = this.mostReadable(first.hsl, first.palette);

            // Button colors (active, hover, .. etc)
            newColors['--demo-btn-bg'] = first.palette.darker;
            newColors['--demo-btn-border'] = first.palette.darkest;
            newColors['--demo-btn-color'] = (firstColor.isDark()) ? "white" : "black";
            newColors['--demo-btn-active-color'] = this.mostReadable(first.palette.lighter, first.palette);
            newColors['--demo-btn-active'] = first.palette.lighter;
            newColors['--demo-btn-active-border'] = first.palette.darkest;

            // header color
            newColors['--demo-header-card'] = second.hsl;
            newColors['--demo-header-card-text-1'] = this.mostReadable(second.hsl, second.palette);
            newColors['--demo-header-card-text-2'] = this.mostReadable(second.hsl, second.palette);
            newColors['--demo-text-success'] = this.mostReadable(second.hsl, first.palette);

            switch (this.selectedPickerMode) {
                case "comple":
                    if (this.settings.default_sidebar_bg == false) {
                        const sidebarHover = this.mostReadable(first.hsl, first.palette);
                        newColors['--demo-sidebar-bg'] = first.palette.lighter;
                        newColors['--demo-sidebar-hover'] = sidebarHover;
                        newColors['--demo-sidebar-hover-color'] = this.mostReadable(sidebarHover, first.palette);
                        newColors['--demo-sidebar-text-color'] = (firstColor.isDark()) ? "white" : "black";
                    }
                    break;

                case "square":
                case "tetra":
                    if(this.colors.length < 4)
                        return;

                    newColors['--demo-scrollbar-foreground'] = fourth.palette.darkest;
                    newColors['--demo-scrollbar-background'] = fourth.palette.lighter;


                case "split":
                case "analog":
                case "mono":
                case "tri":
                case "square":
                case "tetra":
                    if(this.colors.length < 3)
                        return;

                    const thirdColor = tinycolor(third.hsl);

                    if (this.settings.default_sidebar_bg == false) {
                        const sidebarHover = this.mostReadable(third.hsl, second.palette);
                        newColors['--demo-sidebar-bg'] = third.palette.lighter;
                        newColors['--demo-sidebar-hover'] = this.mostReadable(third.hsl, third.palette);
                        newColors['--demo-sidebar-hover-color'] = this.mostReadable(sidebarHover, third.palette);
                        newColors['--demo-sidebar-text-color'] = (thirdColor.isDark()) ? "white" : "black";
                    }

                    break;
            }

            return newColors
        }
    },
    computed: {
        ...mapState(["colors", "selected_picker_mode"]),
        linechartDataset: function () {
            if (this.line_chart.source == 'traffic')
                return this.line_chart.traffic_chart

            return this.line_chart.sales_chart;
        },

    }
});

new Vue({
    el: "#app",
    data: {
        wheel_mode: "comple",
        hide_text: false
    },
    created: function () {
        
        window.addEventListener('keyup', e => {
            this.updateCtrl(false);
        });

        window.addEventListener('keydown', e => {
            if (e.ctrlKey == true)
                this.updateCtrl(true);
        });

        window.addEventListener('keydown', e => {
            if (e.code == 'KeyM' && this.ctrlPressed)
               this.modeSpinner();
            
        });
    },
    methods: {
        updateMode: mutations.updateMode,
        updateCtrl: mutations.updateCtrl,

        modeSpinner: function () {

            if (this.isCustom == true) {
                this.updateMode(this.selectedPickerMode)
                return;
            }

            const currentMode = this.selectedPickerMode;
            const modeIndex = this.modes.findIndex(m => m.name == currentMode);
            const nextIndex = (modeIndex + 1 > this.modes.length - 1) ? 0 : modeIndex + 1;
            const nextMode = this.modes[nextIndex].name;

            this.updateMode(nextMode);
        },
        cancelCustom: function () {
            this.updateMode(this.selectedPickerMode);
        }
    },

    computed: {
        ...mapState(["picker_modes", "selected_picker_mode", "colors", "selected_pointer_index", "is_custom", "ctrl_pressed"]),
        modes() {
            let modes = [];
            this.pickerModes.map(m => {
                modes.push(m);
            });

            if (modes.length == 0)
                return [];

            return modes;
        },

        endColor: function () {

            const c = this.colors[this.selectedPointerIndex];

            return {
                "background-color": `hsl(${c.degrees}, ${c.saturation}%, ${c.lightness}%)`
            }
        },
        isStatic: function () {
            let static = false;
            switch (this.wheel_mode) {
                case "primary":
                case "secondary":
                case "tertiary":
                case "tempreature":
                    static = true;
                    break;
            }

            return static;
        },
        pickerModeTitle: function () {
            return store.picker_mode_title();
        }
    }
})

Demo code Toucan Palette Generator

Saya membagikan code ini hanya untuk tujuan pengujian dan pembelajaran. Jika dirasa Artikel Yang saya tulis melanggar Hak Cipta Silahkan kirimkan pesan kepada Saya Melalui Halaman Kontak dan Saya akan menghapusnya sesegera mungkin.

Untuk para pengunjung Blog yang saat ini sedang mencari layanan jasa pembuatan website, anda bisa kunjungi Jasa Candil Kuya di google.

Penutup

Bagaimana sobat Candil cukup mudah bukan? blog ini sengaja saya sediakan khusus untuk anda yang masih pemula di dalam dunia pemrograman, Semoga dapat bermanfaat bagi sobat Candil yang ingin belajar dasar pemrograman seperti HTML, CSS dan Juga Javascript.

Jangan lupa share dan jika ada kekurangan serta koreksi di dalam penulisan code Toucan Palette GeneratorL silahkan tulis melalui kolom komentar di bawah ini ya. Terima kasih!

Autor:
Kalimah Apps

  • Deskripsi

    Hallo Programmer kembali lagi di blog saya yang sangat sederhana ini, hari ini kita akan belajar web design mebuat tampilan Toucan Palette Generator
  • Tag

    CSS HTML Javascript Tutorial Web Design