awcodes/palette
A Filament Forms color picker field that uses preset color palettes. Stores selections as structured arrays or keys, supports Filament 4.x and 5.x, and offers publishable config for global customization.
Architecture fit. Palette is a Filament form-field package, not a platform component. It ships two form fields (ColorPicker, ColorPickerSelect), one infolist component (ColorEntry), a publishable config, and Blade views styled with Tailwind utilities. It registers nothing outside Filament's field lifecycle — no routes, no migrations, no boot-time service provider work beyond view/config registration, no queue or auth surface. That is the correct blast radius: the worst case is an unstyled or erroring form control, not an outage.
The corollary matters for framing: this is a component procurement decision, not an architecture decision. Do not convene an architecture review, do not add it to the ADR log, do not spend a sprint gate on it. Approve it as a standard library, assign an owner, move on.
Integration feasibility. Low. composer require awcodes/palette, optionally publish config, optionally add a Tailwind @source directive, cast the column as array, declare the field. Realistically one engineer-half-day for a first integration including styling verification — plus a design conversation about palette curation that will take longer than the code.
Two friction points are easy to underestimate:
@source '../../../../vendor/awcodes/palette/resources/**/*.blade.php' in a custom theme or app CSS. This makes the component's appearance a function of the asset build pipeline, not of Composer. A theme refactor, a Filament upgrade that reshuffles theme scaffolding, or a build-tool swap strips the picker's styles with no error and no Composer signal. This is the failure mode most likely to reach a user.key, property, label, type, value), so the column must be cast array or json. A missing cast produces a conversion error on submit — a hard 500 mid-form, discovered only if someone exercises that path in staging.Technical risk.
storeAsKey() scalar is decided in one line of a form definition — exactly where consequential decisions get made accidentally. Reversing it later is a data migration across every table holding a color column, plus every read path and every ColorEntry. Default to the rich array. It lets you re-label, re-shade, and correct hex values without migrations, and the payload is trivial.shades() only applies to Filament Color objects; raw hex or class-string entries (e.g. 'salmon' => '#fa8072', 'bg-gradient-secondary' => 'bg-gradient-secondary') silently ignore a shade. Nothing errors — the swatch just renders the wrong value. Same class of issue as v3.2.0's fix for storeAsKey(false) and ColorEntry key resolution: correct behavior depended on a patch release.Key questions to answer before sign-off.
Stack fit. Native. This is a purpose-built Filament field; there is no adapter, no wrapper, and no impedance mismatch with Livewire form state. Adoption is a one-line Composer require for any team already on Filament 3.x+ with a custom theme.
Migration path. Greenfield: no migration. Existing freeform color columns: additive only — introduce the Palette field on new or low-traffic forms first, keep the freeform picker behind a feature flag until the palette set is stable, then backfill. Because default storage is a JSON array and the legacy column is almost certainly a hex string, existing columns cannot be reused in place; plan either a new column plus a read-time fallback, or a one-shot data migration that maps hex → nearest palette key. Do not attempt an in-place cast change.
storeAsKey() is the cheaper migration target, but only if you are confident the palette will not churn. If palette curation is still unsettled, key-only storage means every palette edit is a potential orphaned reference.
Compatibility. Pin to the 3.x line and require ≥ 3.2.0 — that release fixed storeAsKey(false) honoring and ColorEntry resolving stored keys, both of which are data-integrity adjacent. Verify against your exact Filament minor version rather than the README matrix. Note the hard prerequisite: a custom Filament theme. Teams on a stock panel that have never scaffolded a theme will hit this on day one, and it is a bigger lift than the package itself.
Sequencing.
awcodes/palette ≥3.2.0, install, publish config only if defaults need changing.@source directive and verify swatch styling in a built asset bundle, not just dev.ColorEntry for both a valid and a removed key.Maintenance. Near-zero runtime footprint — no migrations, no scheduled tasks, no background work, no queue, no external service. The recurring cost is version upkeep: roughly a bump per Filament major, plus the occasional patch. If the config is published, that file becomes your local fork surface and must be kept in sync manually on upgrade. Budget a few hours per Filament major release, not per sprint.
Support. Internal tier-1 is thin, because the failure surface is narrow and diagnosable from symptoms (unstyled picker → @source; submit error → missing cast). Tier-2 requires a Filament-comfortable engineer. External support does not exist — there is no vendor, no paid tier, no SLA. Issues are GitHub-only, single-maintainer, best-effort. That is acceptable for a form field and unacceptable if it ever becomes load-bearing for user-facing color configuration across the product.
Scaling. Negligible at runtime — the picker renders server-side Blade plus a small Livewire interaction. The only real dimension is swatch count: a full Tailwind shade grid (22 families × 11 shades) is ~240 DOM nodes per field instance, multiplied across a form with several color fields. Keep curated palettes to roughly 10–30 entries. CSS bundle size grows slightly via the @source scan; measure once after the first production build and stop worrying about it.
Failure modes, ranked by likelihood-to-reach-a-user.
| Mode | Signal | Blast radius | Mitigation |
|---|---|---|---|
@source lost in theme refactor/upgrade |
Picker renders unstyled, no error | Every form using it | Visual regression check in CI on a form fixture |
Missing array/json cast |
500 on form submit | Single form, blocks submission | Include the field path in staging smoke tests |
| Palette edited, stored key orphaned | ColorEntry shows stale or blank |
Historical records, silent | Treat palette keys as append-only; never delete, only deprecate |
| Filament major upgrade | Field fails to render or validate | All usage | Pin Filament; upgrade Palette in the same PR |
| Maintainer abandonment | No releases for a Filament major | Long-term | Fork readiness check; one engineer must be able to read the source |
shades() on a non-Color entry |
Wrong shade renders, silently | Cosmetic, but ships wrong data | Lint/review rule: shades only on Color::* entries |
Ramp-up. Low. The README is sufficient for a Filament-fluent engineer — roughly an hour to first working field. The friction is entirely in prerequisites, not in the API: custom theme setup, the @source path (which is relative and easy to get wrong), and the model cast. Write a short internal runbook covering those three and onboarding cost drops to near zero for subsequent forms.
Verdict. Approve as a standard library with a named owner, pinned at ≥3.2.0, rich-array storage by default, and an append-only palette policy. Reject only if the requirement is runtime-administered palettes or if the team has no Filament-internals coverage and cannot accept an unretirable single-maintainer dependency.
How can I help you explore Laravel packages today?