workup/nova-tailwind
A Tailwind CSS theme for Laravel Nova that replaces the default styling with a clean, utility-first look. Publishable config and assets let you customize colors, spacing, and components to match your admin panel.
What it solves. Nova ships its own purged Tailwind build — only the utility classes Nova's own components use. The moment you write a custom card, Tool Blade view, or a displayUsing() HTML string with flex gap-2 rounded-lg bg-white shadow, those classes simply don't exist. workup/nova-tailwind sidesteps that by loading a complete Tailwind stylesheet into the Nova panel so arbitrary utilities work in your own markup.
Minimal steps
composer require workup/nova-tailwindconfig/app.php (or bootstrap/providers.php) — read the repo's composer.json extra.laravel.providers block first.php artisan vendor:publish --tag=nova-tailwind (or --provider="Workup\NovaTailwind\..."), then php artisan nova:publish.app/Providers/NovaServiceProvider::boot():
Nova::style('nova-tailwind', __DIR__.'/../../vendor/workup/nova-tailwind/dist/tailwind.css');
Cmd+Shift+R) and confirm the file appears in <head> after nova.css.Where to look first
src/ service provider — that's where the asset registration actually happens (or doesn't).dist/ (or resources/css/) — confirm which Tailwind version the CSS was built with. This is the single most important compatibility detail.First use case. Put a class Nova doesn't ship into a custom Tool view and confirm it renders:
{{-- resources/views/vendor/nova/tools/my-tool.blade.php --}}
<div class="grid grid-cols-3 gap-4 p-6 rounded-xl bg-gray-50 dark:bg-gray-900">
<span class="text-xs font-semibold uppercase tracking-wide text-indigo-600">Revenue</span>
</div>
If tracking-wide and rounded-xl now take effect, the package is doing its job.
1. Custom Tools are the primary target. Tool views are plain Blade rendered inside Nova's layout — global CSS reaches them cleanly. Register the Tool normally and write markup with utilities you know Nova won't otherwise emit.
2. Custom Cards (Vue). Two competing approaches:
nova.mix / Vite setup). Then you don't need this package at all for that card.Pick one per card; mixing both is how you end up with 400 KB of duplicated utilities.
3. Inline HTML in resource definitions. Useful for Text::make('Status')->displayUsing(fn ($v) => "<span class=\"px-2 py-1 rounded-full text-xs bg-emerald-100 text-emerald-800\">{$v}</span>") and for HelpText where Nova renders HTML. Verify Nova isn't escaping the string before you debug CSS.
4. Custom dashboard / metrics layout. Wrap cards() output or a custom Dashboard view in a Tailwind grid to control column spans and responsive breakpoints that Nova's built-in grid doesn't expose.
5. Layering your own overrides. After the package loads, add a second Nova::style('nova-tailwind-overrides', ...) call in your NovaServiceProvider for org-specific tweaks — keeps the vendor file untouched and upgradeable.
6. Publish-then-override workflow. If the package supports it, publish its tailwind.config.js, set corePlugins: { preflight: false } and a prefix: 'tw-', rebuild, and register your build instead. This is the pattern most teams end up at.
Preflight will fight Nova. A "full Tailwind" build includes Preflight, which resets h1–h6, ul, button, input, img, and sets border-style: solid; border-width: 0 globally. Nova's own UI is Tailwind-based and already carries a preflight, so a second reset layered on top can visibly break forms, button padding, and heading sizes. If Nova looks subtly wrong after install, this is the cause 90% of the time. Fix: rebuild with preflight: false.
Load order is everything. Nova::style() assets are appended after Nova's core CSS. That means your full Tailwind can override Nova's own component classes (.btn, .card, .form-input) where specificity ties. Inspect the <head> and confirm ordering; if you need to lose, use a prefix instead of fighting it.
Version skew is the real risk. Last release March 2022 puts this in the Tailwind 3.0 / Nova 3–4 era. Nova 4 and 5 both moved Tailwind versions internally. Before adopting, diff the package's build against your Nova major — mismatched space-x-*/ring-* semantics and default color palette names (gray vs slate/zinc in TW3) will bite silently. Test on a staging Nova, not production.
Payload cost. "Full Tailwind" is ~3–3.5 MB uncompressed, roughly 250–350 KB gzipped, on every admin page load. Fine for an internal panel, not fine over a slow VPN on a dashboard that loads on every navigation. Consider a safelist build instead: run your own Tailwind with content: ['./resources/views/**/*.blade.php', './nova-components/**/*.vue'].
Dark mode won't "just work" unless the config matches. Nova toggles a dark class; the package's build must use darkMode: 'class' (not 'media'). Test both themes — missing dark: variants are the most common post-install complaint.
Cache busting. A dist/tailwind.css registered by a stable path gets cached aggressively by browsers. If you publish-and-edit, append a version query string or use a hashed filename, otherwise you'll debug CSS that the browser never re-fetched.
Don't reach for the CDN. https://cdn.tailwindcss.com in a Nova layout breaks CSPs, requires network access on every admin load, and produces flash-of-unstyled-content. Use the package's static file.
Debugging checklist when a class "doesn't work":
<head>? (document.styleSheets)grep -c 'tracking-wide' tailwind.cssExtension points to plan for. You'll almost certainly want (a) prefixing, (b) preflight off, (c) your own content paths. If the package doesn't expose a config file, budget an afternoon to fork it — the service provider is likely a handful of lines, and a five-line provider calling Nova::style() with your own compiled CSS is a lower-risk long-term bet than an unmaintained 0-star dependency. The problem it addresses is real and worth solving; the package is worth auditing first.
How can I help you explore Laravel packages today?