tkrotoff/jquery-simplecolorpicker
A lightweight jQuery color picker plugin that renders a simple grid of color swatches for any text input. Easy to style and theme, it pairs well with Bootstrap and plain forms, requires only jQuery, and needs minimal markup and JavaScript to set up.
tkrotoff/jquery-simplecolorpicker is a small jQuery plugin that turns a text input into a palette-based hex color picker. It is not a Laravel package — there is no service provider, no facade, no config file. You consume it as a front-end asset and wire it up yourself. Treat it as a drop-in widget, not an integration.
Where to look first
examples/ (or demo) HTML file — it shows the required CSS + JS pairing and the expected input markup.dist/ output — that is what you vendor or import, not the source.Install
# if published to npm / your registry
npm i jquery jquery-simplecolorpicker
If it is not resolvable from npm (common for older plugins), vendor the built files:
resources/vendor/simplecolorpicker/jquery.simplecolorpicker.js
resources/vendor/simplecolorpicker/jquery.simplecolorpicker.css
Minimal wiring in resources/js/app.js
import $ from 'jquery';
window.$ = window.jQuery = $; // plugin expects a global jQuery
import '../vendor/simplecolorpicker/jquery.simplecolorpicker.css';
import '../vendor/simplecolorpicker/jquery.simplecolorpicker.js';
$(function () {
$('.simplecolorpicker').simplecolorpicker();
});
Blade
<input
type="text"
name="color"
id="color"
class="simplecolorpicker"
value="{{ old('color', $category->color ?? '#3498db') }}"
>
First use case to ship: a single hex field on a CRUD form (category tag color, label color, status color) stored as a char(7) column and validated with regex:/^#[0-9a-fA-F]{6}$/. Ship that, confirm the input value posts correctly, then expand to palettes and dynamic rows.
1. Bundle order is the integration. jQuery must be global before the plugin file executes. Do not import the plugin in a module that does not guarantee window.jQuery exists first. Keeping jQuery + plugin in the same entry file is the safest pattern; if you must share jQuery, add it to Vite's optimizeDeps / manualChunks so you do not end up with two jQuery instances (the plugin binds to the first one and silently does nothing on the second).
2. Blade component wrapper. Encapsulate markup so the field is consistent:
{{-- resources/views/components/color-field.blade.php --}}
@props(['name', 'value' => '#000000', 'colors' => null])
<div class="color-field" data-colors='@json($colors)'>
<input
type="text"
name="{{ $name }}"
id="{{ $id = $attributes->get('id', $name) }}"
value="{{ old($name, $value) }}"
class="simplecolorpicker"
{{ $attributes->merge(['class' => 'form-input']) }}
>
</div>
Then in JS, initialize by scanning .simplecolorpicker and reading the palette from the closest wrapper's data-colors. This lets the server define palettes instead of hardcoding them in JS.
3. Server-driven palettes. Keep the preset list in a config file so both Blade and validation can reference it:
// config/ui.php
return [
'palettes' => [
'default' => ['#e74c3c', '#e67e22', '#f1c40f', '#2ecc71', '#3498db', '#9b59b6'],
],
];
Emit the palette into the input's data-colors attribute and pass it to the plugin's colors option at init. One source of truth, easy to theme per tenant.
4. Re-init on dynamic content. For Livewire / Alpine / Turbo-style re-renders, initialize idempotently:
function initColorPickers(root = document) {
root.querySelectorAll('.simplecolorpicker').forEach((el) => {
if (el.dataset.scpReady) return; // guard against double-init
el.dataset.scpReady = '1';
$(el).simplecolorpicker({ /* options */ });
});
}
document.addEventListener('DOMContentLoaded', () => initColorPickers());
document.addEventListener('livewire:navigated', () => initColorPickers());
// Livewire morph hooks
document.addEventListener('livewire:init', () => {
Livewire.hook('morph.updated', ({ el }) => initColorPickers(el));
});
The double-init guard matters: the plugin mutates the DOM around the input, and a second init can produce a stacked/duplicated palette or a detached picker.
5. Preserve old() and edit-form values. The plugin reads the input's value on init, so value="{{ old('color', $model->color) }}" is usually enough. If the plugin ignores the pre-set value in your version, set it explicitly after init: $('#color').val(existing).trigger('change'). Always verify with a failed validation round-trip — that is the bug that reaches production.
6. Validation and normalization. The plugin gives you a hex string; the server must not trust it.
$validated = $request->validate([
'color' => ['required', 'string', 'regex:/^#[0-9a-fA-F]{6}$/'],
]);
$validated['color'] = strtolower($validated['color']);
Store normalized lowercase; otherwise #FFF, #ffffff and #FFFFFF all collide in your DB queries and color-diffing logic.
7. Hidden input + preview pattern. For editors where the picker is a side panel rather than an inline field, keep the real input and let the plugin drive it; render a small swatch via a style binding rather than reading the plugin's internal DOM. Read state from $input.val(), never from the plugin's rendered markup.
8. Multi-field / repeater rows. Bind a single delegated initializer per row and key your state off data-* attributes. Avoid array-indexed IDs; the plugin's generated element IDs will collide when rows are cloned.
content config that never sees the vendor CSS.window.jQuery, the plugin throws simplecolorpicker is not a function. A second bundled copy of jQuery is equally fatal and much harder to spot — check window.jQuery.fn.jquery and that your plugin was imported in the same chunk.defer-ed module scripts run before DOMContentLoaded fires is fine, but initializing before the element exists silently no-ops. Also watch for layouts that render the form after an async fetch (modals, drawers) — initialize after insertion, not on page load.wire:ignore around the field if the value does not need to reactively re-render, and re-init on morph.updated.overflow: hidden or appear behind the modal. Often fixed with a higher z-index on the picker class plus a container that is not overflow: hidden.rgba() you need a different widget — do not try to bolt alpha onto a hex string and hope the parser is forgiving.<label for> and a visible current-value readout.<input type="color"> on small viewports.change on the input after selection if anything else (validation libraries, dirty-state trackers, Livewire wire:model) listens to it. Many jQuery plugins update .val() without triggering events, which makes wire:model appear to work only on save.$(function(){...}) in Blade will break under a strict script-src. Keep init in your bundled entry file, not in <script> tags.$.fn.simplecolorpicker in the console? (3) is the CSS present? (4) was the element in the DOM at init time? (5) did a re-render orphan it?<input type="color">, or any maintained palette widget) is a one-file change on the front end. Keep the server-side validation and storage contract hex-only and you keep that exit cheap.How can I help you explore Laravel packages today?