Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Color Field Laravel Package

outl1ne/nova-color-field

outl1ne/nova-color-field is a Laravel Nova color picker field. Add color selection to your resources with preset palettes, a custom picker, and hex value storage. Swatches render cleanly in forms and detail views, making color management simple and consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

Install.

composer require outl1ne/nova-color-field

Package discovery handles registration — nothing to add to config/app.php, and no provider of your own to write. If the package exposes a publishable asset tag, run it once and wire that command into your deploy pipeline (see Gotchas); otherwise Nova loads the compiled component itself.

Where to look first. Before writing any resource code, open vendor/outl1ne/nova-color-field/ and read the field class and the compiled component registration. Two questions to answer in five minutes: what attribute name the Vue component emits, and what asset pipeline (Vite vs legacy Mix) it targets. Those two facts determine everything about how it behaves on your Nova major.

Give the column somewhere to live. The field persists a single string:

Schema::table('settings', function (Blueprint $table) {
    $table->string('brand_color')->nullable()->after('logo_path');
});

string + nullable, no cast. Resist char(7) — you'll want null for "unset" and you'll want room for 3- and 8-digit hex during prototyping.

Render it. One line in the resource:

use Outl1ne\NovaColorField\Color;

public function fields(NovaRequest $request)
{
    return [
        Color::make('Brand Color', 'brand_color'),
    ];
}

Load the index, open a record, pick a color, save. If the picker renders and #1a2b3c round-trips to the database, installation is done — every remaining decision is product, not plumbing.

First real use case: a brand token, read back as a CSS variable.

// layout Blade / view composer
<style>:root { --brand: {{ $settings->brand_color ?? '#0ea5e9' }}; }</style>

Do this on one low-traffic settings screen before touching anything else. It exercises render, persistence, and read-back in a single loop, and it's the shape most later usages will copy.

Implementation Patterns

Validation is yours to write. Always.

Color::make('Accent Color', 'accent_color')
    ->rules('required', 'string', 'regex:/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/')
    ->help('Hex only, e.g. #0ea5e9');

The picker constrains a mouse; it does not constrain a request. If the attribute is writable anywhere else — API, importer, seeder, another resource — mirror the rule in a FormRequest or a Rule object so the field is never the sole contract on stored data.

Normalize once, in one place. Lowercase, expand 3-digit to 6, strip a stray #, convert '' to null. Put it in a model mutator or an observer — not in each resource, and not in each controller action:

public function setBrandColorAttribute($value): void
{
    $this->attributes['brand_color'] = $value
        ? strtolower(Str::length($value) === 4
            ? '#'.$value[1].$value[1].$value[2].$value[2].$value[3].$value[3]
            : $value)
        : null;
}

Defaults vs. nulls — decide per field, not per column. Where a value is genuinely required, pair ->rules('required') with ->default('#0ea5e9'). Where it isn't, let null through and treat it as "inherit the theme" at render time. Don't paper over a missing value with a sentinel color in the database; you'll never be able to tell "unset" from "somebody chose white."

Tame the index view. A full picker in every table cell is noise. Either hide it (->hideFromIndex()) and render a small swatch via ->displayUsing(fn ($v) => $v) plus CSS, or keep it only where the list genuinely benefits (->onlyOnDetail() is the blunt instrument). Keep fields() intact for the form and detail views.

Multiple colors means multiple fields. This is one hex string per attribute. Primary/secondary/accent is three columns and three Color::make() lines. Do not reach for a JSON column and expect the picker to serialize an array — it won't.

Wrap it behind your own class before it spreads.

namespace App\Nova\Fields;

use Outl1ne\NovaColorField\Color;

class BrandColor extends Color
{
    public function __construct($name, $attribute = null, $resolveCallback = null)
    {
        parent::__construct($name, $attribute, $resolveCallback);

        $this->rules('nullable', 'string', 'regex:/^#[0-9a-fA-F]{6}$/')
             ->help('Hex, e.g. #0ea5e9');
    }
}

Resources reference App\Nova\Fields\BrandColor. A Nova breaking change, a fork, or a swap back to a plain Text field with a regex becomes a one-file edit instead of a grep across every resource.

Settings screens are the natural home. Most real usage isn't a CRUD resource — it's a Nova settings page or a singleton model. The field nests fine inside a Panel or a custom Fields grouping alongside toggles and text inputs; load the model, render the fields, save.

Test at the boundary, not in Vue. A unit test on the regex and a feature test that POSTs #0ea5e9 to the update endpoint and asserts the stored value covers ~95% of your risk. Assert 200 and the persisted string; skip component-level tests for a commodity input.

Gotchas and Tips

  • Assume zero server-side validation. A picker that "only allows hex" is a client-side promise. A request that never touched the picker can send url('javascript:...') or a 2KB string. Your request/model rules are the actual contract.
  • Hex only, one value. No alpha, no gradients, no arrays. If you need translucency, store the hex and compose alpha at render time with CSS (color-mix(), a scoped variable) rather than trying to persist rgba(...).
  • The failure mode is silent. Nova field packages ride Nova's asset pipeline. When a Nova upgrade breaks the component you get a blank input, not an exception — CI stays green and the bug reaches a demo. Add "open one color field, pick, save" to the Nova upgrade checklist, and verify against your exact Nova version in staging, not just the composer.json constraint.
  • Asset publishing is a deploy step. If you publish vendor assets, add the publish + view:clear + optimize:clear sequence to the deploy script. After composer update, clear config/views if the field renders stale, and hard-refresh the Nova frontend before you start debugging anything else.
  • Pin the version. Single maintainer, low stars, MIT. Use an exact or ~ constraint, commit the lock file, and write down the fork-and-own fallback (owner + "vendor into App\Nova\Fields") before you need it. It's an afternoon under MIT; discovering that under pressure is worse.
  • Clean the existing data once. Legacy '', mixed case, and 3-digit hex render oddly against a nullable column. Run one pass: normalize case, expand short hex, convert empty strings to null.
  • Rollback is a one-line revert. Swap Color::make() for Text::make() with a hex regex; the column and its data stay. No migration to unwind — that reversibility is precisely what makes trying it cheap. Keep the fallback definition in the resource's history.
  • Don't extend it to customer-facing surfaces without a rethink. Internal admin swatch: adopt. Tenants picking brand colors that land in emails, PDFs, or public pages: you need validated input, previews, and fallbacks the picker doesn't provide. State that boundary now so "we already have a color field" doesn't quietly cover a customer surface later.
  • All extension points are on your side. Custom rules(), model mutators for normalization, a wrapping field class, and a CSS-variable read layer. There's no meaningful hook inside the package to customize the picker itself — don't plan around one.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console