HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URL's added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<div id="palette" class="palette" v-bind:style="{'background-color': bgcolor, '--c-background': bgcolor, '--c-background-contrast': bgcolorcontrast}" v-bind:class="{'palette---visible-settings': settingsVisible}">
<hhead v-bind:color="bgcolor" v-bind:colors="currentColors"></hhead>
<div class="settings-wrap">
<button class="settings-toggler" v-on:click="toggleSettings">
<span class="settings-toggler__label">
customize
<svg viewBox="0 0 24 24" class="dotsnav" xmlns="http://www.w3.org/2000/svg">
<g class="dots dots--bottom" transform="translate(12 12) rotate(0) translate(-12 -12)">
<circle cx="12" cy="6" r="1.5"/>
<circle cx="12" cy="12" r="1.5"/>
<circle cx="12" cy="18" r="1.5"/>
</g>
<g class="dots dots--top" transform="translate(12 12) rotate(0) translate(-12 -12)">
<circle cx="12" cy="6" r="1.5"/>
<circle cx="12" cy="18" r="1.5"/>
</g>
</svg>
</span>
</button>
<div class="settings">
<label class="setting">
<h3 class="setting__label">Method</h3>
<div class="select-wrap">
<vue-multiselect :max-height="1200" :value="currentSpace" @select="selectCurrentSpace" v-bind:selected="name === currentSpace" :options="names" :searchable="false" :close-on-select="true" :show-labels="false" placeholder="Pick a space">
<template slot="option" slot-scope="{ option }">
<strong class="option__title">{{ option }}</strong>
<span class="option__preview" v-bind:style="{'background': 'linear-gradient(to left,' + colorsBySpace(option).join(',') + ')' }"></span>
<ol>
</ol>
</template>
</vue-multiselect>
<!--select @change="selectCurrentSpace">
<option v-for="name in names" @value="name" v-bind:selected="name === currentSpace">{{name}}</option>
</select-->
</div>
</label>
<div>
<setting label="Colors" v-bind:value="colorCount">
<input type="range" v-bind:min="minColorCount" v-bind:max="maxColorCount" v-model="colorCount" step="1">
</setting>
</div>
<div>
<setting multifield="true" label="Background Color">
<div class="button-row">
<button class="button button--color" v-on:click="bgcolor = '#212121'"><i style="background: #212121"></i><strong>Black</strong></button><button class="button button--color" v-on:click="bgcolor = '#5e5e5e'"><i style="background: #5e5e5e"></i><strong>Grey</strong></button><button class="button button--color" v-on:click="bgcolor = '#fffcec'"><i style="background: #fffcec"></i><strong>Ivory</strong></button><button class="button button--color" v-on:click="bgcolor = '#fcfcfc'"><i style="background: #fcfcfc"></i><strong>White</strong></button>
<input type="color" v-model="bgcolor" />
</div>
</setting>
</div>
<div v-for="prop in space.attr">
<setting
v-if="(space.attr.find(e => (e.name === 'gradient space')) && prop.name != space.attr.find(e => (e.name === 'gradient space'))['value']) || !space.attr.find(e => (e.name === 'gradient space'))"
v-bind:label="prop.name"
multifield="prop.type === 'colorlist'"
v-bind:value="typeof(prop.value) != 'array' && typeof(prop.value) != 'object' ? prop.value : null"
>
<input v-if="prop.type === 'range'" type="range" v-bind:min="prop.min" v-bind:max="prop.max" v-model="prop.value" v-bind:step="prop.step" />
<input v-if="prop.type === 'color'" type="color" v-model="prop.value" />
<div v-if="prop.type === 'list'" class="select-wrap">
<select v-model="prop.value">
<option v-for="val in prop.values" v-bind:value="val">{{val}}</option>
<select />
</div>
<colorlistinput v-if="prop.type === 'colorlist'" v-bind:colors="prop.value" v-on:change="(colors) => { prop.value = [...colors] }" />
</setting>
</div>
</div>
</div>
<colorwheel v-on:setcolor="setColorByIndex" v-bind:colors="currentColors" v-bind:names="colorNames"></colorwheel>
<colorgradient v-bind:colors="currentColors" v-bind:names="colorNames"></colorgradient>
<colorball v-bind:colors="currentColors" v-bind:names="colorNames"></colorball>
<colorlist v-bind:colors="currentColors" v-bind:names="colorNames"></colorlist>
<cube v-bind:bgcolorcontrast="bgcolorcontrast" v-bind:colors="currentColors" v-bind:names="colorNames"></cube>
<!--colorabstract v-bind:bgcolorcontrast="bgcolorcontrast" v-bind:colors="currentColors" v-bind:names="colorNames"></colorabstract-->
<colorwatches v-on:setcolor="setColorByIndex" v-bind:colors="currentColors" v-bind:names="colorNames"></colorwatches>
<colorpie v-bind:colors="currentColors" v-bind:names="colorNames"></colorpie>
<fattext v-bind:colors="currentColors" v-bind:names="colorNames"></fattext>
<colorrows v-bind:colors="currentColors" v-bind:names="colorNames"></colorrows>
<itten v-bind:colors="currentColors" v-bind:names="colorNames"></itten>
<colortext v-bind:colors="currentColors" v-bind:bgcolorcontrast="bgcolorcontrast" v-bind:bgcolor="bgcolor">
<article>
<p>WCAG 2 level AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text, and a contrast ratio of at least 3:1 for graphics and user interface components (such as form input borders). Level AAA requires a contrast ratio of at least 7:1 for normal text and 4.5:1 for large text.
Large text is defined either as bold and at least 14 point (typically 18.66px) or regular and at least 18 point (typically 24px).</p>
<p>Large text is defined as 14 point (typically 18.66px) and bold or larger, or 18 point (typically 24px) or larger.</p>
</article>
<article>
<h2>Brewer</h2>
<p>Color schemes based on the research of <a href="https://twitter.com/ColorBrewer">Dr. Cynthia Brewer</a>, are excellent for qualitative, sequential, diverging <strong>data and map visualisation</strong>. <a href="http://www.personal.psu.edu/cab38/ColorBrewer/ColorBrewer_updates.html">Read more</a></p>
</article>
<article>
<h2>Interpolate</h2>
<p>Gradually mixing one color with another to create pleasing gradients. Changing the color space changes the way those colors are mixed together and will greatly affect the result.</p>
</article>
<article>
<h2>CIE L*a*b*</h2>
<p>CIELAB was designed to be perceptually uniform with respect to human color vision, meaning that the same amount of numerical change in these values corresponds to about the same amount of visually perceived change. This is especially visible when the gradient space is set to lightness. <a href="https://en.wikipedia.org/wiki/CIELAB_color_space">Read more</a></p>
</article>
<article>
<h2>RGB</h2>
<p>Is a mix of red green and blue as natively produced by the screen you are reading this on. By choosing the gradient space ‘red’, the color red will gradually be mixed to the amount of ‘blue’ and ‘green’ that you have chosen on the corresponding sliders.</p>
</article>
<article>
<h2>Cubehelix</h2>
<p>Those colors are generated by a helix (spiral) traversing the RGB color space. Looking at the 3-D view should explain this visually pretty nicely. Cubehelix was designed to provide a color mapping that would degrade gracefully to grayscale without losing information. Also meaning that there is a clear hierarchy in lightness.
<a href="https://www.mrao.cam.ac.uk/~dag/CUBEHELIX/">Read more</a></p>
</article>
<article>
<h2>HSL</h2>
<p>HSL stands for Hue Saturation and light. The palette is generated by rotating on the hue parameter.</p>
</article>
<article>
<h2>HULuv & HPLuv</h2>
<p>The input in <strong>HULuv</strong> and <strong>HPLuv</strong> is the same as in HSL. The generated colors are corrected for perceptual uniformity using the findings from CIE. On top of that HPLuv aims to correct the chroma, meaning that not all the colors from the RGB space can be represented. <a href="http://www.hsluv.org/comparison/">Read more</a></p>
</article>
</colortext>
<aside class="footer">
<h1 class="footer__title">color.pizza<sub>{{version}}</sub></h1>
<div class="footer__list">
<aside class="footer__section">
<h2>Color Temple</h2>
<p>Over the years I have coded multiple tools to help me understand and play with ideas sourrounding color. In an effort to make my findings accessible to a broader audience and to simply have fun, I designed and developed this tool. — <a href="https://twitter.com/meodai">David</a></p>
</aside>
<aside class="footer__section">
<h2>Technology</h2>
<ul>
<li><a href="https://vuejs.org/">vue.js</a> general application</li>
<li><a href="https://github.com/gka/chroma.js">chroma.js</a> color math heavy lifting</li>
<li><a href="http://www.hsluv.org/">HSL<sub>uv</sub></a> HSL but with corrected chroma and luminosity</li>
<li><a href="https://github.com/meodai/color-names">color-names</a> API providing the 17530 color-names used</li>
<li><a href="https://threejs.org/">three.js</a> used for the 3d color space</li>
<li><a href="https://rsms.me/inter/">Inter typeface family</a> perfectly hinted font</li>
</ul>
</aside>
<aside class="footer__section">
<h2>Thanks</h2>
<ul>
<li><a href="http://www.florianschulz.info/portfolio/">Florian Schulz</a> for letting me bounce my ideas and endless discussions about design systems</li>
<li><a href="http://www.daviddarx.com/">David Darx</a> for the visual direction and being an awesome person</li>
<li><a href="http://z43.network/">z43.network</a> and all the designers, illustrators and pixelartists involved in my hallway tests: <a href="https://twitter.com/rilemtwit">Richard Lems</a>, <a href="https://twitter.com/ENDESGA">ENDESGA</a></li>
</ul>
</aside>
</div>
<strong class="footer__subline">brought to you by <a href="http://elastiq.ch/"><ellogo v-bind:colors="currentColors" /></a></strong>
<exportjson v-bind:names="colorNames"></exportjson>
</aside>
</div>
@import url('https://rsms.me/inter/inter.css');
:root {
--c-black: #212121;
--c-white: #fff;
--c-background: var(--c-black);
--c-background-contrast: var(--c-white);
--s-toggler-button-height: 4rem;
--s-bezel: 2rem;
--font: 'Inter', sans-serif;
font-size: calc(.6rem + .4vw);
}
@supports (font-variation-settings: normal) {
:root { --font: 'Inter var', sans-serif; }
}
:root {
font-family: var(--font);
font-feature-settings: "dlig" 0, "numr" 0, "dnom" 0, "tnum" , "case" 0, "zero" 0, "frac", "sups" 0, "subs" 0, "cpsp" 0, "salt" 0, "ss01", "ss02" 0, "cv01" 0, "cv02" 0, "cv03" 0, "cv04" 0, "cv05" 0, "cv06" 0, "cv07" 0, "cv08" 0, "cv09" 0, "cv10" 0, "calt", "liga", "kern";
}
.palette {
transition: background-color 300ms 440ms ease-out;
}
a {
color: currentColor;
font-weight: 700;
text-decoration: none;
&:hover {
text-decoration: underline;
}
&[href*="//"]:not([href*="color.pizza"]) {
&::after {
display: inline-block;
content: '↓';
font-size: .8em;
font-weight: 300;
transform: translate(10%, -25%) rotate(-135deg);
transition: 300ms transform cubic-bezier(.8,.3,.25,1.75);
}
&:hover {
&::after {
transform: translate(20%, -35%) rotate(-135deg) scale(.7);
}
}
}
}
.palette--header {
z-index: 1;
position: fixed;
color: var(--c-background-contrast);
padding: 3.5rem 4rem;
width: 40%;
h1 {
font-size: 6rem;
font-weight: 700;
text-shadow: 0 0 0 transparent;
transition: 600ms text-shadow;
&:hover {
text-shadow: var(--text-shadow);
}
}
h2 {
font-size: 2.5rem;
font-weight: 100;
margin-top: .75rem;
}
}
.button-row {
display: flex;
width: 100%;
> input[type=color] {
width: calc(100% - .25em);
margin-left: .7em;
border-radius: 1em;
overflow: hidden;
cursor: pointer;
}
}
.button {
display: inline-block;
border: none;
background: transparent;
font-weight: 700;
font-size: 1.2rem;
color: var(--c-background);
padding: 0;
text-decoration: underline;
cursor: pointer;
margin-bottom: 0.25em;
& + & {
margin-left: 0.75em;
}
&--color {
position: relative;
i {
display: inline-block;
width: .75em;
height: .75em;
border-radius: 50%;
box-shadow: 0 0 0 2px var(--c-black);
}
strong {
display: none;
position: absolute;
left: 1em;
}
}
}
.settings-wrap {
z-index: 2;
position: fixed;
height: var(--s-toggler-button-height);
min-width: 10.75rem;
right: var(--s-bezel);
top: calc(2.3 * var(--s-bezel));
transition: min-width 337ms cubic-bezier(0.13,1,0.4,1) 800ms,
height 337ms cubic-bezier(0.13,1,0.4,1) 500ms,
color 200ms linear 300ms,
background-color 200ms linear 300ms;
.palette---visible-settings & {
min-width: 24rem;
height: calc(100% - calc(3 * var(--s-bezel)));
box-shadow: 0 0 0 0 var(--c-white);
color: var(--c-black);
transition: min-width 337ms cubic-bezier(0.13,1,0.4,1),
height 337ms cubic-bezier(0.13,1,0.4,1) 220ms,
color 200ms linear 300ms,
background-color 200ms linear 300ms;
}
}
.settings-toggler {
z-index: 2;
position: absolute;
right: 0;
top: 0;
left: 0;
width: 100%;
text-align: center;
height: 0;
padding: var(--s-toggler-button-height) 0 0;
font-weight: 200;
border: none;
background: none;
font-size: 1.5rem;
border-radius: 0;
color: var(--c-background-contrast);
cursor: pointer;
outline: none;
&:hover {
// box-shadow: inset 0 0 0 2px var(--c-background-contrast);
.palette---visible-settings & {
box-shadow: none;
}
}
&__label {
z-index: 1;
text-align: left;
position: absolute;
display: block;
right: 2.7rem;
top: 50%;
transform: translate(0, -50%);
margin-top: -.12ex;
padding-left: var(--s-bezel);
width: 8rem;
transition: 400ms width 300ms;
.palette---visible-settings & {
width: 19.3rem;
}
.dotsnav {
position: absolute;
top: 0;
right: -1.5rem;
fill: var(--c-background-contrast);
width: 2rem;
height: 2rem;
}
.dots {
transition: 222ms transform 100ms;
}
.palette---visible-settings & {
.dots {
fill: var(--c-black);
transition: 222ms transform 0ms;
}
.dots--bottom {
transform: translate(12px, 12px) rotate(45deg) translate(-12px, -12px);
}
.dots--top {
transform: translate(12px, 12px) rotate(-45deg) translate(-12px, -12px);
}
}
}
&::after {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
background: hotpink;
transform-origin: 100% 50%;
transform: scaleX(0);
background-color: var(--c-white);
.palette---visible-settings & {
transform: scaleX(1);
transition: transform 337ms cubic-bezier(0.13,1,0.4,1) 200ms;
}
}
.palette---visible-settings & {
color: currentColor;
}
}
.settings {
opacity: 0;
pointer-events: none;
position: absolute;
right: 0;
top: var(--s-toggler-button-height, 0);
bottom: 0;
left: 0;
font-size: 1.2rem;
box-sizing: border-box;
padding: 0 var(--s-bezel);
//backdrop-filter: blur(5px);
color: currentColor;
transition: opacity 200ms linear 500ms;
overflow: hidden;
.palette---visible-settings & {
opacity: 1;
pointer-events: auto;
overflow-y: auto;
}
& > * {
transform: translateY(-50%) translateX(50%) scale(0.5);
opacity: 0;
transition: opacity 350ms cubic-bezier(.88,0,0,1), transform 450ms cubic-bezier(.88,0,0,1);
background-color: var(--c-white);
margin: 0 calc(var(--s-bezel) * -1);
padding: 0 var(--s-bezel);
/*backdrop-filter: blur(5px);
background-color: rgba(255,255,255,.7);
*/
&:last-child {
padding-bottom: var(--s-bezel);
}
.palette---visible-settings & {
transform: scale(1);
opacity: 1;
@for $i from 1 through 15 {
&:nth-child(#{$i}) {
transition-delay: #{$i * 50ms + 150ms}, #{$i * 75ms + 100ms};
}
}
}
}
}
.setting {
display: flex;
align-items: center;
flex-wrap: wrap;
padding-bottom: 1.2em;
button {
border: none;
background: transparent;
font-weight: 700;
font-size: 1.2rem;
color: var(--c-black);
padding: 0;
text-decoration: underline;
}
&__label {
text-transform: capitalize;
flex-grow: 1;
flex-basis: 1 1 80%;
font-weight: 700;
font-size: .8em;
margin-bottom: .5em;
margin-top: .75em;
}
&__input {
width: 70%;
}
&__value {
font-size: 0.6em;
text-align: right;
flex: 1 1 20%;
}
}
.setting__colors {
width: 100%;
> button {
margin-top: .5rem;
}
}
.setting__color {
color: currentColor;
width: 100%;
display: flex;
flex-wrap: wrap;
font-size: .75rem;
margin-bottom: 0.5rem;
input, button {
color: currentColor;
border: none;
box-sizing: border-box;
padding: 0;
}
input {
flex: 0 0 calc(100% - 1.25rem);
}
button {
flex: 0 0 1.25rem;
background: none;
cursor: pointer;
font-size: 1rem;
color: currentColor;
}
}
input {
background-color: transparent;
}
input[type=range],
input[type=color] {
-webkit-appearance: none;
width: 100%;
}
input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0;
}
input[type="color"]::-webkit-color-swatch {
border: none;
}
input[type=color] {
border: 2px solid var(--c-black);
padding: 0;
height: 1.1rem;
}
// range sliders
input[type=range] {
margin: 0;
}
input[type=range]:focus {
outline: none;
&::-webkit-slider-thumb {
//height: .65rem;
//background-color: $c-white;
clip-path: polygon(100% 0%, 0% 0%, 50% 100%, 50% 100%);
//clip-path: polygon(50% 0%, 50% 0%, 0% 100%, 100% 100%);
}
}
@mixin slider-track {
width: 100%;
height: 1rem;
cursor: pointer;
animate: 0.2s;
background: transparent;
color: var(--c-black);
border-radius: 0;
border: solid var(--c-black);
border-width: 0 0 1px;
}
@mixin slider-thumb {
border: 2px solid transparent;
height: .75rem; width: .5rem;
border-radius: 0;
background: var(--c-black);
cursor: pointer;
-webkit-appearance: none;
margin-top: 0.25rem;
transition: 150ms background-color, 200ms clip-path, 200ms -webkit-clip-path;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
}
input[type=range]::-webkit-slider-runnable-track {
@include slider-track;
}
input[type=range]::-webkit-slider-thumb {
@include slider-thumb;
}
input[type=range]:focus::-webkit-slider-runnable-track {
//background: $c-black;
}
input[type=range]::-moz-range-track {
@include slider-track;
}
input[type=range]::-moz-range-thumb {
@include slider-thumb;
}
input[type=range]::-ms-track {
@include slider-track;
}
input[type=range]::-ms-fill-lower {
background: currentColor;
border: none;
border-radius: 100%;
}
input[type=range]::-ms-fill-upper {
background: currentColor;
border-radius: 100%;
box-shadow: none;
}
input[type=range]::-ms-thumb {
@include slider-thumb;
}
input[type=range]:focus::-ms-fill-lower {
//background: $c-black;
}
input[type=range]:focus::-ms-fill-upper {
//background: $c-black;
}
select,
.multiselect {
color: var(--c-black);
font-family: var(--font);
width: 100%;
box-sizing: border-box;
font-size: 1rem;
-webkit-appearance: none;
border: 0;
box-shadow: 0 1px 0 0 currentColor;
border-radius: 0;
padding: 0.25rem 1rem 0.25rem 0rem;
background-color: transparent;
transition: 150ms background-color;
&:focus {
outline: none;
background-color: transparent;
}
}
.multiselect {
padding: 0.25rem 0;
}
.multiselect__content-wrapper {
background: var(--white);
overflow: auto;
}
.multiselect__content {
overflow: hidden;
width: 100%;
padding: 0.5rem 0;
}
.multiselect__option {
position: relative;
display: block;
padding: .25rem 0;
cursor: pointer;
overflow: hidden;
}
.multiselect__single {
display: block;
}
.option__title,
.option__preview {
display: block;
}
.option__title {
position: relative;
z-index: 1;
font-size: .9em;
padding: .25rem 0;
text-shadow: 1px 1px #fff,
-1px -1px #fff,
-1px 1px #fff,
1px -1px #fff;
}
.option__preview {
position: absolute;
bottom: 0;
left: 0; right: 0;
height: 100%;
transform-origin: 100% 50%;
transform: scaleY(.2) scaleX(.4);
transition: 400ms transform cubic-bezier(.3,.7,0,1) 400ms;
}
.multiselect__option:hover .option__preview {
transform: scaleY(1) scaleX(.4);
}
.select-wrap {
width: 100%;
position: relative;
&::after {
pointer-events: none;
position: absolute;
top: 0; right: 0;
content: '↓';
font-size: .9rem;
line-height: 1.6;
}
}
.blade {
position: absolute;
cursor: pointer;
display: flex;
flex-direction: column;
height: 40vh; width: 10vh;
top: -40vh; left: 0;
box-shadow: 0 0 0 1px rgba(255,255,255,.1),
0 0 15px rgba(0,0,0,.1);
transform: translate3d(0,0,0) rotate(0deg);
transform-origin: 1vh 39vh;
border-radius: .5vh;
overflow: hidden;
transition: 200ms 200ms transform ease-in-out;
transition: 200ms 200ms transform cubic-bezier(0.250, 0.250, 0.275, 1.265);
&__label, &__value {
display: flex;
flex-direction: column;
justify-content: center;
padding: 1vh;
line-height: 1.2;
}
&__label {
color: var(--c-white);
font-size: .8rem;
padding-top: .75vh;
font-weight: 300;
line-height: 1.5;
&--inner {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
&__value {
font-size: calc(0.6rem + .3vmin);
line-height: 1.5;
font-weight: 500;
line-height: .75;
text-transform: uppercase;
background: var(--c-white);
color: currentColor;
}
}
.view {
position: relative;
height: 100vh;
overflow: auto;
margin: var(--s-bezel);
}
.view--wheel {
z-index: 1;
margin-top: 0;
overflow: hidden;
}
.fan {
position: absolute;
top: 50%;
left: 50%;
perspective: 600px;
transition: 666ms transform cubic-bezier(.88, 0, 0, 1);
transition: 666ms transform cubic-bezier(0.4, 0, 0.25, 1);
}
.view--cube {
position: relative;
overflow: hidden;
.select-wrap {
z-index: 1;
position: absolute;
bottom: 1rem;
left: 1rem;
width: auto;
min-width: 4rem;
background-position: 90% 50%;
color: var(--c-background-contrast);
select {
text-transform: uppercase;
}
&:focus {
color: var(--c-background);
}
}
}
.view--radialgradient {
overflow: hidden;
}
.radialgradient {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 75vmin; height: 75vmin;
background: #212121;
border-radius: 100%;
cursor: pointer;
}
.view--list {
display: flex;
align-items: center;
justify-content: space-around;
}
.color-list {
min-width: 60%;
//box-shadow: 0 0 10rem rgba(0,0,0,.1);
&__item {
display: flex;
justify-content: space-between;
position: relative;
padding: 0.5rem 1rem;
transition: transform 300ms cubic-bezier(.7, .3, 0, 1);
will-change: transform;
> * {
color: var(--c-black);
opacity: .5;
transition: transform 300ms cubic-bezier(.7, .3, 0, 1);
}
&:hover {
transform: scaleY(2) scaleX(1.05);
z-index: 2;
> * {
opacity: 1;
transform: scaleY(0.5) scaleX(0.95);
}
}
&::after {
z-index: -1;
opacity: .5;
content: '';
position: absolute;
left: 0; right: 0; bottom: 0; top: 0;
box-shadow: 0 0 2rem rgba(0,0,0,.3),
0 0 3rem currentColor;
}
}
}
.color-watch {
display: flex;
flex-direction: column;
width: 14.8%;
max-width: 9rem;
flex: 0 0 14.8%;
margin: .5rem;
perspective: 600px;
border-bottom: 2.7vmin solid #fff;
&__wrap {
padding-top: 100%;
}
&__inner {
position: absolute;
left: 50%;
top: 50%;
width: 14rem;
height: 17rem;
transform: translate(-50%, -50%);
//overflow: hidden;
&::after {
display: none;
border-radius: 50%;
content: '';
box-shadow: 0 0 5rem currentColor;
position: absolute;
top: 0; right: 0; left: 0; bottom: 0;
}
}
&__swatch {
position: relative;
padding-top: 100%;
z-index: 1;
&__shade {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
transform-origin: 50% 100%;
background: currentColor;
&::after {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
}
}
&__label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
flex-grow: 1;
background: #fff;
padding: .6rem .75rem;
color: #212121;
transform-origin: 50% 0;
box-shadow: 0 0 0 1px rgba(#000,.05);
}
&__title {
font-weight: 700;
font-size: 1.2em;
margin-bottom: 0.25em;
text-transform: capitalize;
}
&__subtitle {
font-size: .85em;
text-transform: uppercase;
}
&__title,
&__subtitle {
display: block;
transform-origin: 50% 100%;
}
}
$anim-appear-duration: 666ms;
.color-watch {
overflow: hidden;
box-shadow: 0 0 15px rgba(0,0,0,.1);
&__inner {
pointer-events: none;
opacity: 0;
transition: opacity $anim-appear-duration * .2 linear $anim-appear-duration * .3;
}
&__label {
transform: scaleY(0);
transition: ($anim-appear-duration * 0.2) transform cubic-bezier(.7,.3,0,1);
transition-delay: $anim-appear-duration * 0.3;
}
&__title,
&__subtitle {
opacity: 0;
transform: scaleY(0);
transition: $anim-appear-duration * 0.1 opacity linear, $anim-appear-duration * 0.2 transform cubic-bezier(.7,.3,0,1);
}
&__swatch {
transform: translate3d(0,150%,0);
transition: $anim-appear-duration * 0.666 transform cubic-bezier(.8,.3,.25,1.75);
will-change: transform;
transform-origin: 50% 0;
}
&__swatch__shade {
&:nth-child(1) {
transform: scaleY(0);
transition-delay: $anim-appear-duration * 0.2;
opacity: .3;
}
&:nth-child(2) {
transform: scaleY(0);
transition-delay: $anim-appear-duration * 0.1;
opacity: .5;
}
&:nth-child(3) {
transform: scaleY(0);
transition-delay: $anim-appear-duration * 0;
}
}
}
.color-watch:hover {
overflow: initial;
z-index: 1;
.color-watch__inner {
opacity: 1;
transition: opacity $anim-appear-duration * .1 linear;
overflow: hidden;
}
.color-watch__label {
transform: scaleY(1);
transition: ($anim-appear-duration * .2) transform cubic-bezier(.7,.3,0,1);
transition-delay: $anim-appear-duration * .2;
}
.color-watch__title,
.color-watch__subtitle {
opacity: 1;
transform: scaleY(1);
transition: ($anim-appear-duration * .1) opacity linear, ($anim-appear-duration * .2) transform cubic-bezier(.7,.3,0,1);
transition-delay: $anim-appear-duration * .4;
}
.color-watch__subtitle {
transition-delay: $anim-appear-duration * .45;
}
.color-watch__swatch {
transform: translate3d(0,0,0);
transition: ($anim-appear-duration * 0.566) transform cubic-bezier(.8,.3,.25,1);
will-change: transform;
transform-origin: 50% 0;
}
.color-watch__swatch__shade {
transition: ($anim-appear-duration * 0.666) transform cubic-bezier(.7,.3,0,1);
transform: scaleY(1);
&:nth-child(1) {
transition-delay: $anim-appear-duration * 0;
}
&:nth-child(2) {
transition-delay: $anim-appear-duration * 0.1;
}
&:nth-child(3) {
transition-delay: $anim-appear-duration * 0.2;
}
}
}
.view--watches {
overflow: hidden;
display: flex;
align-items: center;
justify-content: space-around;
}
.view__watch__list {
display: flex;
width: 80%;
flex-wrap: wrap;
align-items: center;
}
.view--pie {
overflow: hidden;
cursor: pointer;
svg {
position: absolute;
top: 50%;
left: 50%;
}
}
.pie {
transition: 400ms transform cubic-bezier(.7,.3,.0,1);
transform: translate(-50%, -50%) rotate(-90deg) scale3d(.8,.8,.8);
&--zoomed {
transform: translate(-50%, -50%) rotate(-90deg) scale3d(5,5,5);
}
&__wrap {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
.view--rows__list {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
display: flex;
align-items: stretch;
box-sizing: border-box;
overflow: hidden;
}
.color__row {
position: relative;
flex-grow: 1;
transition: flex-grow 444ms cubic-bezier(.7,.3,.0,1);
overflow: hidden;
margin: 0 -1px;
&:hover {
flex-grow: 4;
.color__row__label {
transform: translateY(0%);
}
.color__row__title,
.color__row__subtitle {
opacity: 1;
transform: scaleY(1);
}
.color__row__title {
transition-delay: $anim-appear-duration * .35;
}
.color__row__subtitle {
transition-delay: $anim-appear-duration * .4;
}
}
&__label {
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
flex-grow: 1;
background: #fff;
padding: .6rem .75rem;
color: #212121;
transform-origin: 50% 0;
box-shadow: 0 0 0 1px rgba(#000,.05);
white-space: nowrap;
transform: translateY(100%);
transition: 200ms transform cubic-bezier(.7,.3,.0,1) 300ms;
}
&__title {
font-weight: 700;
font-size: 1.2em;
margin-bottom: 0.25em;
text-transform: uppercase;
}
&__subtitle {
font-size: .85em;
text-transform: capitalize;
}
&__title,
&__subtitle {
display: block;
transform-origin: 50% 100%;
opacity: 0;
transform: scaleY(0);
transition: ($anim-appear-duration * .1) opacity linear, ($anim-appear-duration * .2) transform cubic-bezier(.7,.3,0,1);
transition-delay: $anim-appear-duration * .4;
}
}
.view--export {
display: none;
}
.view--export textarea {
position: absolute;
left: 0; top: 0;
width: 100%;
height: 100%;
background: transparent;
border: none;
color: var(--c-background);
font-size: .8rem;
}
.view--itten {
height: auto;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
button {
color: var(--c-background-contrast);
background: none;
font-size: 1.2rem;
border: none;
padding: .5em .5em;
cursor: pointer;
outline: none;
}
}
.itten__list {
display: flex;
align-items: center;
justify-content: space-around;
flex-wrap: wrap;
padding-bottom: 2rem;
}
.color__itten {
position: relative;
width: 10rem;
height: 10rem;
.itten--ball & {
border-radius: 100%;
background-image: linear-gradient(-45deg, transparent, currentColor);
overflow: hidden;
transition: 200ms border-radius linear;
&::after {
opacity: 0;
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image: linear-gradient(-45deg, transparent 50%, currentColor calc(50% + 1px));
}
&:hover {
background-image: none;
}
&:hover::after {
opacity: 1;
transform: scale(1.01);
}
}
.itten--cube & {
box-shadow: inset 0 0 0 4rem currentColor;
transition: 200ms box-shadow cubic-bezier(.5,0,.15,1),
333ms border-radius cubic-bezier(.7,.3,0,1);
&:hover {
box-shadow: inset 0 0 10rem 0 currentColor,
0 0 6rem currentColor;
}
}
.itten--triangle & {
clip-path: polygon(50% 0, 0 100%, 100% 100%);
background-image: linear-gradient(0deg, transparent 20%, currentColor);
&:hover {
background-image: linear-gradient(34deg, transparent 40%, currentColor calc(40% + 1px));
}
}
}
.itten__item {
flex: 0 0 10rem;
margin: 5%;
}
.ittem-vues {
position: absolute;
left: 1rem;
bottom: 1rem;
}
.itten__name {
display: block;
display: none;
color: var(--c-background-contrast);
font-size: .8em;
text-align: center;
line-height: 1.4;
padding-top: 1em;
em {
font-size: .75em
}
}
/*
.expand {
&::after {
animation: 500ms expand cubic-bezier(0, 0.8, 0.6, 1.8);
animation-fill-mode: forwards;
transition: 100ms opacity linear 400ms;
transform: rotate(180deg);
will-change: clip-path, opacity;
}
&:hover::after {
animation: 500ms expand-out cubic-bezier(0, .8, .6, 1);
animation-fill-mode: forwards;
transition: 200ms opacity linear;
transform: rotate(0deg);
}
}
//animation-timing-function: cubic-bezier(.7, .3, 0, 1);
@keyframes expand {
0% {
clip-path: polygon(45% 45%, 85% 45%, 70% 70%, 45% 85%);
}
20% {
clip-path: polygon(30% 30%, 70% 30%, 70% 70%, 30% 70%);
}
30% {
clip-path: polygon(30% 30%, 80% 20%, 70% 70%, 30% 70%);
}
45% {
clip-path: polygon(18% 18%, 100% 0, 80% 80%, 30% 70%);
}
55% {
clip-path: polygon(0% 0%, 100% 0, 100% 100%, 20% 80%);
}
90% {
clip-path: polygon(0% 0%, 100% 0, 100% 100%, 0% 100%);
}
100% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
}
@keyframes expand-out {
0% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
20% {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
30% {
clip-path: polygon(0% 0%, 100% 0, 100% 100%, 0% 100%);
}
40% {
clip-path: polygon(0% 0%, 100% 0, 100% 100%, 20% 80%);
}
50% {
clip-path: polygon(18% 18%, 100% 0, 80% 80%, 30% 70%);
}
70% {
clip-path: polygon(20% 20%, 80% 20%, 70% 70%, 30% 70%);
}
80% {
clip-path: polygon(30% 30%, 70% 30%, 70% 70%, 30% 70%);
}
100% {
clip-path: polygon(30% 30%, 70% 30%, 70% 70%, 30% 70%);
}
}
*/
.view--text {
position: relative;
z-index: 1;
height: auto;
background: var(--c-background);
color: var(--c-background-contrast);
padding: 3.5em 2rem;
line-height: 1.4;
h1, h2 {
font-weight: 700;
}
h1 {
font-size: 4rem;
}
h2 {
font-size: 1.8rem;
margin-top: 1.25em;
}
p {
font-weight: 300;
margin-top: .8em;
font-size: 1.2rem;
max-width: 40rem;
strong {
font-weight: 400;
}
}
}
.text {
display: flex;
justify-content: space-between;
&__sample {
padding-right: 2rem;
}
&__contrast {
font-size: .5em;
}
&__customizer {
text-align: right;
flex: 0 0 25%;
.text-customizer-bg__button,
.text-customizer__button {
cursor: pointer;
border: 0.25rem solid currentColor;
font-size: 2rem;
margin-bottom: .2em;
margin-right: .2em;
}
.text-customizer-bg__button {
background: currentColor;
}
.text-customizer__button {
background: none;
}
}
}
.footer {
position: relative;
z-index: 1;
padding: calc(2 * var(--s-bezel)) calc(2 * var(--s-bezel)) var(--s-bezel);
font-size: 1.2rem;
line-height: 1.4;
color: var(--c-background);
background-color: var(--c-background-contrast);
font-weight: 300;
h2 {
font-size: 1em;
font-weight: 700;
margin-bottom: 1.5em;
}
&__title {
position: relative;
font-size: 2em;
font-weight: 700;
margin-bottom: 1.25em;
sub {
font-size: 0.4em;
font-weight: 300;
transform: translateY(10%);
margin-left: 0.25em;
}
}
&__subline {
display: block;
margin-top: 2em;
font-weight: 700;
a {
position: relative;
&::after {
opacity: 0;
}
svg {
position: absolute;
bottom: 50%;
transform: translate(-2rem, 50%);
}
}
}
&__list {
font-size: 1em;
display: flex;
}
&__section {
flex: 0 0 calc(33.33% - 2em);
& + & {
margin-left: 2em;
}
li {
margin-top: .5em;
}
}
}
.elastiq-logo {
display: inline-block;
width: 12rem;
height: auto;
.font {
fill: currentColor;
}
.elastiq {
fill: none;
stroke: var(--c-background);
stroke-linecap: round;
stroke-linejoin: round;
stroke-width:11.4px;
}
}
.view--abstract {
overflow: hidden;
&::after {
content: '';
position: absolute;
background: linear-gradient(180deg, transparent, var(--c-background) 60%);
bottom: 0;
left: 0;
right: 0;
height: 50%;
}
div {
position: absolute;
top: 75%; left: 50%;
width: 75%;
height: 75%;
background: linear-gradient(45deg, var(--gradient));
transform: translate(-50%, -50%) rotate(30deg) scale(.8);
&::before {
opacity: .7;
content: '';
position: absolute;
top: -4rem; right: 0; bottom: 0; left: -4rem;
background: linear-gradient(45deg, var(--gradient));
filter: blur(4rem);
}
&::after {
content: '';
position: absolute;
top: 1rem; right: 0; bottom: 0; left: 1rem;
background: radial-gradient(circle at 0% 0%, transparent 20%, var(--c-background) 70%);
transform: scale(1.2);
filter: blur(1rem);
}
}
}
.view--fattext {
padding: 3rem 2rem;
min-height: 100vh;
height: auto;
position: relative;
z-index: 1;
font-size: 4rem;
font-weight: 700;
text-align: left;
background: var(--c-background);
h3 {
text-align: left;
line-height: 1.5;
}
}
/**
* # todo:
* ## [ ] Status / Benefit:Complxity / "Description"
* [ ] 3:1 "Think about patreon / monetizing / sponsoring"
* [ ] 3:1 "Make a SVG/Image export"
* [x] 3:1 "Add Close ICON to customize pannel"
* [ ] 3:1 "Add a section with simple text. (Possibly the color space descriptions"
* [x] 3:1 "Make colors clickable (Change Context color on click)"
* [ ] 3:2 "Add some kind of nav (dots or something for the different views)"
* [ ] 3:3 "Save settings"
* [ ] 3:3 "FF & Safari testing"
* [ ] 3:3 "Mobile Friendlyness"
* [X] 2:1 "Add HWB"
* [ ] 2:1 "Add CIELCHuv"
* [x] 2:1 "More padding in pannel"
* [x] 2:1 "Have a default background color per mode"
* [x] 2:1 "Write a short paragraph for each color space"
* [ ] 2:2 "overlay preview gradients over sliders"
* [ ] 2:2 "Only render views when they are close the the scrollable area"
* [ ] 2:2 "Add stats (metrics: I what to know if people scroll, and what they interact with)"
* [ ] 2:2 "Make values inputable on sliders (text input)"
* [ ] 2:2 "Add starting color"
* [x] 2:3 "Make it possible to add colors manually (black / white)"
* [ ] 2:3 "Make a draggable view to be able to compose colors manually" https://github.com/catc/displace
* [x] 1:1 "Automatically close the sidepannel when scrolling a bit"
* [ ] 1:1 "Collabsible Footer"
* [x] 1:1 "Add HCL color space (no need is the same as LCH)"
* [ ] 1:1 "Make UI hideable"
* [ ] 1:1 "Replace chroma.js with culori"
* [~] 1:2 "Font weight / leight height tuning"
* [x] 1:2 "Wheel over font"
**/
console.clear();
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js';
import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
Vue.component('vue-multiselect', window.VueMultiselect.default)
const contrastMinimums = {
aa: 4.5,
aaLarge: 3,
aaa: 7,
aaaLarge: 4.5
};
const ypbprToRGB = (y, pb, pr, kb = 0.0722, kr = 0.2126) => {
const r = y + 2 * pr * (1 - kr);
const b = y + 2 * pb * (1 - kb);
const g = (y - kr * r - kb * b) / (1 - kr - kb);
return [r*255,g*255,b*255];
};
const ucsToxyz = function(ucs) {
const u = ucs[0],
v = ucs[1],
w = ucs[2];
return [
1.5 * u,
v,
1.5 * u - 3 * v + 2 * w
];
};
function ryb2rgb(color) {
var r = color[0], y = color[1], b = color[2];
// Remove the whiteness from the color.
var w = Math.min(r, y, b);
r -= w;
y -= w;
b -= w;
var my = Math.max(r, y, b);
// Get the green out of the yellow and blue
var g = Math.min(y, b);
y -= g;
b -= g;
if (b && g) {
b *= 2.0;
g *= 2.0;
}
// Redistribute the remaining yellow.
r += y;
g += y;
// Normalize to values.
var mg = Math.max(r, g, b);
if (mg) {
var n = my / mg;
r *= n;
g *= n;
b *= n;
}
// Add the white back in.
r += w;
g += w;
b += w;
// And return back the ryb typed accordingly.
return [r, g, b];
}
const xyzToRGB = function (_xyz, white = [100, 100, 100]) {
const x = _xyz[0] / white[0],
y = _xyz[1] / white[1],
z = _xyz[2] / white[2];
// assume sRGB
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
let r = (x * 3.240969941904521) + (y * -1.537383177570093) + (z * -0.498610760293);
let g = (x * -0.96924363628087) + (y * 1.87596750150772) + (z * 0.041555057407175);
let b = (x * 0.055630079696993) + (y * -0.20397695888897) + (z * 1.056971514242878);
r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
: r = (r * 12.92);
g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
: g = (g * 12.92);
b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
: b = (b * 12.92);
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
}
const colorSpaces = [
{
name: ['EDG-RGB-Hue'],
hasStart: false,
bg: '#212121',
attr: [
{
name: 'stops',
type: 'colorlist',
value: [
'#72ffd7',
'#f03b50',
'#0f0b50'
]
},
{
name: 'edg-mix',
min: .2,
max: .8,
step: .001,
value: .5,
type: 'range',
},
{
name: 'space-a',
value: 'rgb',
values: ['lab', 'hsl', 'hsv', 'hsi', 'lch', 'rgb', 'lrgb'],
type: 'list',
},
{
name: 'space-b',
value: 'hsv',
values: ['lab', 'hsl', 'hsv', 'hsi', 'lch', 'rgb', 'lrgb'],
type: 'list',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let scaleRGB = chroma.scale(attrs['stops'].value).mode(attrs['space-a'].value).colors(options.colors);
let scaleHSV = chroma.scale(attrs['stops'].value).mode(attrs['space-b'].value).colors(options.colors);
/*let padding = parseFloat(attrs.padding.value);
if (padding !== 0) {
scale.padding(padding);
}*/
return scaleRGB.map((color, i) => {
return chroma.mix(color, scaleHSV[i], attrs['edg-mix'].value, 'rgb').hex();
});
}
},
{
name: ['HSL', 'HSLuv', 'HPLuv'],
bg: '#212121',
hasStart: true,
attr: [
{
name: 'gradient space',
value: 'hue',
values: ['light', 'saturation', 'hue'],
type: 'list',
excludeValue: true,
},
{
name: 'hue',
min: 0,
max: 360,
step: 1,
value: 0,
type: 'range',
},
{
name: 'saturation',
min: 0,
max: 1,
step: 0.01,
value: 1,
type: 'range',
},
{
name: 'light',
min: 0,
max: 1,
step: 0.01,
value: .8,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['hue', 'saturation', 'light'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let color;
let componentPercent = i / options.colors;
if (mode.toLowerCase() === 'hsluv') {
color = hsluv.hsluvToHex([
iterate === space[0] ? componentPercent * 360 : parseFloat(attrs[space[0]].value),
iterate === space[1] ? componentPercent * 100 : parseFloat(attrs[space[1]].value) * 100,
iterate === space[2] ? componentPercent * 100 : parseFloat(attrs[space[2]].value) * 100,
]);
} else if (mode.toLowerCase() === 'hpluv') {
color = hsluv.hpluvToHex([
iterate === space[0] ? componentPercent * 360 : parseFloat(attrs[space[0]].value),
iterate === space[1] ? componentPercent * 100 : parseFloat(attrs[space[1]].value) * 100,
iterate === space[2] ? componentPercent * 100 : parseFloat(attrs[space[2]].value) * 100,
]);
} else {
color = chroma.hsl([
iterate === space[0] ? componentPercent * 360 : parseFloat(attrs[space[0]].value),
iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value),
iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value),
]).get('hex');
}
colors.push(color);
}
return colors;
}
},
{
name: ['HWB'],
bg: '#212121',
hasStart: true,
attr: [
{
name: 'gradient space',
value: 'blackness',
values: ['hue', 'blackness', 'whiteness'],
type: 'list',
excludeValue: true,
},
{
name: 'hue',
min: 0,
max: 360,
step: 1,
value: 24,
type: 'range',
},
{
name: 'whiteness',
min: 0,
max: 1,
step: 0.01,
value: 0.7,
type: 'range',
},
{
name: 'blackness',
min: 0,
max: 1,
step: 0.01,
value: .8,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['hue', 'whiteness', 'blackness'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let color;
let componentPercent = i / options.colors;
let h = iterate === space[0] ? componentPercent * 360 : parseFloat(attrs[space[0]].value);
let b = iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value);
let w = iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value)
let [ hsvH, hsvS, hsvV ] = [
h,
b === 1 ? 0 : 1 - w / (1 - b) * 1,
1 - w
];
color = chroma.hsv([
hsvH,
hsvS,
hsvV,
]).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['XYZ'],
bg: '#212121',
hasStart: true,
attr: [
{
name: 'gradient space',
value: 'y',
values: ['x', 'y', 'z'],
type: 'list',
excludeValue: true,
},
{
name: 'x',
min: 0,
max: 95.045592705167,
step: 0.01,
value: 29,
type: 'range',
},
{
name: 'y',
min: 0,
max: 100,
step: 0.01,
value: 20,
type: 'range',
},
{
name: 'z',
min: 0,
max: 108.9057750759878,
step: 0.01,
value: 70,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['x', 'y', 'z'];
let iterate = attrs['gradient space'].value;
let max = attrs[iterate].max;
for (let i = 0; i < options.colors; i++) {
let color;
let componentPercent = (i / options.colors) * max;
let x = iterate === space[0] ? componentPercent : parseFloat(attrs[space[0]].value);
let y = iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value);
let z = iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value);
let rgb = xyzToRGB([x,y,z]);
color = chroma.rgb(rgb).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['ucs'],
bg: '#212121',
hasStart: true,
attr: [
{
name: 'gradient space',
value: 'u',
values: ['u', 'v', 'w'],
type: 'list',
excludeValue: true,
},
{
name: 'u',
min: 0,
max: 100,
step: 0.01,
value: 29,
type: 'range',
},
{
name: 'v',
min: 0,
max: 100,
step: 0.01,
value: 20,
type: 'range',
},
{
name: 'w',
min: 0,
max: 100,
step: 0.01,
value: 70,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['u', 'v', 'w'];
let iterate = attrs['gradient space'].value;
let max = attrs[iterate].max;
for (let i = 0; i < options.colors; i++) {
let color;
let componentPercent = (i / options.colors) * max;
let u = iterate === space[0] ? componentPercent : parseFloat(attrs[space[0]].value);
let v = iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value);
let w = iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value);
let rgb = xyzToRGB(ucsToxyz([u,v,w]));
color = chroma.rgb(rgb).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['Harmonies'],
hasStart: false,
bg: '#212121',
attr: [
{
name: 'color',
type: 'color',
value: '#72ffd7',
},
{
name: 'scheme',
value: 'splitcomplementary',
values: ['complementary' ,'splitcomplementary', 'triadic', 'tetradic', 'analogous', 'square'],
type: 'list',
harmonies: {
complementary: { h: [0, 180] },
splitcomplementary: { h: [0, 150, 210] },
triadic: { h: [0, 120, 240] },
tetradic: { h: [0, 60, 180, 240] },
analogous: { h: [-30, 0, 30] },
square: { h: [0, 90, 180, 270] },
}
},
{
name: 'space',
value: 'lab',
values: ['lab', 'hsl', 'hsv', 'hsi', 'lch', 'rgb', 'lrgb', 'num'],
type: 'list',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
const harmonies = attrs['scheme'].harmonies;
const harmonyModel = harmonies[attrs['scheme'].value];
const color = attrs['color'].value;
const colors = [];
for (let componentKey in harmonyModel) {
let currentColor = chroma(color);
for (let val in harmonyModel[componentKey]) {
let value = harmonyModel[componentKey][val];
let operator = Math.sign(value) < 0 ? '-' : '+';
colors.push(currentColor.set('hsl.' + componentKey, operator + Math.abs(value)).hex());
}
}
return chroma.scale(colors).mode(attrs['space'].value).colors(options.colors);
}
},
{
name: ['cubehelix'],
hasStart: false,
bg: '#212121',
attr: [
{
name: 'start',
min: 0,
max: 360,
step: 1,
value: 0,
type: 'range',
},
{
name: 'rotations',
min: -2,
max: 2,
step: 0.01,
value: -0.65,
type: 'range',
},
{
name: 'hue',
min: 0,
max: 1,
step: 0.01,
value: 1,
type: 'range',
},
{
name: 'gamma',
min: 0,
max: 1,
step: 0.01,
value: 1,
type: 'range',
},
{
name: 'lightness min',
min: 0,
max: 0.9,
step: 0.01,
value: 0.2,
type: 'range',
},
{
name: 'lightness max',
min: 0.1,
max: 1,
step: 0.01,
value: 0.8,
type: 'range',
},
{
name: 'padding',
min: -.49,
max: .49,
step: 0.001,
value: 0,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = chroma.cubehelix()
.start(parseFloat(attrs['start'].value))
.rotations(parseFloat(attrs['rotations'].value))
.hue(
parseFloat(attrs['hue'].value)
)
.gamma(
parseFloat(attrs['gamma'].value)
)
.lightness([
parseFloat(attrs['lightness min'].value),
parseFloat(attrs['lightness max'].value)
])
.scale()
let padding = parseFloat(attrs.padding.value);
if (padding !== 0) {
colors.padding(padding);
}
return colors.colors(options.colors);
}
},
{
name: ['RGB'],
hasStart: true,
bg: '#212121',
attr: [
{
name: 'gradient space',
value: 'red',
values: ['red', 'green', 'blue'],
type: 'list',
excludeValue: true,
},
{
name: 'red',
min: 0,
max: 255,
step: 1,
value: 20,
type: 'range',
},
{
name: 'green',
min: 0,
max: 255,
step: 1,
value: 88,
type: 'range',
},
{
name: 'blue',
min: 0,
max: 255,
step: 1,
value: 100,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['red', 'green', 'blue'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let componentPercent = Math.ceil((i / (options.colors - 1)) * 255);
let color = chroma.rgb([
iterate === space[0] ? componentPercent : parseFloat(attrs[space[0]].value),
iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value),
iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value),
]).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['RYB'],
hasStart: true,
bg: '#212121',
attr: [
{
name: 'gradient space',
value: 'yellow',
values: ['red', 'yellow', 'blue'],
type: 'list',
excludeValue: true,
},
{
name: 'red',
min: 0,
max: 255,
step: 1,
value: 255,
type: 'range',
},
{
name: 'yellow',
min: 0,
max: 255,
step: 1,
value: 88,
type: 'range',
},
{
name: 'blue',
min: 0,
max: 255,
step: 1,
value: 125,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['red', 'yellow', 'blue'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let componentPercent = Math.ceil((i / (options.colors - 1)) * 255);
let rgb = ryb2rgb([
iterate === space[0] ? componentPercent : parseFloat(attrs[space[0]].value),
iterate === space[1] ? componentPercent : parseFloat(attrs[space[1]].value),
iterate === space[2] ? componentPercent : parseFloat(attrs[space[2]].value)
]);
let color = chroma.rgb(rgb).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['YPbPr'],
hasStart: true,
bg: '#212121',
attr: [
{
name: 'gradient space',
value: 'Y',
values: ['Y', 'Pb', 'Pr'],
type: 'list',
excludeValue: true,
},
{
name: 'Y',
min: 0,
max: 1,
step: 0.0001,
value: 0,
type: 'range',
},
{
name: 'Pb',
min: -0.5,
max: 0.5,
step: 0.0001,
value: -0.5,
type: 'range',
},
{
name: 'Pr',
min: -0.5,
max: 0.5,
step: 0.0001,
value: 0.5,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['Y', 'Pb', 'Pr'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let componentPercent = i / (options.colors - 1);
let color = chroma(
ypbprToRGB(
iterate === space[0] ? componentPercent : parseFloat(attrs[space[0]].value),
iterate === space[1] ? -0.5 + componentPercent : parseFloat(attrs[space[1]].value),
iterate === space[2] ? -0.5 + componentPercent : parseFloat(attrs[space[2]].value),
), 'rgb'
).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['lch'/*, 'hcl'*/],
hasStart: true,
bg: '#fffcec',
attr: [
{
name: 'gradient space',
value: 'h',
values: ['l', 'c', 'h'],
type: 'list',
excludeValue: true,
},
{
name: 'h',
min: 0,
max: 360,
step: 1,
value: 20,
type: 'range',
},
{
name: 'l',
min: 0,
max: 100,
step: 1,
value: 75,
type: 'range',
},
{
name: 'c',
min: 0,
max: 140,
step: 1,
value: 100,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['l', 'c', 'h'];
let iterate = attrs['gradient space'].value;
//color = chroma.hcl(h, s, l).get('hex');
for (let i = 0; i < options.colors; i++) {
let color = chroma[mode]([
iterate === space[0] ? Math.ceil(((i / options.colors)) * 100) : parseFloat(attrs[space[0]].value),
iterate === space[1] ? Math.ceil(((i / options.colors)) * 140) : parseFloat(attrs[space[1]].value),
iterate === space[2] ? Math.ceil(((i / options.colors)) * 360) : parseFloat(attrs[space[2]].value)
]).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['lab'],
hasStart: true,
bg: '#f5f5f5',
attr: [
{
name: 'lightness',
min: 0,
max: 100,
step: 1,
value: 74,
type: 'range',
},
{
name: 'green–red',
min: -100,
max: 100,
step: 1,
value: 67,
type: 'range',
},
{
name: 'blue–yellow',
min: -100,
max: 100,
step: 1,
value: 8,
type: 'range',
},
{
name: 'gradient space',
value: 'blue–yellow',
values: ['lightness', 'green–red', 'blue–yellow'],
type: 'list',
excludeValue: true,
}
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let colors = [];
let space = ['lightness', 'green–red', 'blue–yellow'];
let iterate = attrs['gradient space'].value;
for (let i = 0; i < options.colors; i++) {
let color = chroma.lch([
iterate === space[0] ? Math.ceil(((i / options.colors)) * 100) : parseFloat(attrs[space[0]].value),
iterate === space[1] ? -100 + Math.ceil(((i / options.colors)) * 200) : parseFloat(attrs[space[1]].value),
iterate === space[2] ? -100 + Math.ceil(((i / options.colors)) * 200) : parseFloat(attrs[space[2]].value)
]).get('hex');
colors.push(color);
}
return colors;
}
},
{
name: ['interpolate'],
hasStart: false,
bg: '#212121',
attr: [
{
name: 'stops',
type: 'colorlist',
value: [
'#72ffd7',
'#f03b50',
'#0f0b50'
]
},
{
name: 'space',
value: 'lab',
values: ['lab', 'hsl', 'hsv', 'hsi', 'lch', 'rgb', 'lrgb', 'num'],
type: 'list',
},
{
name: 'padding',
min: -.49,
max: .49,
step: 0.001,
value: 0,
type: 'range',
},
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let scale = chroma.scale(attrs['stops'].value).mode(attrs['space'].value);
let padding = parseFloat(attrs.padding.value);
if (padding !== 0) {
scale.padding(padding);
}
let colors = scale.colors(options.colors);
/*colors = colors.map(c => {
return chroma(c).set('hsl.h', '+20').hex();
})*/
return colors;
}
},
{
name: ['matplotlib'],
hasStart: false,
bg: '#fdfdfd',
attr: [
{
name: 'scheme',
value: 'plasma',
values: ['plasma' ,'inferno', 'magma', 'viridis'],
type: 'list',
},
{
name: 'padding',
min: -.49,
max: .49,
step: 0.001,
value: 0,
type: 'range',
}
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
const schemes = {
'inferno': ["#000004","#010005","#010106","#010108","#02010a","#02020c","#02020e","#030210","#040312","#040314","#050417","#060419","#07051b","#08051d","#09061f","#0a0722","#0b0724","#0c0826","#0d0829","#0e092b","#10092d","#110a30","#120a32","#140b34","#150b37","#160b39","#180c3c","#190c3e","#1b0c41","#1c0c43","#1e0c45","#1f0c48","#210c4a","#230c4c","#240c4f","#260c51","#280b53","#290b55","#2b0b57","#2d0b59","#2f0a5b","#310a5c","#320a5e","#340a5f","#360961","#380962","#390963","#3b0964","#3d0965","#3e0966","#400a67","#420a68","#440a68","#450a69","#470b6a","#490b6a","#4a0c6b","#4c0c6b","#4d0d6c","#4f0d6c","#510e6c","#520e6d","#540f6d","#550f6d","#57106e","#59106e","#5a116e","#5c126e","#5d126e","#5f136e","#61136e","#62146e","#64156e","#65156e","#67166e","#69166e","#6a176e","#6c186e","#6d186e","#6f196e","#71196e","#721a6e","#741a6e","#751b6e","#771c6d","#781c6d","#7a1d6d","#7c1d6d","#7d1e6d","#7f1e6c","#801f6c","#82206c","#84206b","#85216b","#87216b","#88226a","#8a226a","#8c2369","#8d2369","#8f2469","#902568","#922568","#932667","#952667","#972766","#982766","#9a2865","#9b2964","#9d2964","#9f2a63","#a02a63","#a22b62","#a32c61","#a52c60","#a62d60","#a82e5f","#a92e5e","#ab2f5e","#ad305d","#ae305c","#b0315b","#b1325a","#b3325a","#b43359","#b63458","#b73557","#b93556","#ba3655","#bc3754","#bd3853","#bf3952","#c03a51","#c13a50","#c33b4f","#c43c4e","#c63d4d","#c73e4c","#c83f4b","#ca404a","#cb4149","#cc4248","#ce4347","#cf4446","#d04545","#d24644","#d34743","#d44842","#d54a41","#d74b3f","#d84c3e","#d94d3d","#da4e3c","#db503b","#dd513a","#de5238","#df5337","#e05536","#e15635","#e25734","#e35933","#e45a31","#e55c30","#e65d2f","#e75e2e","#e8602d","#e9612b","#ea632a","#eb6429","#eb6628","#ec6726","#ed6925","#ee6a24","#ef6c23","#ef6e21","#f06f20","#f1711f","#f1731d","#f2741c","#f3761b","#f37819","#f47918","#f57b17","#f57d15","#f67e14","#f68013","#f78212","#f78410","#f8850f","#f8870e","#f8890c","#f98b0b","#f98c0a","#f98e09","#fa9008","#fa9207","#fa9407","#fb9606","#fb9706","#fb9906","#fb9b06","#fb9d07","#fc9f07","#fca108","#fca309","#fca50a","#fca60c","#fca80d","#fcaa0f","#fcac11","#fcae12","#fcb014","#fcb216","#fcb418","#fbb61a","#fbb81d","#fbba1f","#fbbc21","#fbbe23","#fac026","#fac228","#fac42a","#fac62d","#f9c72f","#f9c932","#f9cb35","#f8cd37","#f8cf3a","#f7d13d","#f7d340","#f6d543","#f6d746","#f5d949","#f5db4c","#f4dd4f","#f4df53","#f4e156","#f3e35a","#f3e55d","#f2e661","#f2e865","#f2ea69","#f1ec6d","#f1ed71","#f1ef75","#f1f179","#f2f27d","#f2f482","#f3f586","#f3f68a","#f4f88e","#f5f992","#f6fa96","#f8fb9a","#f9fc9d","#fafda1","#fcffa4"],
'magma': ["#000004","#010005","#010106","#010108","#020109","#02020b","#02020d","#03030f","#030312","#040414","#050416","#060518","#06051a","#07061c","#08071e","#090720","#0a0822","#0b0924","#0c0926","#0d0a29","#0e0b2b","#100b2d","#110c2f","#120d31","#130d34","#140e36","#150e38","#160f3b","#180f3d","#19103f","#1a1042","#1c1044","#1d1147","#1e1149","#20114b","#21114e","#221150","#241253","#251255","#271258","#29115a","#2a115c","#2c115f","#2d1161","#2f1163","#311165","#331067","#341069","#36106b","#38106c","#390f6e","#3b0f70","#3d0f71","#3f0f72","#400f74","#420f75","#440f76","#451077","#471078","#491078","#4a1079","#4c117a","#4e117b","#4f127b","#51127c","#52137c","#54137d","#56147d","#57157e","#59157e","#5a167e","#5c167f","#5d177f","#5f187f","#601880","#621980","#641a80","#651a80","#671b80","#681c81","#6a1c81","#6b1d81","#6d1d81","#6e1e81","#701f81","#721f81","#732081","#752181","#762181","#782281","#792282","#7b2382","#7c2382","#7e2482","#802582","#812581","#832681","#842681","#862781","#882781","#892881","#8b2981","#8c2981","#8e2a81","#902a81","#912b81","#932b80","#942c80","#962c80","#982d80","#992d80","#9b2e7f","#9c2e7f","#9e2f7f","#a02f7f","#a1307e","#a3307e","#a5317e","#a6317d","#a8327d","#aa337d","#ab337c","#ad347c","#ae347b","#b0357b","#b2357b","#b3367a","#b5367a","#b73779","#b83779","#ba3878","#bc3978","#bd3977","#bf3a77","#c03a76","#c23b75","#c43c75","#c53c74","#c73d73","#c83e73","#ca3e72","#cc3f71","#cd4071","#cf4070","#d0416f","#d2426f","#d3436e","#d5446d","#d6456c","#d8456c","#d9466b","#db476a","#dc4869","#de4968","#df4a68","#e04c67","#e24d66","#e34e65","#e44f64","#e55064","#e75263","#e85362","#e95462","#ea5661","#eb5760","#ec5860","#ed5a5f","#ee5b5e","#ef5d5e","#f05f5e","#f1605d","#f2625d","#f2645c","#f3655c","#f4675c","#f4695c","#f56b5c","#f66c5c","#f66e5c","#f7705c","#f7725c","#f8745c","#f8765c","#f9785d","#f9795d","#f97b5d","#fa7d5e","#fa7f5e","#fa815f","#fb835f","#fb8560","#fb8761","#fc8961","#fc8a62","#fc8c63","#fc8e64","#fc9065","#fd9266","#fd9467","#fd9668","#fd9869","#fd9a6a","#fd9b6b","#fe9d6c","#fe9f6d","#fea16e","#fea36f","#fea571","#fea772","#fea973","#feaa74","#feac76","#feae77","#feb078","#feb27a","#feb47b","#feb67c","#feb77e","#feb97f","#febb81","#febd82","#febf84","#fec185","#fec287","#fec488","#fec68a","#fec88c","#feca8d","#fecc8f","#fecd90","#fecf92","#fed194","#fed395","#fed597","#fed799","#fed89a","#fdda9c","#fddc9e","#fddea0","#fde0a1","#fde2a3","#fde3a5","#fde5a7","#fde7a9","#fde9aa","#fdebac","#fcecae","#fceeb0","#fcf0b2","#fcf2b4","#fcf4b6","#fcf6b8","#fcf7b9","#fcf9bb","#fcfbbd","#fcfdbf"],
'plasma': ["#0d0887","#100788","#130789","#16078a","#19068c","#1b068d","#1d068e","#20068f","#220690","#240691","#260591","#280592","#2a0593","#2c0594","#2e0595","#2f0596","#310597","#330597","#350498","#370499","#38049a","#3a049a","#3c049b","#3e049c","#3f049c","#41049d","#43039e","#44039e","#46039f","#48039f","#4903a0","#4b03a1","#4c02a1","#4e02a2","#5002a2","#5102a3","#5302a3","#5502a4","#5601a4","#5801a4","#5901a5","#5b01a5","#5c01a6","#5e01a6","#6001a6","#6100a7","#6300a7","#6400a7","#6600a7","#6700a8","#6900a8","#6a00a8","#6c00a8","#6e00a8","#6f00a8","#7100a8","#7201a8","#7401a8","#7501a8","#7701a8","#7801a8","#7a02a8","#7b02a8","#7d03a8","#7e03a8","#8004a8","#8104a7","#8305a7","#8405a7","#8606a6","#8707a6","#8808a6","#8a09a5","#8b0aa5","#8d0ba5","#8e0ca4","#8f0da4","#910ea3","#920fa3","#9410a2","#9511a1","#9613a1","#9814a0","#99159f","#9a169f","#9c179e","#9d189d","#9e199d","#a01a9c","#a11b9b","#a21d9a","#a31e9a","#a51f99","#a62098","#a72197","#a82296","#aa2395","#ab2494","#ac2694","#ad2793","#ae2892","#b02991","#b12a90","#b22b8f","#b32c8e","#b42e8d","#b52f8c","#b6308b","#b7318a","#b83289","#ba3388","#bb3488","#bc3587","#bd3786","#be3885","#bf3984","#c03a83","#c13b82","#c23c81","#c33d80","#c43e7f","#c5407e","#c6417d","#c7427c","#c8437b","#c9447a","#ca457a","#cb4679","#cc4778","#cc4977","#cd4a76","#ce4b75","#cf4c74","#d04d73","#d14e72","#d24f71","#d35171","#d45270","#d5536f","#d5546e","#d6556d","#d7566c","#d8576b","#d9586a","#da5a6a","#da5b69","#db5c68","#dc5d67","#dd5e66","#de5f65","#de6164","#df6263","#e06363","#e16462","#e26561","#e26660","#e3685f","#e4695e","#e56a5d","#e56b5d","#e66c5c","#e76e5b","#e76f5a","#e87059","#e97158","#e97257","#ea7457","#eb7556","#eb7655","#ec7754","#ed7953","#ed7a52","#ee7b51","#ef7c51","#ef7e50","#f07f4f","#f0804e","#f1814d","#f1834c","#f2844b","#f3854b","#f3874a","#f48849","#f48948","#f58b47","#f58c46","#f68d45","#f68f44","#f79044","#f79143","#f79342","#f89441","#f89540","#f9973f","#f9983e","#f99a3e","#fa9b3d","#fa9c3c","#fa9e3b","#fb9f3a","#fba139","#fba238","#fca338","#fca537","#fca636","#fca835","#fca934","#fdab33","#fdac33","#fdae32","#fdaf31","#fdb130","#fdb22f","#fdb42f","#fdb52e","#feb72d","#feb82c","#feba2c","#febb2b","#febd2a","#febe2a","#fec029","#fdc229","#fdc328","#fdc527","#fdc627","#fdc827","#fdca26","#fdcb26","#fccd25","#fcce25","#fcd025","#fcd225","#fbd324","#fbd524","#fbd724","#fad824","#fada24","#f9dc24","#f9dd25","#f8df25","#f8e125","#f7e225","#f7e425","#f6e626","#f6e826","#f5e926","#f5eb27","#f4ed27","#f3ee27","#f3f027","#f2f227","#f1f426","#f1f525","#f0f724","#f0f921"],
'viridis': ["#440154","#440256","#450457","#450559","#46075a","#46085c","#460a5d","#460b5e","#470d60","#470e61","#471063","#471164","#471365","#481467","#481668","#481769","#48186a","#481a6c","#481b6d","#481c6e","#481d6f","#481f70","#482071","#482173","#482374","#482475","#482576","#482677","#482878","#482979","#472a7a","#472c7a","#472d7b","#472e7c","#472f7d","#46307e","#46327e","#46337f","#463480","#453581","#453781","#453882","#443983","#443a83","#443b84","#433d84","#433e85","#423f85","#424086","#424186","#414287","#414487","#404588","#404688","#3f4788","#3f4889","#3e4989","#3e4a89","#3e4c8a","#3d4d8a","#3d4e8a","#3c4f8a","#3c508b","#3b518b","#3b528b","#3a538b","#3a548c","#39558c","#39568c","#38588c","#38598c","#375a8c","#375b8d","#365c8d","#365d8d","#355e8d","#355f8d","#34608d","#34618d","#33628d","#33638d","#32648e","#32658e","#31668e","#31678e","#31688e","#30698e","#306a8e","#2f6b8e","#2f6c8e","#2e6d8e","#2e6e8e","#2e6f8e","#2d708e","#2d718e","#2c718e","#2c728e","#2c738e","#2b748e","#2b758e","#2a768e","#2a778e","#2a788e","#29798e","#297a8e","#297b8e","#287c8e","#287d8e","#277e8e","#277f8e","#27808e","#26818e","#26828e","#26828e","#25838e","#25848e","#25858e","#24868e","#24878e","#23888e","#23898e","#238a8d","#228b8d","#228c8d","#228d8d","#218e8d","#218f8d","#21908d","#21918c","#20928c","#20928c","#20938c","#1f948c","#1f958b","#1f968b","#1f978b","#1f988b","#1f998a","#1f9a8a","#1e9b8a","#1e9c89","#1e9d89","#1f9e89","#1f9f88","#1fa088","#1fa188","#1fa187","#1fa287","#20a386","#20a486","#21a585","#21a685","#22a785","#22a884","#23a983","#24aa83","#25ab82","#25ac82","#26ad81","#27ad81","#28ae80","#29af7f","#2ab07f","#2cb17e","#2db27d","#2eb37c","#2fb47c","#31b57b","#32b67a","#34b679","#35b779","#37b878","#38b977","#3aba76","#3bbb75","#3dbc74","#3fbc73","#40bd72","#42be71","#44bf70","#46c06f","#48c16e","#4ac16d","#4cc26c","#4ec36b","#50c46a","#52c569","#54c568","#56c667","#58c765","#5ac864","#5cc863","#5ec962","#60ca60","#63cb5f","#65cb5e","#67cc5c","#69cd5b","#6ccd5a","#6ece58","#70cf57","#73d056","#75d054","#77d153","#7ad151","#7cd250","#7fd34e","#81d34d","#84d44b","#86d549","#89d548","#8bd646","#8ed645","#90d743","#93d741","#95d840","#98d83e","#9bd93c","#9dd93b","#a0da39","#a2da37","#a5db36","#a8db34","#aadc32","#addc30","#b0dd2f","#b2dd2d","#b5de2b","#b8de29","#bade28","#bddf26","#c0df25","#c2df23","#c5e021","#c8e020","#cae11f","#cde11d","#d0e11c","#d2e21b","#d5e21a","#d8e219","#dae319","#dde318","#dfe318","#e2e418","#e5e419","#e7e419","#eae51a","#ece51b","#efe51c","#f1e51d","#f4e61e","#f6e620","#f8e621","#fbe723","#fde725"],
};
let scale = chroma.scale(schemes[attrs['scheme'].value]);
let padding = parseFloat(attrs.padding.value);
if (padding !== 0) {
scale.padding(padding);
}
return scale.colors(options.colors);
}
},
{
name: ['brewer'],
hasStart: false,
bg: '#212121',
attr: [
{
name: 'scheme',
value: 'Spectral',
values: ['Spectral', 'BrBG', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'BuGn', 'BuPu', 'GnBu', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'RdPu', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd'],
type: 'list',
},
{
name: 'padding',
min: -.49,
max: .49,
step: 0.001,
value: 0,
type: 'range',
}
],
palette: (mode, attrs, options = {correctLightness: false, colors: 32}) => {
let scale = chroma.scale(attrs['scheme'].value);
let padding = parseFloat(attrs.padding.value);
if (padding !== 0) {
scale.padding(padding);
}
return scale.colors(options.colors);
}
}
];
const colorSpaceNames = colorSpaces.reduce((names, space) => space.name.concat(names), []);
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
currentColors: [],
currentNames: [],
selectedColorIndex: -1,
isLoading: false,
},
mutations: {
toggleLoadingStatus (state) {
state.isLoading != state.isLoading;
},
updateColors (state, newColors) {
state.currentColors = newColors;
},
updateSelectedColor (state, newIndex) {
state.selectedColorIndex = newIndex;
},
updateNames (state, colorNames) {
state.currentNames = colorNames;
},
},
actions: {
fetchNames (context) {
fetch('https://api.color.pizza/v1/' + colors.join(',').replace(/#/g, '') + '?noduplicates=true&goodnamesonly=true')
.then(data => data.json()).then(data => {
context.commit('updateNames', context.colors);
});
}
},
getters: {
}
})
const palette = new Vue({
el: '#palette',
store,
data: function () {
return {
version: '0.6β',
spaces: [...colorSpaces],
names: [...colorSpaceNames],
currentSpace: 'cubehelix',
colorCount: 18,
maxColorCount: 32,
minColorCount: 2,
correctLightness: false,
views: ['wheel', 'row', '3d'],
view: 'wheel',
colorNames: this.$store.state.currentColors,
bgcolor: '#212121',
settingsVisible: false,
}
},
computed: {
bgcolorcontrast: function () {
return chroma.contrast(this.bgcolor, '#ffffff') > chroma.contrast(this.bgcolor, '#212121') ? '#ffffff' : '#212121';
},
space: function () {
return this.spaces.filter(space => space.name.includes(this.currentSpace))[0];
},
currentColors: function() {
const colors = this.colorsBySpace(this.currentSpace);
// create a method for the flter (cache)
this.bgcolor = this.spaces.filter(space => space.name.includes(this.currentSpace))[0].bg;
this.getNames(colors);
return colors;
},
},
methods: {
onScroll: function() {
const scrollTop = window.scrollY;
if (this.settingsVisible) {
this.scrollTimer = setTimeout(() => {
if (Math.abs(scrollTop - window.scrollY) > window.innerHeight * .2) {
this.settingsVisible = false;
}
}, 200);
}
},
setColorByIndex: function(index) {
//console.log(index)
this.bgcolor = this.currentColors[Math.min(index, this.colorCount)]
},
colorsBySpace: function(spaceName) {
const space = this.spaces.filter(space => space.name.includes(spaceName))[0]
const attrs = {};
space.attr.forEach(arg => {
attrs[arg.name] = arg;
});
let colors = space.palette(
spaceName,
attrs,
{
correctLightness: false,
colors: this.colorCount,
}
);
return colors;
},
toggleSettings: function() {
this.settingsVisible = !this.settingsVisible;
},
selectCurrentSpace: function(e) {
const val = typeof(e) == "string" ? e : e.target.value;
if (this.names.includes(val)) {
this.currentSpace = val;
} else {
console.error(`no color space called ${val} choose from: ${this.names.join(', ')}`);
}
},
getNames: function(colors) {
fetch('https://api.color.pizza/v1/' + colors.join(',').replace(/#/g, '') + '?noduplicates=true&goodnamesonly=true').then(data => data.json()).then(data => {
this.colorNames = data.colors;
this.$store.commit('updateNames', data.colors);
});
},
getColorName: function(index) {
if( this.colorNames.length && this.colorNames[index]) {
return this.colorNames[index].name;
}
}
},
mounted () {
// const io = new IntersectionObserver(sentinelListener);
//io.observe(this.$el);
// @hook:mounted="childMounted"
console.log('mounted', this)
window.addEventListener('scroll', this.onScroll);
}
});
Vue.component(
'setting',
{
template: `<component :is="multifield ? 'div' : 'label'" class="setting">
<h3 class="setting__label">{{label}}</h3>
<span v-if="value" class="setting__value">{{value}}</span>
<slot />
</component>`,
props: {
label: String,
value: Number,
multifield: Boolean
}
}
);
Vue.component(
'colorlistinput',
{
template: `<div class="setting__colors">
<div v-for="(color, i) in colors" class="setting__color">
<input type="color" v-bind:value="color" v-on:input="(event) => { updateColors(event, i) }"/>
<button v-if="colors.length> 2" v-on:click="removeColor(i)">×</button>
<strong>{{color}}</strong>
</div>
<button v-on:click="addColor">Add Color</button>
</div>`,
props: {
colors: Array,
},
methods: {
updateColors: function (e, i) {
this.colors[i] = e.target.value;
this.$set(this, 'colors', this.colors);
this.$forceUpdate();
this.$emit('change', this.colors);
},
addColor: function () {
this.colors.push(chroma.random().hex());
},
removeColor: function (i) {
this.colors.splice(i, 1);
}
}
}
);
Vue.component(
'hhead',
{
template: `<header class="palette--header">
<h1 v-bind:style="txtshadow">{{colorname}}</h1>
<h2>{{color}}</h2>
</header>`,
props: {
title: String,
color: String,
colors: Array,
},
data: function () {
return {
colorname: 'color.pizza',
}
},
computed: {
txtshadow: function () {
let textShadow = '';
this.colors.forEach((col, i) => {
textShadow += (i ? ',' : '') + `${i * 2}px ${i * 2}px 0 ${col} `;
})
return `--text-shadow: ${textShadow}`;
}
},
watch: {
color: function() {
fetch('https://api.color.pizza/v1/' + this.color.replace('#', '')).then(data => data.json()).then(data => {
this.colorname = data.colors[0].name;
});
}
}
},
);
Vue.component(
'colorwheel',
{
template: `
<div class="view view--wheel">
<div class="fan" v-bind:style="{transform: 'rotate(' + this.rotation + 'deg)'}">
<article v-for="(color, index) in colors" class="blade" v-bind:style="{'background-color': color, transform: 'translateZ(' + (index) * 1.5 + 'px) rotate(' + (index / colors.length) * collabsed * 360 +'deg)'}" v-on:click="$emit('setcolor', index); setRotation(index)">
<h2 class="blade__value"><strong>{{color}}</strong></h2>
<h3 class="blade__label"><span class="blade__label--inner">{{names[index].name}}</span></h3>
</article>
</div>
</div>
`,
props: {
colors: Array,
names: Array,
},
data: function () {
return {
selectedIndex: 0,
rotation: 0,
fullturns: 0,
collabsed: 1,
}
},
methods: {
setRotation: function (index) {
this.rotation = (index / this.colors.length * -360);
this.selectedIndex = index;
}
}
}
);
Vue.component(
'colorgradient',
{
template: `
<div class="view view--gradient" v-bind:style="{'background-image': 'linear-gradient(45deg, ' + colors.join(',') + ')'}"></div>
`,
props: {
colors: Array,
names: Array
}
}
);
Vue.component(
'colorball',
{
template: `
<div class="view view--radialgradient" v-on:click="staggered = !staggered">
<div class="radialgradient" v-bind:style="getGradient()"></div>
</div>
`,
data: function () {
return {
staggered: true,
}
},
props: {
colors: Array,
names: Array
},
methods: {
getGradient: function () {
const cc = this.colors.length;
let grad = '';
if (this.staggered) {
grad = this.colors.reduce((str, color, i) => {
if ( i === 1 ) {
str = `${str} 0%, ${str} 0 ${(i/cc) * 100}%`
}
return str + `, ${color} 0 ${(i/cc) * 100}%`
});
} else {
grad = this.colors.join(',');
}
return `background-image: radial-gradient(circle at center, ${grad})`;
}
}
}
);
Vue.component(
'colorlist',
{
template: `
<div class="view view--list" v-bind:style="{'background-image': 'linear-gradient(' + colors.join(',') + ')'}">
<ol class="color-list view--list__list">
<li class="color-list__item" v-for="(color, index) in colors" v-bind:style="{'background-color': color, 'color': color}">
<span class="color-list__value">{{color}}</span>
<span class="color-list__name">{{names[index].name}}</span>
</li>
</ol>
</div>
`,
props: {
colors: Array,
names: Array
}
}
);
function translate(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * ((value - low1) / (high1 - low1));
}
Vue.component(
'cube',
{
template: `
<div class="view view--cube" ref="container">
<div class="select-wrap">
<select v-model="cMode">
<option v-for="(obj, colorMode) in colorModes" v-bind:value="colorMode">{{colorMode}}</option>
<select />
</div>
</div>
`,
props: {
bgcolorcontrast: String,
colors: Array,
names: Array,
},
data: function () {
return {
cubeSize: 100,
dotSize: 5,
cDark: '#212121',
cLight: '#ffffff',
cMode: 'rgb',
objects: [],
colorModes: {
hsv: {
func: 'hsv',
x: [0, 360],
y: [1, 1],
z: [2, 1]
},
hsi: {
func: 'hsi',
x: [0, 360],
y: [1, 1],
z: [2, 1]
},
hsl: {
func: 'hsl',
x: [0, 360],
y: [1, 1],
z: [2, 1]
},
rgb: {
func: 'rgb',
x: [0, 255],
y: [1, 255],
z: [2, 255]
},
lab: {
func: 'lab',
z: [0, 100],
y: [1, 127, -128],
x: [2, 127, -128]
},
lch: {
func: 'lch',
z: [0, 100],
y: [1, 140],
x: [2, 0, 360]
},
}
}
},
methods: {
onWindowResize: function () {
this.width = window.innerWidth + 1;
this.height = window.innerHeight + 1;
this.cam.aspect = this.width / this.height;
this.cam.updateProjectionMatrix();
this.renderer.setSize( this.width, this.height );
},
render() {
this.controls.update();
this.renderer.render( this.scene, this.cam );
},
cube: function(size) {
const h = size * 0.5;
const geometry = new THREE.Geometry();
geometry.vertices.push(
new THREE.Vector3( -h, -h, -h ),
new THREE.Vector3( -h, h, -h ),
new THREE.Vector3( -h, h, -h ),
new THREE.Vector3( h, h, -h ),
new THREE.Vector3( h, h, -h ),
new THREE.Vector3( h, -h, -h ),
new THREE.Vector3( h, -h, -h ),
new THREE.Vector3( -h, -h, -h ),
new THREE.Vector3( -h, -h, h ),
new THREE.Vector3( -h, h, h ),
new THREE.Vector3( -h, h, h ),
new THREE.Vector3( h, h, h ),
new THREE.Vector3( h, h, h ),
new THREE.Vector3( h, -h, h ),
new THREE.Vector3( h, -h, h ),
new THREE.Vector3( -h, -h, h ),
new THREE.Vector3( -h, -h, -h ),
new THREE.Vector3( -h, -h, h ),
new THREE.Vector3( -h, h, -h ),
new THREE.Vector3( -h, h, h ),
new THREE.Vector3( h, h, -h ),
new THREE.Vector3( h, h, h ),
new THREE.Vector3( h, -h, -h ),
new THREE.Vector3( h, -h, h )
);
return geometry;
},
addCube: function (color) {
let geometryCube = this.cube( this.cubeSize );
//THREE.Line.computeLineDistances;
//geometryCube
const colorspace = new THREE.LineSegments( geometryCube,
new THREE.LineDashedMaterial(
{
color: this.bgcolorcontrast || 0xffffff,
dashSize: 1,
gapSize: 1,
linewidth: 1,
//transparent: true,
blending: THREE.AdditiveBlending
}
)
);
colorspace.name = 'colorspace';
this.objects.push( colorspace );
this.scene.add( colorspace );
this.spaceCube = colorspace;
},
addParticles: function(){
// create the particle variables
const particleCount = this.colors.length;
const particles = new THREE.Geometry();
const pMaterial = new THREE.PointsMaterial({
vertexColors: THREE.VertexColors,
//size: this.dotSize,
/*
map: createCanvasMaterial('#fff'),
blending: THREE.AdditiveBlending,
transparent: true,
depthWrite: true,
alphaTest: .5
*/
});
let colors = [];
const mode = this.colorModes[this.cMode];
this.colors.forEach(col => {
let colorComp;
if (mode.func === 'yuv') {
colorComp = this.yuv(chroma(col).rgb());
} else {
colorComp = chroma(col)[mode.func]();
}
let pX = translate(colorComp[mode.x[0]], mode.x[2] || 0, mode.x[1], -this.cubeSize * .5, this.cubeSize * .5),
pZ = translate(colorComp[mode.z[0]], mode.z[2] || 0, mode.z[1], -this.cubeSize * .5, this.cubeSize * .5),
pY = translate(colorComp[mode.y[0]], mode.y[2] || 0, mode.y[1], -this.cubeSize * .5, this.cubeSize * .5);
if (mode.func === 'hsl' || mode.func === 'hsv' || mode.func === 'hsi') {
let theta = Math.PI * colorComp[mode.x[0]] / 180;
let r = colorComp[mode.y[0]] * (this.cubeSize * .5);
pY = r * Math.cos(theta);
pX = r * Math.sin(theta);
}
if (mode.func === 'lch') {
let theta = Math.PI * colorComp[mode.x[0]] / 180;
let r = translate(colorComp[mode.y[0]], 0, mode.y[1], 0, this.cubeSize * .5);
pY = r * Math.cos(theta);
pX = r * Math.sin(theta);
}
const particle = new THREE.Vector3(pX, pY, pZ),
Tcolor = new THREE.Color(col);
//colors.push(Tcolor)
// add it to the geometry
//particles.vertices.push(particle);
const sphere = new THREE.Mesh(
new THREE.IcosahedronGeometry(this.dotSize * .4, 2),
new THREE.MeshStandardMaterial({
flatShading: true,
//roughness: 0.1,
//metalness: 0.7
})
);
this.scene.add(sphere);
this.objects.push(sphere);
sphere.position.set(pX, pY, pZ);
sphere.material.emissive = Tcolor;
});
// create the particle system
/*const particleSystem = new THREE.Points(
particles,
pMaterial
);
particleSystem.name = 'colors';
particles.colors = colors;*/
// add it to the scene
//this.objects.push(particleSystem);
//this.scene.add(particleSystem);
//this.part = particleSystem;
},
reset: function() {
clearTimeout(this.debouncer);
this.debouncer = setTimeout(() => {
this.objects = [];
while(this.scene.children.length > 0){
this.scene.remove(this.scene.children[0]);
}
this.addCube();
this.addParticles();
this.renderer.render(this.scene, this.cam);
}, 50);
}
},
watch: {
bgcolorcontrast: function() {
this.reset();
},
cMode: function() {
this.reset();
},
colors: function() {
this.reset();
}
},
mounted() {
const width = window.innerWidth;
const height = window.innerHeight;
this.cam = new THREE.PerspectiveCamera( 60, width/height, 1, 400 );
this.cam.position.z = this.cubeSize * 1.75;
this.scene = new THREE.Scene();
this.root = new THREE.Object3D();
this.renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
this.renderer.setPixelRatio( window.devicePixelRatio );
this.renderer.setSize(width, height);
this.addCube();
this.controls = new OrbitControls(this.cam, this.renderer.domElement);
//const light = new THREE.SpotLight(0xffffff, 1, 80, Math.PI * 0.25, 1, 2);
//light.position.set(0, 0, 0);
//this.scene.add(light);
//const light2 = new THREE.PointLight("#EFEFEF", 2, 80, Math.PI * 0.25, 1, 2);
//this.scene.add(light2);
// enable animation loop when using damping or autorotation
this.controls.enableDamping = true;
this.controls.dampingFactor = .75;
this.controls.enableZoom = true;
this.controls.zoomSpeed = .25;
this.controls.autoRotate = true;
this.controls.autoRotateSpeed = .25;
this.controls.maxDistance = this.cubeSize * 1.75;
this.controls.maxPolarAngle = Math.PI * 4;
//this.controls.minPolarAngle = 0;
this.controls.maxAzimuthAngle = Infinity;
this.controls.minAzimuthAngle = -Infinity;
this.controls.noPan = true;
this.controls.noKeys = true;
this.controls.noZoom = true;
const container = this.$refs.container;
container.appendChild( this.renderer.domElement );
window.addEventListener('resize', this.onWindowResize, false);
//document.querySelector('button').addEventListener('click', toggleDarkMode, false);
this.addParticles();
this.renderer.render(this.scene, this.cam);
//this.scene.background = new THREE.Color('#212121');
this.renderer.setClearColor( 0x000000, 0 ); // the default
this.animate = true;
const anim = () => {
this.render();
if (this.animate) {
requestAnimationFrame(anim);
}
}
anim();
},
beforeDestroy() {
this.animate = false;
window.removeEventListener('resize', this.onWindowResize, false);
},
}
);
Vue.component(
'colorabstract',
{
template: `
<div class="view view--abstract" v-bind:style="{'--gradient': gradient}">
<div></div>
</div>
`,
props: {
colors: Array,
names: Array
},
computed: {
gradient: function () {
return this.colors.join(',');
}
}
}
);
Vue.component(
'colorwatches',
{
template: `
<div class="view view--watches">
<ol class="view__watch__list">
<li v-on:click="$emit('setcolor', index)" class="color-watch" v-for="(color, index) in colors" v-bind:style="{'background-color': color, 'color': color}">
<div class="color-watch__wrap">
<div class="color-watch__inner">
<div class="color-watch__swatch">
<div class="color-watch__swatch__shade"></div>
<div class="color-watch__swatch__shade"></div>
<div class="color-watch__swatch__shade"></div>
</div>
<div class="color-watch__label">
<span class="color-watch__title">{{names[index].name}}</span>
<span class="color-watch__subtitle">{{color}}</span>
</div>
</div>
</div>
</li>
</ol>
</div>
`,
props: {
colors: Array,
names: Array
}
}
);
Vue.component(
'colorpie',
{
template: `
<div class="view view--pie" v-on:click="zoomed = !zoomed">
<div class="pie__wrap" ref="svg">
<svg class="pie" v-bind:class="{'pie--zoomed': zoomed}" viewBox="-2 -2 4 4" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="blur" x="0" y="0">
<feGaussianBlur in="SourceGraphic" stdDeviation=".03" />
</filter>
</defs>
<path v-for="(color, index) in colors" v-bind:d="getPath(index, colors.length)" v-bind:fill="color" />
</svg>
</div>
</div>
`,
data: function () {
return {
zoomed: true
}
},
props: {
colors: Array,
names: Array
},
mounted () {
document.addEventListener('mousemove', (event) => {
let angle = Math.atan2(event.clientY - window.innerHeight * .5, event.clientX - window.innerWidth * .5 );
angle = angle * (180/Math.PI);
this.$refs.svg.style.transform = `rotate(${angle}deg)`;
})
},
methods: {
getCoordinatesForPercent: (percent) => {
const x = Math.cos(2 * Math.PI * percent);
const y = Math.sin(2 * Math.PI * percent);
return [x, y];
},
getPath: function (index, total) {
const [startX, startY] = this.getCoordinatesForPercent((index / total) - 0.001);
const [endX, endY] = this.getCoordinatesForPercent(((index + 1) / total) + 0.001);
const largeArcFlag = total < 3 ? 1 : 0;
const pathData = [
`M ${startX} ${startY}`, // Move
`A 1 1 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc
`L 0 0`, // Line
].join(' ');
return pathData;
}
}
}
);
Vue.component(
'colorrows',
{
template: `
<div class="view view--rows">
<ol class="view--rows__list">
<li class="color__row" v-for="(color, index) in colors" v-bind:style="{'background-color': color, 'color': color}">
<span class="color__row__label">
<div class="color__row__title">{{color}}</div>
<div class="color__row__subtitle">{{names[index].name}}</div>
</span>
</li>
</ol>
</div>
`,
props: {
colors: Array,
names: Array
}
}
);
Vue.component(
'itten',
{
template: `
<div class="view view--itten" v-bind:class="'itten--' + currentView">
<div class="itten__list">
<div class="itten__item" v-for="(color, index) in colors">
<div class="color__itten" v-bind:style="{'background-color': color, 'color': colors[colors.length - index - 1]}">
</div>
<strong class="itten__name">
{{names[index].name}} <em>↔</em> {{names[colors.length - index - 1].name}}</strong>
</div>
</div>
<div class="ittem-vues">
<button v-on:click="currentView='ball'">●</button>
<button v-on:click="currentView='cube'">■</button>
<button v-on:click="currentView='triangle'">▲</button>
</div>
</div>
`,
data: function () {
return {
currentView: 'ball',
}
},
props: {
colors: Array,
names: Array
}
}
);
Vue.component(
'ellogo',
{
template: `
<svg class="elastiq-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 613 433">
<path class="font" d="M311.54 203.84a9 9 0 0 0-1.91.21 7.74 7.74 0 0 0-1.83.64 4 4 0 0 0-1.4 1.15 2.81 2.81 0 0 0 1.49 4.38 29.19 29.19 0 0 0 7 1.45 17.65 17.65 0 0 1 8.93 3.36 9.22 9.22 0 0 1 3.4 7.7q0 7.49-5.23 11.74t-14.51 4.25a21.41 21.41 0 0 1-8-1.36 17.6 17.6 0 0 1-5.53-3.4 14.1 14.1 0 0 1-3.28-4.51 12.64 12.64 0 0 1-1.19-4.68l10.55-2.55a7.32 7.32 0 0 0 2.38 4.81q2.13 2 6.47 2a13.24 13.24 0 0 0 5.19-.94 3.34 3.34 0 0 0 2.21-3.32 3.24 3.24 0 0 0-1.7-2.85q-1.7-1.06-6.3-1.49a17.19 17.19 0 0 1-9.74-3.49 9.73 9.73 0 0 1-3.62-7.91 13.4 13.4 0 0 1 1.49-6.38 14 14 0 0 1 4-4.68 17.87 17.87 0 0 1 5.74-2.85 23.84 23.84 0 0 1 6.85-1 20.07 20.07 0 0 1 7.4 1.19 15 15 0 0 1 4.85 3 11.8 11.8 0 0 1 2.76 3.87 15.47 15.47 0 0 1 1.15 3.87l-10.45 2.72a5.27 5.27 0 0 0-2.13-3.62 8.32 8.32 0 0 0-5.04-1.31zm39.25 1.68h-11.91v-10.19H353l3.91-18.55h10.72l-3.92 18.55h14.63v10.19h-16.83l-4.8 21.89.85.6 11.4-7.83 5.36 8-12.3 8.34a12 12 0 0 1-6.89 2.21 10.08 10.08 0 0 1-3.57-.64 8.74 8.74 0 0 1-5-4.72 9.22 9.22 0 0 1-.77-3.83 8 8 0 0 1 .08-1.23q.08-.55.25-1.49zm63.58 31a11.67 11.67 0 0 1-3.49 1.7 12.69 12.69 0 0 1-3.49.51 10.08 10.08 0 0 1-3.57-.64 9.25 9.25 0 0 1-3-1.79 8 8 0 0 1-2-2.81 9.16 9.16 0 0 1-.72-3.7 12.25 12.25 0 0 1 .34-3l5.1-21.36-.85-.6-11.4 7.83-5.36-8 12.34-8.34a11.68 11.68 0 0 1 3.49-1.7 12.68 12.68 0 0 1 3.49-.51 10.11 10.11 0 0 1 3.57.64 9.28 9.28 0 0 1 3 1.79 8 8 0 0 1 2 2.81 9.18 9.18 0 0 1 .72 3.7 12.32 12.32 0 0 1-.34 3l-5.11 21.36.85.6 11.4-7.83 5.36 8zm7.4-53.69a8 8 0 0 1-.64 3.19 7.68 7.68 0 0 1-1.74 2.55 8.57 8.57 0 0 1-2.59 1.7 7.82 7.82 0 0 1-3.11.64 7.72 7.72 0 0 1-3.15-.64 8.69 8.69 0 0 1-2.55-1.7 7.67 7.67 0 0 1-1.74-2.55 8.29 8.29 0 0 1 0-6.38 7.71 7.71 0 0 1 1.74-2.55 8.73 8.73 0 0 1 2.55-1.7 7.7 7.7 0 0 1 3.15-.64 7.8 7.8 0 0 1 3.11.64 8.61 8.61 0 0 1 2.59 1.7 7.72 7.72 0 0 1 1.74 2.55 8 8 0 0 1 .64 3.18zm42.8 48.58h-1.53a21.9 21.9 0 0 1-2.13 2.77 13 13 0 0 1-2.85 2.34 14.44 14.44 0 0 1-4 1.62 21.53 21.53 0 0 1-5.36.6 14 14 0 0 1-10.17-4.25 14.71 14.71 0 0 1-3.15-4.94 17.39 17.39 0 0 1-1.15-6.47 37.71 37.71 0 0 1 1.57-10.93 28.53 28.53 0 0 1 4.64-9.23 23.16 23.16 0 0 1 7.49-6.38 21 21 0 0 1 10.12-2.38q5.19 0 7.79 2.13a10.62 10.62 0 0 1 3.53 5.19h1.53l1.28-6.13h10.72l-10.47 48.92.85.6 4.76-3.23 5.36 8-5.7 3.74a11.59 11.59 0 0 1-3.53 1.7 13.14 13.14 0 0 1-3.46.44 9.35 9.35 0 0 1-6.51-2.42 8.55 8.55 0 0 1-2.68-6.68 13.84 13.84 0 0 1 .34-2.81zm-10.38-2.89a12.38 12.38 0 0 0 5.62-1.28 14.13 14.13 0 0 0 4.42-3.45 15.84 15.84 0 0 0 2.89-5 17 17 0 0 0 1-5.87 8.39 8.39 0 0 0-2.34-6.34 9 9 0 0 0-6.51-2.25 12.31 12.31 0 0 0-5.66 1.32 14 14 0 0 0-4.42 3.49 16.25 16.25 0 0 0-2.85 5 17 17 0 0 0-1 5.87q0 4.17 2.34 6.34a9.21 9.21 0 0 0 6.51 2.17z"></path>
<path class="elastiq" d="M228.45 202.41c25.89-7.67 59 2.55 80.49-31.66 25.23-40.1 60.94-44.68 85.27-31.62 34.2 18.36 31.67 68.27 7.58 90.7-27.42 25.53-29.58 52.91-13.68 67.24 22.95 20.68 47.1-2.67 35.85-19.93-8.94-13.73-31.93-25.89-98.1 6.9-65.32 32.36-129.62 19-133.91-32.42-1.03-12.53 2.88-39.25 36.5-49.21z"></path>
<path class="font" d="M132.3 236.52l12.41-58.55h35.31v10.72h-26.37l-2.94 13.62h23.06v10.72h-25.31l-2.89 13.78h25.14v10.71H132.3zm77.08 1.69a12.69 12.69 0 0 1-3.49.51 10.08 10.08 0 0 1-3.57-.64 9.25 9.25 0 0 1-3-1.79 8 8 0 0 1-2-2.81 9.16 9.16 0 0 1-.72-3.7 12.93 12.93 0 0 1 .34-3l9.27-38.71-.85-.6-11.4 7.83-5.36-8 12.34-8.34a11.68 11.68 0 0 1 3.49-1.7 12.67 12.67 0 0 1 3.49-.51 10.09 10.09 0 0 1 3.57.64 9.26 9.26 0 0 1 3 1.79 8.05 8.05 0 0 1 2 2.81 9.17 9.17 0 0 1 .72 3.7 14.62 14.62 0 0 1-.34 3l-9.27 38.71.85.6 11.4-7.83 5.36 8-12.34 8.35a11.68 11.68 0 0 1-3.49 1.69zm62.88-10.8l.85.6 4.08-2.89 5.36 8-5 3.4a12.39 12.39 0 0 1-7 2.21 9.85 9.85 0 0 1-5.79-1.79 7.85 7.85 0 0 1-3.23-5H260a15.69 15.69 0 0 1-1.79 2.68 9.7 9.7 0 0 1-2.5 2.1 14.05 14.05 0 0 1-3.49 1.45 17.83 17.83 0 0 1-4.72.55 14.23 14.23 0 0 1-6.13-1.32 15.05 15.05 0 0 1-4.89-3.66 17.4 17.4 0 0 1-3.28-5.49 19.3 19.3 0 0 1-1.19-6.89 35.69 35.69 0 0 1 1.53-10.63 26.73 26.73 0 0 1 4.42-8.64 20.46 20.46 0 0 1 16.59-8 12.9 12.9 0 0 1 7.4 1.87 9.08 9.08 0 0 1 3.66 4.94h1.53l1.19-5.62h10.72zm-20.55 1.11a11.54 11.54 0 0 0 9.4-4.51 15.06 15.06 0 0 0 2.42-4.81 19.73 19.73 0 0 0 .85-5.83 9.27 9.27 0 0 0-2.3-6.51 7.91 7.91 0 0 0-6.13-2.51 11.68 11.68 0 0 0-5.45 1.23 12.16 12.16 0 0 0-4 3.28 14.54 14.54 0 0 0-2.47 4.81 19.7 19.7 0 0 0-.85 5.83 10.06 10.06 0 0 0 2.08 6.3q2.07 2.72 6.45 2.72z"></path>
</svg>`
}
)
Vue.component(
'exportjson',
{
template: `
<div class="view view--export">
<textarea>{{textexport}}</textarea>
</div>
`,
props: {
names: Array
},
computed: {
textexport: function() {
return this.names.map(color => {
return {
color: color.requestedHex,
name: color.name,
}
});
}
}
}
);
Vue.component(
'colortext',
{
template: `
<section class="text view view--text" v-bind:style="{'--c-background': currentbackground, '--c-background-contrast': currentcolor}">
<div class="text__sample">
<h1>Color Spaces <span class="text__contrast">{{chroma.contrast(currentcolor || bgcolorcontrast, currentbackground || bgcolor).toFixed(2)}}</span></h1>
<slot></slot>
</div>
<div class="text__customizer">
<div class="text__buttons">
<button class="text-customizer__button" v-for="color in colors" v-bind:style="{color: color}" v-on:click="setColor(color)">Aa</button><button class="text-customizer__button" v-bind:style="{color: bgcolorcontrast}" v-on:click="setColor(bgcolorcontrast)">Aa</button>
</div>
<div class="text__buttons">
<button class="text-customizer-bg__button" v-for="color in colors" v-bind:style="{color: color}" v-on:click="setBgColor(color)">Aa</button><button class="text-customizer-bg__button" v-bind:style="{color: bgcolor}" v-on:click="setBgColor(bgcolor)">Aa</button>
</div>
</div>
</section>
`,
methods: {
setColor: function(color) {
this.currentcolor = color;
},
setBgColor: function(color) {
this.currentbackground = color;
}
},
data: function () {
return {
currentcolor: '',
currentbackground: '',
}
},
props: {
colors: Array,
bgcolorcontrast: String,
bgcolor: String,
}
}
);
Vue.component(
'fattext',
{
template: `
<div class="view view--fattext">
<div class="fattext-list">
<h3 v-for="name in names" v-bind:style="{color: name.requestedHex}">{{name.name}}</h3>
</div>
</div>
`,
data: function () {
return {
}
},
props: {
colors: Array,
names: Array
}
}
);
Also see: Tab Triggers