Skip to content

gVguy/vue-color-input

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vue-color-input

Slick and perfomant Vue 3 color picker component whose goal is to replace <input type="color">

🚀  Live demo  🚀

Codepen

Why

Multi-format

Forget about color conversions: vue-color-input does it for you. Unlike <input type="color"> (which only understands hex) vue-color-input supports all commonly used color models, and by default will output color in the same format that was passed as input. It also has support for alpha channel, unless you specifically disable it.

Customizable

HTML's native color input is annoying to style. Most likely you'll have to get tricky hiding the original input & binding click event to a presentable-looking div. But it only gets you halfway there cause the color picker popup window is still out of reach and it might look way different in different browsers.
With vue-color-input this poblem is solved. It looks pretty out of the box with the default styles, but it's also intuitive & straight-forward to customize from css.

Uniform

Not only that native color input looks different in different browsers, it also operates differently, and in some cases it's just not what you expect it to be. Yes, I'm looking at you, Safari. vue-color-input delivers a color picker that looks and performs the same regardless of browser.

It just works

vue-color-input combines minimalist approach with comprehensive functionality. You can customize and extend it to your liking, set it up with additional properties, but you don't have to. It works as expected out of the box just as well, with only v-model provided.

Installation

npm

npm i vue-color-input
import ColorInput from 'vue-color-input'

CDN

<script src="https://unpkg.com/vue-color-input@latest"></script>

Usage

// install it with use()
app.use(ColorInput)

// OR register component globally
app.component('ColorInput', ColorInput)

// OR locally
export.default {
    components: { ColorInput }
}
<color-input v-model="color" />

Table of contents

Properties Styling Events $refs & methods
v-model Transitions Event names Instances
format Box active transition Elements
position Example CSS Methods
disabled Styling guidelines color property
disable-alpha
disable-text-inputs
transition

Properties

v-model

This is where you supply the variable holding the color to be adjusted by end user.
Model value is allowed to be changed externally too, vue-color-input will adjust accordingly.
When first initialized and every time v-model value updates from outside the component, incoming color format is stored to be matched by output.

Input (initial value)

Under the hood vue-color-input uses tinycolor2 for color conversion. So everything tinycolor accepts as input, is valid here as well (both string and object).

Output (return value)

By default output will be a string or an object in the same color model as the initial value.

For example:

// in parent component

export.default {
    data() {
        color: "rgb(50, 150, 150)"
    }
}
<!-- in template -->

<color-input v-model="color" />

User adjusts hue to 0, now color becomes

"rgb(150, 50, 50)"

Then user adjusts alpha to 0.5, color becomes

"rgba(150, 50, 50, 0.5)"

Let's say color property was initialy set to be an object:

// in parent component

export.default {
    data() {
    color: { "h": 350, "s": 1, "l": 0.8 }
    }
}

In the same scenario the resulting output would be

{ "h": 0, "s": 1, "l": 0.8, "a": 0.5 }

vue-color-input will always try to output color in the same color model as the initial value (unless target format is specified explicitly by format property.
However in some cases that would not be possible. For those colors it will fall back to different formats.

name || hex -> rgba fallback

If initial color format was name (e.g. "purple") or hex (e.g. "#800080"), and then alpha is changed to be less than 1, output will be formatted as rgba:

"#cd5c5c" // hex input

/* user changes alpha to 0.9 */

"rgba(205, 92, 92, 0.9)" // rgba output

Note: this behavior does not apply if format property is explicitly set to be hex or name.
Note 2: if initial color format is hex8 (e.g. #800080ff), output will be hex8 also, unless specified differently by format property.

name -> hex fallback

If initial color format was name, but the resulting output color does not have a name equivalent, hex value will be output instead:

"indianred" // name input

/* user changes hue to 180 */

"#5ccdcc" // hex output

invalid -> rgb fallback

Invalid color initialy diasplays as black. Default output format will be set to rgb:

"ironmanred" // invalid string input

/* user changes alpha to 0.1 */

"rgba(0, 0, 0, 0.1)" // rgb(a) output

format optional

Here you can supply the color format you want the output to be in.

The value consists of two arguments: format & type. The order of two is inconsequential, e.g. both "hsl object" & "object hsl" are valid values.

Format is the target color model that the return value is converted to. [ "rgb", "hsv", "hsl", "hex", "hex8", "name" ]

Type is data type of the return value. [ "string", "object" ]

If you want to use v-model value for styling, "string" type should do the job. On the other hand, if you want to continue processing the data, "object" is probably more useful.

Hsv & hsl color component values are presented differently in different output types:

"hsl(0, 53%, 58%)" // "hsl string"

{ "h": 0, "s": 0.531, "l": 0.582, "a": 1 } // "hsl object"

Notice how strings contain percent-based values, and object 0-1 floats.

Note that name & hex formats don't support alpha channel. Specifying either of them as target format will prevent vue-color-input from falling back to rgba. Instead, it will disable alpha slider and always return full opacity color.
If this is not the behavior that you want, and you'd rather it fall back to rgba to support alpha, you should not specify the format.

Type

String

Allowed values

[ "rgb", "rgb object", "rgb string", 
  "hsv", "hsv object", "hsv string", 
  "hsl", "hsl object", "hsl string",
  "name", "name string",
  "hex", "hex string",
  "hex8", "hex8 string" ]

Note: "name object", "hex object" & "hex8 object", make no sense and therefore are illegal.
Note 2: format without type is allowed, type without format is not.

Default value

Calculated to match the input.

Example

<color-input v-model="color" format="rgb object" />

position optional

This is where you specify the position of the popup color picker window relative to the clickable box.

Type

String

Allowed values

[ "top", "top right", "top left", "top center",
  "right top", "right", "right bottom", "right center",
  "bottom right", "bottom", "bottom left", "bottom center",
  "left top", "left bottom", "left", "left center" ]

Pretty intuitive: the first value is the direction from the box in which the popup will appear, the second is how it will align.
Note: Omitting the second parameter results in center alignment, making "top" a shortcut for "top center"

Default value

"bottom"

Example

<color-input v-model="color" position="right top" />

disabled optional

Setting this to true will make the initial box nonresponsive to user clicks. The popup will not appear.
However the box will still react to v-model changes, should they come from elsewhere.

Type

Boolean

Allowed values

[ true, false ]

Default value

false

Example

<color-input v-model="color" :disabled="!allowColorAdjustment" />

disable-alpha optional

If you set this to true, alpha slider will be removed from the color picker, and the returned color will always have full opacity.

Specifying name or hex as the target format will make this property default to true and ignore any passed value.

Type

Boolean

Allowed values

[ true, false ]

Default value

false,
true if target format is hex or name

Example

<color-input v-model="color" disable-alpha />

disable-text-inputs optional

With this property you can hide the section of the color picker containing the text inputs.

Type

Boolean

Allowed values

[ true, false ]

Default value

false

Example

<color-input v-model="color" disable-text-inputs />

transition optional

Set this to a custom transition name to override factory enter and leave-to transitions of the popup.

This is not the only way to customize color picker transition.
You can also override default transition classes from css. More details below.

More information about Vue enter/leave transitions here.

Type

String

Default value

"picker"

Example

<color-input v-model="color" transition="my-cool-transition" />
.my-cool-transition-enter-from,
.my-cool-transition-leave-to {
    transform: rotate(240) scale(.5);
    opacity: 0;
}
.my-cool-transition-enter-active,
.my-cool-transition-leave-active {
    transition: transform .3s, opacity .3s;
}

Styling

As previously mentioned, applying styles to vue-color-input is a breeze.

Default CSS is written with custumizability in mind, so anything you want to style will likely work as expected, and the whole component's layout will not get screwed up by that.

To override factory styles, you should address elemets through .color-input.user parent selector, e.g:

.color-input.user .box { }

Class names

class description
.color-input Root element
.box Initial clickable box
.picker-popup Popup color picker window
.saturation-area Picking area where you select saturation and brightness
.slider Hue and opacity sliders (track)
.saturation-pointer Pointer in the saturation-brightness area
.slider-pointer Pointer on a slider
.text-input Text inputs of the color picker

Feel free to scout the HTML for more class names.

Transitions

Instead of using transition property with a custom transition name, you can simply override default transition styles.
This can be done in the same manner as with the other classes, e.g:

.color-input.user .picker-popup-enter-from {
    transform: translateY(-100%) scale(.1);
}
.color-input.user .picker-popup-leave-to {
    transform: scale(3);
}
/* and if you want to change the durations as well */
.color-input.user .picker-popup-enter-active,
.color-input.user .picker-popup-leave-active {
    transition: all .5s;
}

More information about Vue enter/leave transitions here.

Box active transition

When clicked on, the box gets what looks like an outline, but in reality its content is scaled down and background is revealed.

Here's what the box element html looks like:

<div class="box [active] [disabled]"> <!-- This has a background -->
    <div class="inner transparent"> <!-- This scales down to reveal it -->
        <div class="color"></div>
    </div>
</div>

To customize this transition, you can use .box.active in combination with .box.active .inner.
For example:

.color-input.user .box.active {
    /* "outline" color */
    background: #0f0f0f;
}
.color-input.user .box.active .inner {
    /* different transition effect */
    transform: scale(.9) rotate(90deg);
}

Example CSS

.color-input.user .box {
    /* make clickable box a 100x100 circle */
    width: 100px;
    height: 100px;
    border-radius: 50px;
}
.color-input.user .picker-popup {
    /* dark mode for popup window */
    background: #000;
    color: #fbfbfb;
    /* and make it wide */
    width: 400px;
}
.color-input.user .slider {
    /* thin out the sliders and make them wider */
    height: 2px;
    width: 92%;
}
.color-input.user .saturation-area {
    /* bigger picking area */
    height: 150px;
}
.color-input.user .slider-pointer {
    /* make slider pointers square-ish and 10x10 */
    border-radius: 4px;
    width: 10px;
    height: 10px;
}
.color-input.user .saturation-pointer {
    /* increase saturation pointer size */
    width: 40px;
    height: 40px;
}

Styling guidelines

Root element is not the .box

Here's the base structure of the component:

<div class="color-input">
    <div class="box [active] [disabled]"></div>
    <div class="picker-popup"></div> <!-- position: absolute -->
</div>

Root element wraps arond the clickable box, but if you want to change box styles, you should select it like this: .color-input.user .box.

Generally, you should attempt to style the root element only if you want to customize the flow: properties like margin, position, display.

Changing size of the root element independently from the box will mess with how the popup is positioned.

Use stylesheets, no need to pass inline styles

Inline styles will only let you style the root element, which is typically not what you want to style very often.

Use .color-input.user to override default styles

There is no need to use !important. Default styles are easily overridable by adding specificity to the selectors with .color-input.user .<classname>.

And if you use scss that's even more natural with nesting:

.color-input.user {
    .box {}
    .picker-popup {}
    // etc
}

Set margin on the root element

margin is one of the few properties that should belong to the .color-input itself.
Setting margin on the .box instead will increase the space around it inside the root element, and that will mess with how the popup is positioned.

Events

The instance provides hooks for custom event handling.

Most events carry payload with current state of the corresponding color component.

Notice that event data is always passed in hsv format.

Event names

event description payload
pickEnd color picking process is finished, popup will close now
mounted lifecycle hook, emitted from root component's mounted()
beforeUnmount lifecycle hook, emitted from root component's beforeUnmount()
pickStart color picking process is initiated, popup is opening
saturationInputStart saturation-brightness adjustment has begun.
This is only emitted when pointerdown inside saturation-brightness area is registered.
This will not emit when text inputs are edited
current state of saturation & value (hsv)
{ s: 0.5, v: 0.5 }
saturationInputEnd saturation-brightness adjustment has ended.
This is only emitted when pointerup of the saturation-brightness area is registered.
This will not emit when text inputs are edited
current state of saturation & value (hsv)
{ s: 0.5, v: 0.5 }
saturationInput saturation-brightness is being adjusted.
This will emit every time saturation-brightness is changed, including text inputs
current state of saturation & value (hsv)
{ s: 0.5, v: 0.5 }
hueInputStart hue adjustment has begun.
This is only emitted when pointerdown over the hue slider is registered.
This will not emit when hue is changed from text inputs
current state of hue
{ h: 180 }
hueInputEnd hue adjustment has ended.
This is only emitted when pointerup of the hue slider is registered.
This will not emit when hue is changed from text inputs
current state of hue
{ h: 180 }
hueInput hue is being adjusted.
This will emit every time hue is changed, including text inputs
current state of hue
{ h: 180 }
alphaInputStart alpha adjustment has begun.
This is only emitted when pointerdown over the alpha slider is registered.
This will not emit when alpha is changed from text inputs
current state of alpha
{ a: 0.5 }
alphaInputEnd alpha adjustment has ended.
This is only emitted when pointerup of the alpha slider is registered.
This will not emit when alpha is changed from text inputs
current state of alpha
{ a: 0.5 }
alphaInput alpha is being adjusted.
This will emit every time alpha is changed, including text inputs
current state of alpha
{ a: 0.5 }
change the color has changed by user interaction.
This will emit every time any parameter is changed.
This will emit when color is changed from text inputs as well, on blur
current state of all color components
{ h: 180, s: 0.5, v: 0.5, a: 0.5 }

Example

<color-input v-model="color" @mounted="colorInputMountedHandler" @pickStart="colorPickerShowHandler" />

$refs & methods

You shouldn't need to manually access instance elements or methods, but if you feel like it, you can.
This can be done by specifying a ref property on the instance.

The following section implies you have a vue-color-input instance with a ref property set to "colorInput":

<color-input v-model="color" ref="colorInput" />

Instances

const colorInput = this.$refs.colorInput // root instance
const picker = colorInput.$refs.picker // popup color picker instance

Elements

colorInput.$refs.root // root element
colorInput.$refs.box // box root element
picker.$refs.rootPicker // color picker root element

Methods

colorInput.pickStart() // begin color selection (show popup)
colorInput.pickEnd() // end color selection (hide popup)

color property

colorInput.color // tinycolor instance

License

MIT