Product Decisions This Supports
- Whether to ship a "brand color" / "theme accent" selector at all, and in what form. Palette changes the product question from "should users pick any color?" to "should users pick from a curated set?" That is a product decision, not a technical one, and it usually resolves in favor of curation: preset swatches eliminate unreadable contrast, off-brand hues, and support tickets about a client who chose neon yellow on white.
- Feature scoping for any admin-facing customization surface. Concretely: tenant branding, category/tag color coding, status badge colors, calendar or kanban lane colors, label/tag systems, report accent colors. Each of these is a "pick a color" story, and each becomes a ~1-day story instead of a design-and-build-a-picker story.
- Build vs. buy, resolved as buy. The freeform alternative is Filament's native color picker — but the native field gives an arbitrary hex value with no palette governance, no labels, and no semantic key. Building the curated version in-house is a form field, a Blade view, a Tailwind-aware style hook set, and an infolist display component. Buying is correct here; the package is MIT and small enough that a fork is a weekend, not a rewrite.
- Data model / roadmap commitment on how color is stored. This is the one decision with real downstream weight. Storing the rich array (
key, property, label, type, value) lets you re-label a palette entry, adjust a shade, or correct a hex value later without a migration. Storing only the key (storeAsKey()) is leaner but means every color change is a data migration plus every read path plus ColorEntry. Default recommendation for the roadmap: rich array, and treat storeAsKey() as an opt-in for high-volume tables where the payload actually matters.
- Design-system governance. Adopting Palette is a lightweight forcing function: the palette becomes a declared, reviewed artifact (
->colors([...]) in a form definition or the published palette.php config) rather than an emergent set of hex codes scattered across the app. That is a tangible win for design consistency that is easy to under-sell internally.
- Read-only display parity.
ColorEntry for infolists means the "edit experience uses presets, view experience shows a labeled swatch" story is complete out of the box — no follow-on ticket to build a display component.
When to Consider This Package
Adopt when all of these hold:
- The product runs on Filament 3.x, 4.x, or 5.x and the color input lives inside a Filament form. This package is narrow by design; outside Filament forms it has no value.
- The product wants curated choice, not freeform. If a designer or brand owner can define the acceptable set, presets are strictly better UX than a wheel.
- There is one engineer who can read Filament internals available to own it. This matters because of the bus factor: every release from v1.0.0 through v3.2.0 is authored by a single maintainer, with 25 stars and zero dependents — no production ecosystem pressure-testing. MIT makes a fork legally trivial, but a fork only mitigates risk if someone can maintain it. If that person does not exist, this is an unretireable tail risk.
- The team accepts a Tailwind build-pipeline dependency. Rendering requires a
@source directive pointed at the package's Blade views in a custom theme or app CSS. A theme refactor, a Filament upgrade that reshuffles scaffolding, or a build-tool swap can strip the component's styles silently — no Composer signal, no error, just a broken-looking picker. This is the most likely failure to reach a user and should be covered by a visual check in staging.
- Someone will exercise the form path in staging before release. Default storage is a structured array; the model column must be cast
array or json. A missing cast throws on submit — a hard 500 mid-form, invisible until that path is actually tested.
Look elsewhere, or build, when:
- The product is not on Filament (Livewire-only, plain Blade, Vue/React admin, Inertia). The package's entire value is its Filament field integration.
- The requirement is a freeform picker — eyedropper, arbitrary hex, opacity/alpha, gradient builder. Palette is deliberately the opposite; forcing it creates a worse experience than the native field.
- The app needs color at high volume or in hot read paths, where the structured array payload is genuinely wasteful and
storeAsKey() plus a lookup table is the better shape.
- Supply-chain review is strict (single-maintainer packages rejected on policy). Note the version matrix is inconsistent between the README and the release notes — the README implies 3.x covers Filament 4.x and 5.x, while releases describe v3.0.0 as "v5 support" and v2.0.0 as "v4 support." Pin by Composer constraint, never by prose, and verify against the installed Filament major.
- The need is broader than a field — a full theming subsystem with palette versioning, per-tenant overrides, and audit history. Palette is a form control; the governance layer would still be yours to build.
How to Pitch It (Stakeholders)
For executives — the one-liner: This is a small, free (MIT), open-source form control that lets users pick brand-approved colors from a curated palette instead of an arbitrary color wheel. It removes a class of design-consistency and accessibility problems before they reach customers, and it turns every "let users pick a color" request on the roadmap into a roughly one-day story.
The business case in three points:
- Consistency by construction. Presets replace freeform choice, so customers cannot produce off-brand or unreadable results. That is fewer support tickets and fewer design-review cycles.
- Cheap and reversible. It is a Composer dependency with no routes, no migrations, no background jobs, and no auth surface. Worst case is a form control that renders unstyled — not an outage. A fork is a weekend if we ever need one.
- It closes the loop. Editing uses presets; the read-only infolist view shows a labeled swatch. Both halves ship together, so there is no orphaned follow-on ticket.
For engineering — the honest summary: ~200 lines of concern, a publishable config, and low integration cost (realistically an engineer-half-day including styling verification). Two things to get right and neither is hard, but both are easy to underestimate:
- Add the Tailwind
@source directive for the package views in the custom theme. The component's appearance depends on the asset build, not on Composer — so a theme refactor can silently break styling. Add a visual smoke check to staging.
- Cast the column as
array (or json) if we take the default structured storage. A missing cast is a 500 on submit.
One decision to make deliberately rather than by accident, because it is a one-way door: rich array vs. storeAsKey(). We recommend the rich array — it lets us re-label, re-shade, and correct colors without touching the database. Reversing this later means a data migration across every table with a color column plus every read path.
What we are accepting: single-maintainer bus factor, 25 stars, zero dependents. We mitigate by pinning the Composer constraint to the installed Filament major (the README and release notes disagree about the version matrix — trust Composer, not prose) and by naming an internal owner who can read Filament internals. Ask: approve this as a standard library, assign that owner, and skip architecture review — it does not warrant an ADR or a sprint gate.