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 Swatches Laravel Package

ynacorp/nova-swatches

A Laravel Nova field that lets you display and select colors as visual swatches, making color attributes easier to manage in Nova resources with a clean, compact admin UI.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish — then verify the API against the source before you write a line of application code.

composer require ynacorp/nova-swatches
php artisan nova:publish
php artisan view:clear

This is a Nova field package: no migrations, no config file, no provider you register by hand in a standard Nova app. nova:publish is the step that gets skipped and the one that produces the classic "blank field in staging, works locally" bug.

Before you trust the README, spend five minutes in vendor/ynacorp/nova-swatches:

# Real namespace (PSR-4)
grep -A4 '"psr-4"' vendor/ynacorp/nova-swatches/composer.json

# Your actual fluent API surface
ls vendor/ynacorp/nova-swatches/src/ && sed -n '1,200p' vendor/ynacorp/nova-swatches/src/*.php

# Vue props: each withMeta() key maps 1:1 to a PHP setter
ls vendor/ynacorp/nova-swatches/resources/js/

Also read the laravel/nova constraint in that same composer.json — it is the single most important line in the package, because a Nova field is only a stable contract within a Nova major. The snippets below assume a conventional field class with a palette setter; substitute whatever the source actually exposes.

First use case — one nullable hex column on one low-traffic resource.

// migration
$table->string('accent_color', 9)->nullable();
// app/Nova/Project.php
use Ynacorp\NovaSwatches\NovaSwatches;

public function fields(NovaRequest $request)
{
    return [
        NovaSwatches::make('Accent Color', 'accent_color')
            ->options([
                '#0F172A' => 'Slate',
                '#2563EB' => 'Blue',
                '#16A34A' => 'Green',
                '#DC2626' => 'Red',
                '#F59E0B' => 'Amber',
            ])
            ->rules(['nullable', 'string', 'regex:/^#([0-9a-f]{3}|[0-9a-f]{6})$/i']),
    ];
}

Ship that on one resource, then confirm the value round-trips through create, detail, and index views before rolling it out. If the field renders blank, stop and fix assets (php artisan nova:publish && php artisan view:clear) before debugging anything else — it is almost never your code.

Implementation Patterns

Put the palette in a PHP constant, never inline in the field call. The palette is design-system data: validation, seeders, factories, API resources, and Blade partials all need it.

// app/Support/Palette.php
final class Palette
{
    public const BRAND = [
        '#0F172A' => 'Slate',
        '#2563EB' => 'Blue',
        '#16A34A' => 'Green',
        '#DC2626' => 'Red',
        '#F59E0B' => 'Amber',
    ];

    public static function keys(): array
    {
        return array_keys(self::BRAND);
    }

    public static function label(?string $hex): ?string
    {
        return $hex ? (self::BRAND[$hex] ?? null) : null;
    }
}

Enforce the palette server-side with Rule::in(). The swatch UI is a suggestion; the rule is the enforcement. Without it, the field is just a hex-paste endpoint with a pretty face.

use Illuminate\Validation\Rule;

NovaSwatches::make('Accent Color', 'accent_color')
    ->options(Palette::BRAND)
    ->rules(['nullable', Rule::in(Palette::keys())]),

Wrap the vendor class behind an internal adapter. One file, one swap point, and no resource ever imports the vendor namespace. Given this package's maintenance profile (22 stars, zero dependents, no upstream activity), treat the adapter as mandatory hygiene:

// app/Nova/Fields/Swatch.php
namespace App\Nova\Fields;

use App\Support\Palette;

class Swatch extends \Ynacorp\NovaSwatches\NovaSwatches
{
    public function __construct($name, $attribute = null, ?callable $resolveCallback = null)
    {
        parent::__construct($name, $attribute, $resolveCallback);
        $this->options(Palette::BRAND);
    }
}

A fork, a reimplementation, or a removal then touches exactly one file.

Use it for enumerated metadata, not free-form color. It works best when the stored value is really an enum: status labels, category tags, product variants, content accents. Resolve the human label from the same constant:

// app/Models/Project.php
public function getAccentLabelAttribute(): ?string
{
    return Palette::label($this->accent_color);
}

On the index view, show the label (or a small color chip via a computed field) rather than a raw hex string — that is the payoff that makes the field worth installing.

Reuse across resources with a trait or base-resource helper, not copy-paste:

protected function colorMeta(string $name = 'Color', string $attribute = 'color'): array
{
    return [
        Swatch::make($name, $attribute)
            ->rules(['nullable', Rule::in(Palette::keys())]),
    ];
}

Seed and test the constraint. Factories should pick from the palette, and one feature test posting an off-palette value is worth more than any amount of manual clicking — it's the only artifact that survives a Nova upgrade:

// database/factories/ProjectFactory.php
'accent_color' => fake()->randomElement(\App\Support\Palette::keys()),
public function test_rejects_off_palette_color(): void
{
    $this->actingAs($this->admin())
        ->postJson('/nova-api/projects', [
            'name' => 'Demo',
            'accent_color' => '#ABCDEF',
        ])
        ->assertStatus(422);
}

Chain dependsOn() deliberately, then verify visually. Nova's reactive re-render is the usual failure mode for custom Vue fields that cache props on mount. Test the toggle yourself; if the palette doesn't refresh, gate the field with onlyOnForms() instead of fighting the reactivity.

Gotchas and Tips

The namespace/repo mismatch is a supply-chain question, not a typo. The package is ynacorp/nova-swatches but publishes from triolla-io/nova-swatches. Confirm who controls the Packagist namespace before this lands in composer.lock, then pin an exact tag ("ynacorp/nova-swatches": "1.0.0", not ^1.0). Unmaintained packages are exactly the ones you don't want floating.

Nova major drift is the real risk. Nova 4 → 5 moved to Vue 3 with revised asset registration. Check whether dist/ is a compiled artifact checked into the repo — if so it has no dependency-update surface at all, and a Nova upgrade means rebuilding the front-end yourself. If the constraint pins ^4.0 and you're on 5, you have inherited a Vue component.

Normalize before persisting — decide once, enforce everywhere. Pick #RRGGBB, lowercase, empty → null. A saving hook or an Attribute cast keeps exports, CSS interpolation, and downstream comparisons from silently disagreeing. Use a string(9) column as a cheap hedge for future alpha.

Debugging a broken field, in order: (1) php artisan nova:publish + view:clear; (2) confirm the class resolves (namespace mismatch between README and src/); (3) open the browser console — a JS error in the compiled component usually shows there; (4) disable the field on one resource to rule out global CSS bleed.

Asset publishing belongs in your deploy script, next to config:clear and view:clear. Stale compiled assets are the #1 cause of "works on my machine" for custom Nova fields.

Accessibility is unvalidated — test it yourself. Color pickers are a known failure point for keyboard navigation, focus management, and selected-state announcements. Tab through the palette and complete a selection without a mouse. Admin a11y regressions are quiet and never reported by users.

Don't assume sortable() is meaningful. Swatches store a value, not a rank. Sorting by hex is rarely what anyone means; if you need ordered palettes, add a separate integer column and sort on that.

Watch for global CSS and CSP interaction. Custom fields sometimes inject global styles or inline style attributes. If unrelated admin pages look subtly wrong after install, or a strict CSP blocks inline styles, isolate by disabling the field on one resource first.

Assign an owner and a calendar reminder. There is no vendor support and effectively zero upstream maintenance; the burden transfers to whoever adopts it. Write the expectation into the adapter's docblock and review the package against Nova release notes each upgrade cycle — otherwise it gets rediscovered in eighteen months by someone debugging a blank field in production.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums