惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
Project Zero
Project Zero
K
Kaspersky official blog
G
Google Developers Blog
T
Threat Research - Cisco Blogs
T
The Blog of Author Tim Ferriss
Cyberwarzone
Cyberwarzone
Y
Y Combinator Blog
Recorded Future
Recorded Future
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Cisco Talos Blog
Cisco Talos Blog
Latest news
Latest news
Microsoft Security Blog
Microsoft Security Blog
H
Help Net Security
S
Schneier on Security
P
Palo Alto Networks Blog
H
Hacker News: Front Page
N
News and Events Feed by Topic
N
Netflix TechBlog - Medium
博客园 - Franky
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
SecWiki News
SecWiki News
Cloudbric
Cloudbric
TaoSecurity Blog
TaoSecurity Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Hacker News
The Hacker News
C
Check Point Blog
L
LangChain Blog
腾讯CDC
小众软件
小众软件
T
Tenable Blog
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
L
LINUX DO - 最新话题
A
About on SuperTechFans
Google Online Security Blog
Google Online Security Blog
C
Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Vercel News
Vercel News
雷峰网
雷峰网
美团技术团队
D
DataBreaches.Net
Martin Fowler
Martin Fowler
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Full Disclosure
博客园_首页

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Understanding Filament themes in v4/v5: from Colors to custom CSS
yebor974 · 2026-06-26 · via DEV Community

Filament provides a flexible theming system that lets you customize the look and feel of your admin panel. This guide covers how to create and apply custom themes for Filament v4/v5 with Tailwind CSS v4.

This is an updated version of an earlier guide written for Filament v3 and Tailwind v3. The overall approach is similar, but what the theme command generates and the CSS syntax have changed with Tailwind v4's CSS-first configuration. If you're still on Filament v3, the original guide is available here.

Setting primary colors for panels

Before touching the theme files, the simplest customization is setting your panel's color palette. This part hasn't changed: Filament still uses the Filament\Support\Colors\Color class.

Option 1: define colors per panel

In your panel provider:

use Filament\Support\Colors\Color;

public function panel(Panel $panel): Panel
{
    return $panel
        ->colors([
            'danger' => Color::Rose,
            'gray' => Color::Gray,
            'info' => Color::Blue,
            'primary' => Color::Indigo,
            'success' => Color::Emerald,
            'warning' => Color::Orange,
        ]);
}

Option 2: define colors globally

In AppServiceProvider, applied across all panels:

use Filament\Facades\Filament;
use Filament\Support\Colors\Color;

public function boot(): void
{
    Filament::defaultColors([
        'danger' => Color::Rose,
        'gray' => Color::Gray,
        'info' => Color::Blue,
        'primary' => Color::Indigo,
        'success' => Color::Emerald,
        'warning' => Color::Orange,
    ]);
}

When to stop here: if your goal is only to change branding colors, ->colors() is all you need. Create a custom theme only when you need structural UI changes, like repositioning elements, changing spacing, or styling parts of the interface that color tokens don't reach.

Generating the theme

The command itself hasn't changed:

php artisan make:filament-theme

You'll be prompted for a panel name (default: admin). What changed is what this command generates and does for you.

On a Tailwind v4 setup, running it for a member panel generates a theme.css scoped to that panel:

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../app/Filament/Member/**/*';
@source '../../../../resources/views/filament/member/**/*';

Notice the @source paths point specifically to Member, not a generic Filament/**/*. Each panel gets its own scoped theme out of the box.

That's it: the generated file is intentionally minimal. If you want a dedicated place for CSS variables or light/dark overrides, you can add your own @layer base block:

@layer base {

    :root {

    }

    :root[class~="dark"] {

    }

}

The other change worth noting: in Filament v3, you had to manually add the theme to vite.config.js and register it with ->viteTheme() in your panel provider. In v4/v5, the command does both for you automatically. No manual wiring required.

A few things changed compared to Tailwind v3:

No more tailwind.config.js. Tailwind v4 no longer requires a tailwind.config.js file for basic theme customization. It moved to CSS-first configuration. There's nothing left to generate or maintain in a separate JS config file for the theme.

@source directives replace the old content array. Tailwind v4 scans these paths directly from CSS to know which files to look at for class usage, instead of declaring them in a JS config.

The generated file is intentionally minimal. Just the import and the @source paths for that panel. No boilerplate @layer base block is added automatically; that's something you add yourself if and when you need it, as shown above.

Adding your own directories to @source

The generated @source directives only cover Filament's own files. Your resources, components, or anything outside app/Filament won't be scanned by default. If you use Tailwind classes in Blade components, Livewire components, or custom views, you need to add those directories yourself:

@source '../../../../app/Filament/**/*';
@source '../../../../resources/views/filament/**/*';
@source '../../../../resources/views/components/**/*';
@source '../../../../resources/views/livewire/**/*';
@source '../../../../app/Livewire/**/*';

Forget to add a directory here, and Tailwind will purge classes it never saw being used, even if they're correctly written in your code. Rebuild with npm run build after adding new paths.

What gets registered automatically

Since the command handles registration for you, vite.config.js already includes the new theme after running make:filament-theme:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/css/app.css',
                'resources/css/filament/admin/theme.css',
                'resources/js/app.js',
            ],
            refresh: true,
        }),
        tailwindcss(),
    ],
});

And your panel provider already has the theme registered too:

->viteTheme('resources/css/filament/admin/theme.css')

Nothing left to wire up manually. Just run the command, and both files are updated for you.

Multiple panels, multiple themes

If your application has more than one panel, an admin panel and a member panel for example, each one gets its own theme file and is registered independently.

Run make:filament-theme once per panel:

php artisan make:filament-theme backend
php artisan make:filament-theme member

Each run automatically adds its theme to vite.config.js and registers it in the matching panel provider. You'll end up with both entries already in place:

laravel({
    input: [
        'resources/css/app.css',
        'resources/css/filament/backend/theme.css',
        'resources/css/filament/member/theme.css',
        'resources/js/app.js',
    ],
    refresh: true,
}),


// BackendPanelProvider
->id('backend')
->viteTheme('resources/css/filament/backend/theme.css')

// MemberPanelProvider
->id('member')
->viteTheme('resources/css/filament/member/theme.css')

Each theme.css is scoped to its own panel by default. The @source directives only point to that panel's files, and customizing one theme doesn't affect the other. This is how the Multipanel and Multi-Tenant starters are set up: two themes, generated empty and scoped, ready for each panel to be styled differently if needed.

Sharing one theme across panels: you don't have to generate a separate file per panel. If two panels should look identical, you can point both panel providers at the same theme file:

// BackendPanelProvider
->viteTheme('resources/css/filament/shared/theme.css')

// MemberPanelProvider
->viteTheme('resources/css/filament/shared/theme.css')

Just make sure the @source paths in that shared file cover both panels directories, since a single generated theme only scopes to the panel it was created for.

Reading Filament's hook classes

Once you start customizing beyond colors, you'll be targeting Filament's own CSS classes, the ones used in the sidebar example below and throughout the interface. They follow a consistent naming convention worth knowing:

  • fi is short for "Filament"
  • fi-ac is used for classes from the Actions package
  • fi-fo is used for classes from the Forms package
  • fi-in is used for classes from the Infolists package
  • fi-no is used for classes from the Notifications package
  • fi-sc is used for classes from the Schema package
  • fi-ta is used for classes from the Tables package
  • fi-wi is used for classes from the Widgets package
  • btn is short for "button"
  • col is short for "column"
  • ctn is short for "container"
  • wrp is short for "wrapper"

Once you know this pattern, hook class names become much easier to read without digging through Filament's source every time.

Finding hook classes in practice: the naming convention tells you what a class means once you've found it, but you'll still need to find it first. The fastest way:

  1. Open your browser's dev tools on the panel you want to customize
  2. Inspect the element you want to target
  3. Look for classes starting with fi- in the inspector
  4. Use that class in your theme's CSS

This is the most reliable way to target the exact element you're looking at, rather than guessing from the naming convention alone.

Customizing your theme

With the theme registered, you can now add your own styles directly in theme.css. Filament's hook classes, documented in the Filament docs, let you target specific parts of the interface.

Here's an example customizing the sidebar, the same idea as in the original v3 guide, adapted to the new file structure:

@import '../../../../vendor/filament/filament/resources/css/theme.css';

@source '../../../../app/Filament/**/*';
@source '../../../../resources/views/filament/**/*';

.fi-sidebar {
    @apply !bg-gray-500;

    .fi-sidebar-header {
        .fi-icon-btn-icon {
            @apply text-primary-500;
        }
    }

    .fi-sidebar-group-label, .fi-icon-btn-icon, .fi-sidebar-group-icon, .fi-sidebar-item-icon, .fi-sidebar-item-button, .fi-sidebar-item-label {
        @apply text-white;
    }

    .fi-sidebar-item-active {
        .fi-sidebar-item-button {
            @apply bg-primary-500;
        }
    }

    .fi-sidebar-item, .fi-sidebar-item-button {
        :hover {
            @apply hover:bg-primary-500;
        }
    }

    .fi-icon-btn {
        :hover {
            @apply hover:text-primary-500;
        }
    }

    .fi-sidebar-nav-groups {
        @apply gap-y-0;
    }
}

The @apply syntax and the hook classes themselves haven't changed. What changed is everything around them: how the theme is generated, how Tailwind scans your files, and how the build is configured.

Using CSS variables for reusable values

If you find yourself repeating the same color or value across multiple rules, defining a CSS variable in @layer base keeps things consistent and easier to update later:

@layer base {
    :root {
        --sidebar-bg: #1f2937;
    }

    :root[class~="dark"] {
        --sidebar-bg: #111827;
    }
}

Then reference it directly in your styles:

.fi-sidebar {
    background: var(--sidebar-bg);
}

This is especially useful for values you'll reuse in several places, or want to swap between light and dark mode without duplicating rules.

Run npm run build once you're done, and your custom theme is live.

Common mistakes when customizing Filament themes

A few things come up repeatedly when developers start working with Filament themes for the first time.

Creating a theme when ->colors() is enough. If the only goal is changing the primary color, buttons, and sidebar accents, ->colors() in the panel provider covers that without generating any files. A custom theme is only necessary for structural changes, targeting specific interface elements via hook classes, or using CSS variables for fine-grained control. Generating a theme file when it isn't needed just adds a build step and a file to maintain.

Forgetting to add custom Blade directories to @source. The generated theme only scans Filament's own directories by default. Any Tailwind classes used in custom Blade components, Livewire components, or views outside app/Filament won't be detected unless you explicitly add those paths. Tailwind silently purges the undetected classes, and the issue only shows up at build time when styles randomly stop working in production.

Styling internal Filament classes instead of hook classes. Filament's hook classes, prefixed with fi-, are the intended surface for CSS customization. Styling classes without the fi- prefix means targeting internal implementation details that can change between releases without notice. Filament's own documentation explicitly warns against this: if a hook class you need doesn't exist yet, the recommended path is to open a pull request to add it, not to target internal classes and accept the maintenance risk.

Overriding too much. The more CSS overrides added to a theme, the harder upgrades become. When Filament ships a new version, any structural change to the interface can break rules that were written against the old markup. Prefer color tokens, CSS variables, and targeted hook class overrides before rewriting large sections of the interface. A theme should adapt Filament to a brand, not turn Filament into a completely different application.

And of course, editing vendor files directly. Any change made inside vendor/filament is overwritten the next time composer update runs. If something in Filament's default theme needs to be overridden, the right place is the custom theme.css file, using hook classes to target the element. The vendor directory is never the right place.

Don't want to build a theme from scratch?

If building a custom Filament theme isn't where you want to spend your time, Filafly offers premium themes and plugins ready to drop into your panel.

Use code MASTERY15 for 15% off your purchase.

Discover Filafly themes →

Originally published on Filament Mastery