timothyasp/nova-color-field
A color picker field for Laravel Nova that adds an intuitive color selection input to your resource forms. Supports hex values and custom colors, integrates quickly into any Nova resource, and gives editors a clean, simple way to choose and store colors.
composer require timothyasp/nova-color-field
Before you wire it in, confirm two things:
composer show laravel/nova timothyasp/nova-color-field and, if you're on Nova 5, smoke-test the form field in a branch before committing.vendor/timothyasp/nova-color-field/src/ and confirm the namespace (commonly Timothyasp\Color\Color). The README snippet is the source of truth; a wrong use statement is the most common "it doesn't work" report.Then it's a normal Nova field:
use Timothyasp\Color\Color;
public function fields(Request $request): array
{
return [
ID::make()->sortable(),
Text::make('Name'),
Color::make('Brand Color')
->rules('required', 'regex:/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/')
->help('Hex value, e.g. #FF5733'),
];
}
Migration — size the column for hex, not for a color name:
$table->string('brand_color', 9)->nullable(); // #RRGGBBAA fits; 7 if you only allow #RRGGBB
First use case: a Settings/SiteTheme resource (or a brand_color on Team/Product) where an admin picks a hex value and the front end consumes it via a CSS custom property. That exercises the whole path — form input, DB storage, index display, Blade output — in about 20 minutes.
Treat it as a plain string field. The package stores a hex string; everything else is stock Nova. That means ->default('#0ea5e9'), ->nullable(), ->required(), ->help(), ->placeholder(), ->sortable(), ->onlyOnForms(), ->hideFromIndex() and ->rules() all behave exactly as they do on Text. Reach for those first before assuming the package needs to do more.
Normalize on the model, not in the field. Pickers can emit upper- or lower-case hex, or nothing at all. A mutator keeps the DB consistent and makes later where('brand_color', '#ff5733') queries sane:
protected function brandColor(): Attribute
{
return Attribute::make(
set: fn (?string $value) => $value ? strtolower(trim($value)) : null,
);
}
Render a swatch where the field can't. If the field doesn't render a proper index/detail component to your taste, keep the value visible with a displayUsing on a Text field, or just use the color field for the form and a text/HTML field for the index:
Text::make('Brand Color', 'brand_color')
->displayUsing(fn ($value) => $value
? "<span style=\"display:inline-block;width:1rem;height:1rem;border-radius:3px;background:{$value}\"></span> {$value}"
: '—')
->asHtml()
->onlyOnIndex(),
(Only do this because the hex is regex-validated — see the injection note below.)
Consume the value as a CSS variable rather than string-concatenating styles all over your views:
<div style="--brand: {{ $team->brand_color ?? '#0ea5e9' }}">…</div>
.btn-primary { background: var(--brand); }
Pairs well with: Image/File for logo + color, BelongsTo for per-tenant theming, and Flexible/Repeater (Nova 4) if you need a list of named swatches — the field itself is a standard form component and nests fine, but remember repeater contents serialize into a single JSON column, so you can't where() on them.
Defaults vs DB defaults. ->default() only pre-fills the create form. If records are also created outside Nova (seeders, imports, API), set a DB default or a model-level $attributes default too, or you'll have null colors in production while the form looked fine in staging.
string('brand_color', 7) and a future picker setting emits #RRGGBBAA, MySQL strict mode errors or silently truncates depending on config. Use 9 and validate against the formats you actually accept.'' rather than null. With nullable plus a regex rule, '' fails validation while null passes. Either add 'sometimes'/Rule::requiredIf logic or coerce in the mutator (?string + ?: null), as above.config/app.php if discovery is disabled), then clear Nova's compiled assets (php artisan nova:publish, php artisan optimize:clear) and check the browser console for a missing field.js. Nova caches aggressively in production builds.style attribute without validation. asHtml() + style="background: {$value}" is a CSS/HTML injection vector if the column can be written outside Nova. Keep a regex rule on the field and normalize in the model; consider a sprintf with a whitelist when rendering.component(), or vendor the FormField.vue into resources/js/fields/ and register your own Nova::script(). Don't build your theming system around options this package might never gain.$this->put(...) through a Nova action or assertDatabaseHas('teams', ['brand_color' => '#ff5733']), plus a validation test for a garbage value like red. That's where the actual bugs live (empty string, casing, column width).How can I help you explore Laravel packages today?