codeplace-io/color-picker-bundle
codeplace-io/color-picker-bundle adds a reusable color picker form field to Symfony applications. It offers configurable picker options, simple asset and Twig integration, and a straightforward way to add color selection to admin panels, settings pages, and other forms.
Read this first: it is a Symfony bundle, not a Laravel package. The repo is Wandi/ColorPickerBundle published as codeplace-io/color-picker-bundle, with zero stars and zero dependents. There is no service provider, no facade, no config publish tag, and no laravel/framework constraint anywhere. composer require will technically resolve (it pulls Symfony components), but nothing boots — Laravel's package discovery has nothing to discover.
So the "getting started" path for a Laravel dev is evaluation, then port, not installation.
Minimal steps, in order:
composer show codeplace-io/color-picker-bundle --all (or clone the repo and read composer.json). You are looking for three things: the type (symfony-bundle), the PHP/Symfony version constraints under require, and extra.symfony. If it pins symfony/form: ^3.x|^4.x, it will conflict with the Symfony 6/7 components a modern Laravel app drags in transitively — that's a blocker, not a to-do.Wandi\ColorPickerBundle\WandiColorPickerBundle) and DependencyInjection/Configuration.php. The config tree is your only documentation for what options exist (theme, default format, picker library).Form/Type/ColorPickerType.php. This class is the entire payload. Everything else is Symfony plumbing you will not reuse.Resources/public/js/* and Resources/public/css/*. This is the only part you can lift verbatim, and it's the only part worth lifting.First use case: a brand-color field on a settings page. Do not install the bundle for this. Spend 30–60 minutes extracting the JS picker and reimplementing the field as a Blade component plus a validation rule. That is cheaper than one hour of Symfony-bundle archaeology, and you end up with code your team actually owns.
If you're on a mixed stack and genuinely need the Symfony Form type, that's a different document — this one assumes a Laravel app.
1. Blade component wrapper. Mirror the Symfony type's options (format, value, theme) as props and keep the component dumb:
{{-- resources/views/components/color-picker.blade.php --}}
@props(['name', 'value' => '#000000', 'format' => 'hex'])
<div class="color-picker" data-colorpicker data-format="{{ $format }}">
<input type="text"
name="{{ $name }}"
id="{{ $id = $attributes->get('id', $name) }}"
value="{{ old($name, $value) }}"
pattern="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
{{ $attributes->except('id')->merge(['class' => 'color-picker__input']) }}>
</div>
2. Initialize once, in your Vite entry. Do not inline <script> tags per field. The bundle's Twig template attaches a data-* hook and a single init pass; replicate that in resources/js/app.js so it survives Turbo/Livewire navigation:
import Pickr from '@simonwep/pickr'; // or whatever the bundle actually wraps
const init = (root = document) => {
root.querySelectorAll('[data-colorpicker]:not([data-ready])').forEach((el) => {
el.dataset.ready = '1';
const input = el.querySelector('input');
Pickr.create({ el, default: input.value, components: { preview: true, opacity: false, hue: true } })
.on('save', (color) => { input.value = color.toHEXA().toString(3).toUpperCase(); });
});
};
document.addEventListener('DOMContentLoaded', () => init());
document.addEventListener('livewire:navigated', () => init());
3. Validation is a first-class concern, and the bundle won't give it to you. Symfony's default would be a Regex/Assert constraint; in Laravel make it a rule and reuse it everywhere:
class HexColor implements ValidationRule
{
public function __construct(private bool $allowAlpha = false) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$pattern = $this->allowAlpha
? '/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3,4})$/'
: '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/';
if (!is_string($value) || !preg_match($pattern, $value)) {
$fail('The :attribute must be a valid hex color.');
}
}
}
4. Normalize on the way in, not on the way out. Pickers emit #fff, #FFFFFF, and occasionally rgba(...). Store one canonical shape so your CSS and queries stay sane:
class HexColorCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes): ?string
{
return $value;
}
public function set($model, $key, $value, $attributes): array
{
return [$key => $value === null ? null : strtoupper(
'#'.ltrim(preg_replace('/[^0-9A-Fa-f]/', '', $value), '#')
)];
}
}
Pair it with $table->char('brand_color', 7) (or 9 with alpha) and a factory default of '#000000'. Validate the length after normalization — #FFF expanding to #FFFFFF is a decision, not an accident.
5. Config over YAML. The bundle exposes picker options through config/packages/wandi_color_picker.yaml. Translate that into config/color-picker.php returning ['default_format' => 'hex', 'theme' => 'classic', 'allow_alpha' => false] and read it via config() in the component. Same extension point, Laravel-native.
6. Livewire. Wrap the widget in wire:ignore and re-init inside Livewire.hook('morph.updated'). Morphing the DOM under an initialized picker leaves a detached instance that still writes to a stale input — a classic silent data-loss bug.
7. Contributing back / debugging upstream. Clone, run its PHPUnit suite (expect KernelTestCase), and open the Twig template to see exactly which data-* attributes the JS binds to. Mirror those attribute names in the Blade component so the two stay behaviorally identical, then you can diff against the upstream asset when it changes.
extra.laravel.providers exists. Even if a provider were added, the bundle's Extension and Configuration classes only run inside Symfony's kernel. Don't try to make them load — the payoff for a color input is nil.old(). The bundle's template reads Symfony's form view state. Laravel's flash-and-repopulate model is different; empty values and null behave differently in particular. Rewrite the conditionals rather than transliterating them.symfony/form range before you assume composer require in a mixed-stack app is safe.Resources/public + assets:install (Assetic or Encore era). Laravel expects Vite with files under resources/. Don't attempt to wire public/vendor publishing; copy the JS/CSS into resources/, import in app.js, and treat it as a vendored dependency pinned in package.json.<input type="color"> might be enough. It's native, accessible-ish, zero-JS, and hex-only. If the bundle's picker adds nothing beyond a palette swatch row, you're paying a dependency for cosmetics. Test the native input first — it's your baseline for "is this worth a library."<input type="color">; verify the JS library's a11y (keyboard, screen reader labels) before committing to that.format: 'rgb' produces values your hex regex rejects. If you support alpha, the column grows to 9 chars and your CSS consumers need to handle #RRGGBBAA. Decide once and enforce in the rule.data-colorpicker attribute actually in the DOM — view-source, not devtools post-init? (2) did the JS bundle load — check the network tab for the entry chunk? (3) is the init selector querying a selector that a Blade {{ $attributes }} merge accidentally overwrote? The missing-data-attribute failure is by far the most common.composer.json / LICENSE before vendoring into a client project — this is a legal review item, not a nitpick.resources/js/vendor/ with a comment pointing at the source commit. Do not let composer update or npm update be your only supply-chain control.How can I help you explore Laravel packages today?