Jump to Table of Contents

Example: HSL Harmony

Use the HSL color picker to create harmony colors.

Saturation:
Luminance:
  • Complementary:
  • Split Complementary:
  • Analogous:
  • Triad:
  • Square:
  • Tetrad:
  • Monochromatic:
  • Similar:

Setting Up the HSL Picker UI

This example takes advantage of the dial and slider widgets. We will use the dial widget to represent and select the hue from the 360 degree color wheel.

Next we will use two sliders to select the saturation and luminance.

<!-- HSL Color Picker -->
<div class="picker">
    <div id="hue-dial"></div>
    <div class="sliders">
        <div id="sat-slider"><strong>Saturation: <span></span></strong></div>
        <div id="lum-slider"><strong>Luminance: <span></span></strong></div>
    </div>
    <div class="color"></div>
</div>

<!-- Picker Output -->
<div class="harmonies picker-output">
    <ul>
        <li><strong>Complementary:</strong> <span id="h-complementary"></span></li>
        <li><strong>Split Complementary:</strong> <span id="h-split-complementary"></span></li>
        <li><strong>Analogous:</strong> <span id="h-analogous"></span></li>
        <li><strong>Triad:</strong> <span id="h-triad"></span></li>
        <li><strong>Square:</strong> <span id="h-square"></span></li>
        <li><strong>Tetrad:</strong> <span id="h-tetrad"></span></li>
        <li><strong>Monochromatic:</strong> <span id="h-monochromatic"></span></li>
        <li><strong>Similar:</strong> <span id="h-similar"></span></li>
    </ul>
</div>

This could use a little CSS for positioning, so let's get to that next.

/** HSL Color Picker **/
.picker {
    padding: 15px;
    background: #efefef;
    border: 1px solid #ddd;
}

#hue-dial, .sliders, .color {
    display: inline-block;
    zoom: 1; *display: inline;
    vertical-align: middle;
}

#hue-dial .yui3-dial-ring {
    background: url(../assets/color/colorwheel.png) -25px -25px no-repeat;
}

.sliders {
    margin: 0 30px;
}

.sliders strong {
    font-weight: 600;
    display: block;
}
.sliders strong span {
    font-weight: 300;
}
.sliders div + div {
    margin-top: 1em;
}

.color {
    width: 100px;
    height: 100px;
    border: 1px solid rgba(0, 0, 0, 0.5);
    -webkit-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.3);
    -moz-box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.3);
    box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.3);
}

/** Output Styles **/
.picker-output {
    margin: 15px;
}

.picker-output ul {
    margin: 0;
    padding: 0;
}
.picker-output li {
    list-style: none;
    line-height: 1.8em;
}

.picker-output .swatch {
    width: 1em;
    height: 1em;
    margin-left: 0.5em;
    vertical-align: middle;
    position: relative;
}
.tooltip {
    position: absolute;
    top: 1.3em;
    left: 0;
    background: #ffefc2;
    border: 1px solid #f5b400;
    padding: 10px;
    z-index: 10;
}
.tooltip li {
    white-space: nowrap;
}

Setting Up the YUI Instance

Now we need to create our YUI instance and tell it to load the color, slider, dial and event-valuechange modules.

YUI().use('dial', 'slider', 'event-valuechange', 'color', function (Y) {
    // Code Here.
});

Building The Color Picker

Initializing UI Variables

First things first, we need to make sure our picker UI has the sam skin class applied.

Next, we create a dial for the hue from 0 to 360. Then create a slider from 0 to 100 for saturation and luminance.

We also want to maintain a reference to the nodes where the saturation and luminance value can be read and the color swatch that will be updated from the UI.

Y.one('.picker').addClass('yui3-skin-sam');

var hue = new Y.Dial({
        min: 0,
        max: 360,
        stepsPerRevolution: 360,
        continuous: true,
        centerButtonDiameter: 0.4,
        render: '#hue-dial'
    }),
    sat = new Y.Slider({
        min: 0,
        max: 100,
        value: 100,
        render: '#sat-slider'
    }),
    lum = new Y.Slider({
        min: 0,
        max: 100,
        value: 50,
        render: '#lum-slider'
    }),
    satValue = Y.one('#sat-slider span'),
    lumValue = Y.one('#lum-slider span'),
    color = Y.one('.color');
Binding Events

After the UI components are initialized, we need to bind their respective change methods to update the UI. For dial, this is valueChange. For a slider, we use thumbMove.

hue.after('valueChange', function(e) {
    updatePickerUI();
});

sat.after('thumbMove', function(e) {
    updatePickerUI();
});

lum.after('thumbMove', function(e) {
    lumValue.set('text', lum.get('value') + '%');
    updatePickerUI();
});
Useful Functions

Finally, we create two methods: setPickerUI and updatePickerUI.

setPickerUI will allow us to send an Object with h, s and l values to update the UI positions.

updatePickerUI will process the values for hue, saturation and luminance and update the color swatch. updatePickerUI will also call updateOutput that we will define next.

function setPickerUI(hsl) {
    if (typeof hsl.h !== 'undefined') {
        hue.set('value', +hsl.h);
    }

    if (typeof hsl.s !== 'undefined') {
        sat.set('value', +hsl.s);
    }

    if (typeof hsl.l !== 'undefined') {
        lum.set('value', +hsl.l);
    }
}

function updatePickerUI() {
    var h = hue.get('value'),
        s = sat.get('value'),
        l = lum.get('value'),
        hslString = Y.Color.fromArray([h, s, l], Y.Color.TYPES.HSL);

    satValue.set('text', s + '%');
    lumValue.set('text', l + '%');

    color.setStyle('backgroundColor', hslString);

    updateOutput(hslString);
}

Building The Picker's Output

Initializing Output Variables
var complementary = Y.one('#h-complementary'),
    split = Y.one('#h-split-complementary'),
    analogous = Y.one('#h-analogous'),
    triad = Y.one('#h-triad'),
    square = Y.one('#h-square'),
    tetrad = Y.one('#h-tetrad'),
    mono = Y.one('#h-monochromatic'),
    similar = Y.one('#h-similar'),
    swatchTip = Y.Node.create('<div class="tooltip"></div>');
Ouput Functions
function updateOutput(hslString) {
    swatchTip.remove();
    clearColorSwatches();
    makeColorSwatches(hslString);
}

function clearColorSwatches() {
    complementary.empty();
    split.empty();
    analogous.empty();
    triad.empty();
    square.empty();
    tetrad.empty();
    mono.empty();
    similar.empty();
}

function makeColorSwatches(hslString) {
    // complementary swatches
    Y.Array.each(Y.Color.getComplementary(hslString), function(color) {
        complementary.append(getColorSwatch(color));
    });

    // split complementary swatches
    Y.Array.each(Y.Color.getSplit(hslString), function(color){
        split.append(getColorSwatch(color));
    });

    // analogous swatches
    Y.Array.each(Y.Color.getAnalogous(hslString), function(color) {
        analogous.append(getColorSwatch(color));
    });

    // triad swatches
    Y.Array.each(Y.Color.getTriad(hslString), function(color) {
        triad.append(getColorSwatch(color));
    });

    // square swatches
    Y.Array.each(Y.Color.getSquare(hslString), function(color) {
        square.append(getColorSwatch(color));
    });

    // tetrad swatches
    Y.Array.each(Y.Color.getTetrad(hslString), function(color) {
        tetrad.append(getColorSwatch(color));
    });

    // monochrome swatches
    Y.Array.each(Y.Color.getMonochrome(hslString), function(color) {
        mono.append(getColorSwatch(color));
    });

    // similar swatches
    Y.Array.each(Y.Color.getSimilar(hslString), function(color) {
        similar.append(getColorSwatch(color));
    });
}

function getColorSwatch(color) {
    return '<span class="color swatch" style="background-color:' + color + '" title="' + color + '"></span>';
}
Provide Useful Information

Let's be honest, having a bunch of colors show up on the screen isn't really useful if you cannot get the value easily.

One solution is to provide a tooltip with the hex, rgb, and hsl values when the user clicks on the swatch. So let's set that up.

Y.one('.picker-output').delegate('click', function(e) {

    swatchTip.empty();

    var str = '<ul>',
        swatch = e.currentTarget,
        color = swatch.get('title');

    if (swatch.contains(swatchTip)) {
        swatchTip.remove();
    } else {
        str += '<li><strong>Hex: </strong> ' + Y.Color.toHex(color) + '</li>';
        str += '<li><strong>RGB: </strong> ' + Y.Color.toRGB(color) + '</li>';
        str += '<li><strong>HSL: </strong> ' + Y.Color.toHSL(color) + '</li>';

        str += '</ul>';

        swatch.append(swatchTip);
        swatchTip.setHTML(str);
    };

}, '.swatch');

Get It Started

Our last step is to make sure we set the initial view of the UI when everything has loaded. We can do this by simply calling updatePickerUI.

updatePickerUI();