harunpehlivan
5/18/2018 - 7:10 PM

css gradient maker

css gradient maker

@import url('https://fonts.googleapis.com/css?family=Prompt:700')

.app
    width: 100%
    height: 100vh
    box-sizing: border-box
    display: flex
    overflow: hidden
.left, .right
    width: 50%
    min-height: inherit
    
.left
    display: flex
    flex-direction: column
    height: 101vh
    > div
        width: 100%
        display: flex
        align-items: center
        justify-content: center
        &:hover
            span
                color: rgba(0, 0, 0, 0.5)
                mix-blend-mode: color-burn

.sample
    transition: 200ms all ease-in-out
    font-family: 'Prompt'
    font-size: 16px
    color: transparent

.getGradient
    position: absolute
    bottom: 10px
    right: 10px
    box-sizing: border-box
    font-family: 'Prompt'
    outline: none
    border: 2px solid black
    border-radius: 4px
    padding: 10px
    font-size: 24px
    background-color: transparent
    mix-blend-mode: color-burn
    &:focus, &:hover
        outline: none
        cursor: pointer
        
h1
    font-family: 'Prompt'
    position: absolute
    font-size: 24px
    mix-blend-mode: color-burn
    color: black
    top: calc(50vh - 12px)
    width: 50%
    text-align: center
    z-index: 1
    text-transform: uppercase
    
.leftHeading
    left: 0
    
.rightHeading
    left: 50vw
    
.overlay
    position: absolute
    top: 0
    left: 0
    background: radial-gradient(farthest-corner at 40px 40px, #051937, #004d7a, #008793, #00bf72, #a8eb12)
    width: 100%
    height: 100vh
    z-index: 2
    display: flex
    align-items: center
    justify-content: center
    h1
        color: white
        mix-blend-mode: difference
        
.hide
    display: none !important
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/435311/quicksettings.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.5/chroma.min.js"></script>
// Labels
const ADD_COLOR = 'Add color'
const REMOVE_COLOR = 'Remove color'
const CLEAR_COLORS = 'Clear colors'
const SAMPLES = 'Samples'
const INTERPOLATION = 'Interpolation'
const MODE = 'Mode'
const LIGHTNESS_CORRECTION = 'Lightness correction'
const CSS_GRADIENT_TYPE = 'Css gradient type'
const POSITION = 'Position'
const TOP = 'Top'
const LEFT = 'Left'
const SHAPE = 'Shape'
const EXTENT_KEYWORD = 'Extent keyword'
const WITH_ANGLE = 'With angle'
const ANGLE = 'Angle'

const left = document.querySelector('.left')
const right = document.querySelector('.right')
const overlay = document.querySelector('.overlay')
const getGradient = document.querySelector('.getGradient')

// Handling the palette
const DEFAULT_COLOR = '#fcefa2'

let INDEX = 0
let MAX_INDEX = 4

const settingsPanel = QuickSettings
    .create(10, 10, 'Settings (double click to close)')
    .addButton(ADD_COLOR, () => addColor())
    .addButton(REMOVE_COLOR, () => removeColor())
    .addButton(CLEAR_COLORS, () => clearColors())
    

const addColor = () => {
    if (INDEX < MAX_INDEX) {
        INDEX++
        settingsPanel
            .addColor(`color_${INDEX}`, DEFAULT_COLOR)
    }
}

const removeColor = () => {
    if (INDEX > 0) {
        settingsPanel
            .removeControl(`color_${INDEX}`)
        INDEX--
    }
}

const clearColors = () => {
    if (INDEX > 0) {
        let colorName = /color_[0-9]/
        let colorControls = Object
            .entries(settingsPanel._controls)
            .filter(entry => {
                if (colorName.test(entry[0])) {
                    return entry
                }
            })
            .map(entry => entry[0])
        colorControls.forEach(control => {
            settingsPanel.removeControl(control)
        })
        INDEX = 0
    }
}

// handling chroma.js & css configuration
settingsPanel
    .addRange(SAMPLES, 10, 40, 10, 10)
    .addDropDown(INTERPOLATION, ['linear', 'bezier'])
    .addDropDown(MODE, ['none', 'lab', 'lch', 'lrgb', 'hsl'])
    .addBoolean(LIGHTNESS_CORRECTION, false)
    .addDropDown(CSS_GRADIENT_TYPE, ['linear', 'radial'])
    .addBoolean(POSITION, false)
    .addRange(TOP, 1, 100, 50, 1)
    .addRange(LEFT, 1, 100, 50, 1)
    .addDropDown(SHAPE, ['ellipse', 'circle'])
    .addDropDown(EXTENT_KEYWORD, ['none', 'closest-side', 'closest-corner', 'farthest-side', 'farthest-corner'])
    .addBoolean(WITH_ANGLE, false)
    .addRange(ANGLE, 1, 360, 180, 1)

// rule set
const modifyRules = () => {
    let s = settingsPanel
    let interpolation = s.getValue(INTERPOLATION).value
    let cssGradientType = s.getValue(CSS_GRADIENT_TYPE).value
    let withAngle = s.getValue(WITH_ANGLE)
    let shape = s.getValue(SHAPE).value
    let extentKeyword = s.getValue(EXTENT_KEYWORD).value
    let position = s.getValue(POSITION)
    
    switch (interpolation) {
        case 'linear':
            s.showControl(MODE);
        break;
        case 'bezier':
            s.hideControl(MODE);
        break;
    }

    switch (cssGradientType) {
        case 'linear':
            s.showControl(WITH_ANGLE)
            s.hideControl(SHAPE)
            s.hideControl(EXTENT_KEYWORD)
            s.hideControl(POSITION)
            s.hideControl(TOP)
            s.hideControl(LEFT)
            if (withAngle === true) {
                s.showControl(ANGLE)
            }
            if (withAngle === false) {
                s.hideControl(ANGLE)
            }
        break;
        case 'radial':
            s.hideControl(WITH_ANGLE)
            s.hideControl(ANGLE)
            s.showControl(POSITION)
            if (position === true) {
                s.showControl(TOP)
                s.showControl(LEFT)
            }
            if (position === false) {
                s.hideControl(TOP)
                s.hideControl(LEFT)
            }
            if (shape == 'circle') {
                s.hideControl(EXTENT_KEYWORD)
            }
            if (shape == 'ellipse') {
                s.showControl(EXTENT_KEYWORD)
            }
            if (extentKeyword !== 'none') {
                s.hideControl(SHAPE)
            }
            if (extentKeyword === 'none') {
                s.showControl(SHAPE)
            }
        break;
    }
    
}

// render gradients
const stringifyResults = (colors) => {
    return colors.map(color => {
        let r = Math.floor(color._rgb[0]),
            g = Math.floor(color._rgb[1]),
            b = Math.floor(color._rgb[2]),
            a = color._rgb[3]
        return `rgba(${r}, ${g}, ${b}, ${a})`
    })
}

const getEndString = result => {
    let s = settingsPanel
    let cssGradientType = s.getValue(CSS_GRADIENT_TYPE).value
    let shape = s.getValue(SHAPE).value
    let extentKeyword = s.getValue(EXTENT_KEYWORD).value
    let withAngle = s.getValue(WITH_ANGLE)
    let angle = s.getValue(ANGLE)
    let position = s.getValue(POSITION)
    let top = s.getValue(TOP)
    let left = s.getValue(LEFT)
    let endString
    let $angle = angle && withAngle && cssGradientType === 'linear' ? angle + 'deg, ' : ''
    let $shape = () => {
        if (cssGradientType === 'radial') {
            if (shape && shape !== 'ellipse' && position === false) {
                return shape + ', '
            }
            if (shape && shape !== 'ellipse' && position === true) {
                return `${shape} at ${left}% ${top}%, `
            }
            if (shape && shape === 'ellipse' && position === true && extentKeyword === 'none') {
                return `${shape} at ${left}% ${top}%, `
            }
            if (shape && shape === 'ellipse' && position === true && extentKeyword !== 'none') {
                return ''
            }
        }
        return ''
    }
    let $extent = () => {
        let afterExtent = position === true ? ` at ${left}% ${top}%, ` : ', '
        if (extentKeyword !== 'none') {
            return extentKeyword + afterExtent
        }
        return ''
    }
    endString = `${cssGradientType}-gradient(${$angle}${$shape()}${$extent()}${result})`
    
    return endString
}

const renderGradient = (result, endString, samples) => {
    while (left.firstChild) {
        if (left.firstChild.nodeName !== 'h3') {
            left.removeChild(
                left.firstChild
            )
        }
    }
    for (let i = 0; i < result.length; i++) {
        let color = result[i];
        let div = document.createElement('div')
        div.style = `background-color: ${color}; height: ${101 / samples}vh;`
        div.innerHTML = `<span class="sample">${div.style.backgroundColor}</span>`
        left.appendChild(div)
    }
    right.style.background = endString
}

const createGradient = () => {
    // get current configuration
    let s = settingsPanel
    let samples = s.getValue(SAMPLES)
    let interpolation = s.getValue(INTERPOLATION).value
    let mode = s.getValue(MODE).value
    let lightnessCorrection = s.getValue(LIGHTNESS_CORRECTION)
    let cssGradientType = s.getValue(CSS_GRADIENT_TYPE).value
    let shape = s.getValue(SHAPE).value
    let extentKeyword = s.getValue(EXTENT_KEYWORD).value
    let withAngle = s.getValue(WITH_ANGLE)
    let angle = s.getValue(ANGLE)
    let position = s.getValue(POSITION)
    let top = s.getValue(TOP)
    let left = s.getValue(LEFT)

    let colors = Object
        .keys(s._controls)
        .filter(entry => /color_[0-9]/.test(entry) === true)
        .map(entry => s.getValue(entry))

    let scale
    let result = []
    let endString
    
    // set scale
    switch (interpolation) {
        case 'linear':
            if (mode !== 'none') {
                if (lightnessCorrection) {
                    scale = chroma
                        .scale(colors)
                        .mode(mode)
                        .correctLightness()
                } else {
                    scale = chroma
                        .scale(colors)
                        .mode(mode)
                }
            } else {
                if (lightnessCorrection) {
                    scale = chroma
                        .scale(colors)
                        .correctLightness()
                } else {
                    scale = chroma
                        .scale(colors)
                }
            }
        break
        case 'bezier':
                if (lightnessCorrection) {
                    scale = chroma
                        .bezier(colors)
                        .scale()
                        .correctLightness()
                } else {
                    scale = chroma
                        .bezier(colors)
                }
        break
    }
    
    for (let i = 0; i < samples; i++) {
        result.push(scale(i / samples));
    }
    
    // create base gradient
    result = stringifyResults(result)
    // and the css one
    endString = getEndString(result)
    // render both
    renderGradient(result, endString, samples)
}

settingsPanel
    .setGlobalChangeHandler(() => {
        modifyRules()
        if (INDEX >= 2) {
            overlay.classList.add('hide')
            createGradient()
        } 
        if (INDEX < 2) {
            overlay.classList.remove('hide')
        }
    })

modifyRules()

//get css gradient
const showGradient = () => {
    getGradient.addEventListener('click', () => {
        let style = right.style
        document.execCommand('Copy')
        alert(style.background)
    })
}

showGradient()
.app
    .left
    .right
        button.getGradient Get CSS

h1.leftHeading Base
h1.rightHeading Css

.overlay
    h1 Add 2+ colors to create a gradient

css gradient maker

Uses chroma.js for generating a gradient and modified (pull request waits for approve) QuickSettings.js.

A Pen by HARUN PEHLİVAN on CodePen.

License.