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

Laravel Nova Theme Classify Laravel Package

gregoriohc/laravel-nova-theme-classify

A custom theme for Laravel Nova that gives your admin panel a cleaner, more organized look. Drop-in install restyles navigation, cards, and resources with a classified, minimalist layout built for fast, readable dashboards.

View on GitHub
Deep Wiki
Context7

Getting Started

Understand what you're actually installing. Despite the description ("custom theme package that gives your Laravel Nova admin panel a distinct, polished look"), the code is a theme classifier: it resolves which Nova theme is active and exposes it as a class you can scope CSS and Blade against. Do not expect a palette, typography, or spacing overhaul to appear after composer require. Read src/ and composer.json first — the whole package is tens of lines and one service provider, and the Composer constraints are your only truthful compatibility signal. There has been no release since 2019-06-08 and it targets the Nova 1.x/2.x era, so on Nova 4/5 or PHP 8.2+ assume it does not boot until you prove otherwise in a spike.

Spike on a throwaway branch, not in your app.

git checkout -b spike/nova-theme-classify
composer require gregoriohc/laravel-nova-theme-classify

Auto-discovery should register the provider. If discovery is disabled or you're on an older Laravel, add it manually in config/app.php:

Gregoriohc\LaravelNovaThemeClassify\LaravelNovaThemeClassifyServiceProvider::class,

Then confirm the provider boots and the admin panel still renders. A boot failure here takes down the entire Nova admin, so this is the first gate — not a formality.

Publish the stub, because the config is the API surface:

php artisan vendor:publish --provider="Gregoriohc\LaravelNovaThemeClassify\LaravelNovaThemeClassifyServiceProvider"
php artisan config:clear

Where to look first, in order:

  1. composer.json — Nova/Laravel/PHP constraints against your pinned laravel/nova.
  2. src/ — the service provider plus any helper/facade. Five minutes of reading beats searching for issues on a 17-star repo.
  3. The published config stub — this is where theme → class mapping lives.
  4. The rendered DOM of a Nova page — confirm the class is actually emitted where you expect before you style anything.

First use case: pick exactly one custom Nova card or tool that needs theme-aware styling. Inspect the DOM, verify the body class is present on that page, wire one scoped rule against it, and confirm it flips when you change the theme. Once that loop works, spread the pattern to other cards. Do not start by theming the whole admin panel.

If you're on Nova 4/5, skip the install and copy the pattern in-house: a config map of theme → classes, a resolver bound in the container, resolved once in a Nova::serving callback, emitted as a body class. That's an afternoon of work and it's yours to maintain.

Implementation Patterns

Keep the mapping in config, not in Blade. The config file is the seam you'll actually touch day to day:

// config/theme-classify.php
return [
    'default' => 'light',
    'themes'  => [
        'light' => ['body' => 'theme-light', 'card' => 'bg-white'],
        'dark'  => ['body' => 'theme-dark',  'card' => 'bg-gray-900'],
    ],
];

When someone asks for a tenant-specific accent, this is the file you edit — one entry — rather than grepping views.

Resolve once per request, not per component. Do resolution in the service provider or a Nova::serving callback and emit the value a single time; read it back from a container binding or helper. Calling resolution inside every card view is how you get two different classes on the same page — and with a package this small there is no internal caching you can lean on.

Wrap the package behind your own helper. Even if you keep the dependency, add one indirection so a fork or reimplementation swaps a single file:

// app/Support/theme.php
function theme_body_class(): string { /* delegate to package */ }
function theme_class(string $key): string { /* theme lookup */ }

Then Blade and your tools call theme_class('card') and never reference the package namespace directly. This single decision is what makes vendoring cheap later.

Scope CSS to the emitted body class, and prefer custom properties over duplicated rules. Naive theming re-declares every component per theme; that's how you end up with hundreds of rules and a specificity problem. Instead:

body.theme-dark  { --panel-bg: #111827; --panel-fg: #e5e7eb; }
body.theme-light { --panel-bg: #ffffff; --panel-fg: #111827; }
.card-panel { background: var(--panel-bg); color: var(--panel-fg); }

Adding a third (tenant/brand) theme is then a handful of variables, not a new rule set — which is the whole reason the classifier earns its keep.

Consume the same resolver inside custom tools and cards. Your custom Nova tool that renders its own wrapper should call theme_class(...), not hardcode classes. That guarantees the tool and the body agree, and it's the seam that makes per-tenant branding tractable.

Compose with Nova's own Tailwind classes; don't fight them. The package classifies and emits a class — it does not re-render Nova's shell. Apply scoped classes to your own cards, tools, and fields, and leave Nova's compiled styles alone.

Server-rendered theme selection is the clean path. If the active theme comes from config, a user preference, or a tenant, resolve it server-side so the body class is correct on first paint. Reach for a JS shim only if you genuinely need an instant client-side toggle without a round trip.

Gotchas and Tips

  • Composer constraints are the first failure point. A 2019 theme helper will pin or conflict on laravel/nova, Laravel, or PHP. Pin exact versions, keep the lockfile reviewed, and treat any conflict as an upgrade blocker — a cosmetic package that blocks a platform upgrade is a bad trade.
  • Provider boot failure kills the whole admin panel. Always spike on a disposable branch or isolated staging, and have a one-command rollback (composer remove + restore composer.lock) before this touches production.
  • Server-side classification vs. client-side toggles. If users flip light/dark in the browser without a round trip, the emitted class goes stale. Either persist the preference server-side (cookie/session/DB column) or accept a JS shim — and if you accept the shim, you've partly given up the "no JavaScript" benefit you adopted this for.
  • Not every Nova page emits your class. Login screens, error pages, and Nova's own layouts may not render the body class you expect. Check each page you intend to style; don't assume a global body.theme-* covers everything.
  • Specificity wars with Nova's compiled shell. Nova's Tailwind has specific, sometimes !important-adjacent rules. Scope under your body class and use variables rather than escalating selector weight — otherwise a Nova minor bump silently breaks your theming.
  • Config cache is the usual "config quirk." After publishing or editing config, run php artisan config:clear. Remember production caches config, so a deploy needs the same step.
  • Breakage is silent and visual only. No exception is thrown when a theme class stops matching — the admin just looks wrong, and backend monitoring will never catch it. Add admin-UI visual regression snapshots, or at minimum an explicit post-deploy smoke check on a themed page.
  • Debugging order when the class is missing: (1) is the provider registered / auto-discovery working? (2) is config cached and stale? (3) does this page even use the layout that emits the class? Then read src/ — five minutes beats issue archaeology.
  • Extension points are effectively the published config and the service provider. There is no meaningful plugin surface. "Extending" this package means "owning" it.
  • Zero dependents means zero compatibility pressure. No ecosystem keeps this working across Nova/PHP versions; every upgrade is yours alone. That's the argument for forking under an internal namespace with a named owner, or reimplementing the ~30 lines yourself — a config map, a container-bound resolver, one Nova::serving callback, one body class.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect