Make Custom Filament Pages React to Dark/Light Mode
Table of Contents
- Introduction
- The Problem with Custom Filament Pages
- Understanding Filament's Dark Mode Implementation
- The Solution: Custom Dark Variant
- Step-by-Step Implementation
- How It Works
- Practical Examples
- Best Practices and Tips
- Troubleshooting Common Issues
- Conclusion
Introduction
If you've built custom pages in FilamentPHP, you've likely encountered a frustrating issue: your beautifully styled custom pages don't automatically adapt when users toggle between light and dark mode in the Filament dashboard. While Filament's built-in components handle theme switching elegantly, custom pages often remain stuck in light mode, creating an inconsistent user experience.
In this guide, you'll discover a simple yet powerful CSS configuration that makes your custom Filament pages fully reactive to the dashboard's theme settings. This solution requires just one line of code and works seamlessly with Tailwind CSS's dark mode utilities.
Whether you're building admin dashboards, custom reports, or specialized interfaces within Filament, this technique will ensure your pages maintain visual consistency across both theme modes.
The Problem with Custom Filament Pages
When you create custom pages in Filament using the php artisan make:filament-page command, you get a blank canvas to build your interface. However, these pages don't automatically inherit Filament's dark mode behavior.
Here's what typically happens:
Expected Behavior:
- User toggles dark mode in the Filament dashboard
- All components, including custom pages, switch to dark theme
- Consistent user experience across the entire application
Actual Behavior:
- Built-in Filament components switch themes correctly
- Custom pages remain in light mode
- Jarring visual inconsistency breaks the user experience
This occurs because Tailwind CSS's default dark mode implementation uses the @media (prefers-color-scheme: dark) query, which relies on the user's system preferences. However, Filament implements its own dark mode toggle that adds a .dark class to the HTML element, and your custom styles need to be configured to respect this class.
Understanding Filament's Dark Mode Implementation
Filament uses a class-based dark mode strategy rather than relying on system preferences. When a user toggles dark mode:
- Filament adds or removes a
.darkclass to the root HTML element - This class triggers Tailwind's dark mode variants throughout the application
- Built-in Filament components are pre-configured to respond to this class
- Your custom CSS, however, needs explicit configuration to detect this change
The key insight is that Tailwind CSS needs to be told where to look for the dark mode indicator. By default, it doesn't check for a .dark class on parent elements, which is exactly what Filament uses.
The Solution: Custom Dark Variant
The solution is elegantly simple: we'll create a custom Tailwind variant that tells our styles to check for the .dark class anywhere in the parent hierarchy. This is accomplished with a single line of CSS using Tailwind's @variant directive.
This approach:
- ✅ Works automatically with Filament's theme toggle
- ✅ Requires no JavaScript
- ✅ Maintains full Tailwind CSS compatibility
- ✅ Supports all Tailwind utility classes
- ✅ Has zero performance impact
Step-by-Step Implementation
Step 1: Locate Your Application CSS File
First, navigate to your Filament application's CSS file. This is typically located at:
resources/css/filament/admin/app.css
Or if you're using a custom theme:
resources/css/filament/[your-panel-id]/theme.css
Step 2: Add the Custom Dark Variant
Open the CSS file and add the following line at the top, after any @import statements but before other custom styles:
@variant dark (&:where(.dark, .dark *));Your complete CSS file should look something like this:
@import '/vendor/filament/filament/resources/css/theme.css';
@config 'tailwind.config.js';
@variant dark (&:where(.dark, .dark *));
/* Your custom styles below */Step 3: Rebuild Your Assets
After adding the variant, compile your CSS assets:
npm run buildOr for development with hot reload:
npm run devStep 4: Test Your Implementation
- Navigate to your custom Filament page
- Toggle the dark mode switch in the Filament dashboard
- Verify that your dark mode utilities now respond correctly
That's it! Your custom pages will now seamlessly switch between light and dark modes along with the rest of your Filament dashboard.
How It Works
Let's break down what the custom variant does:
@variant dark (&:where(.dark, .dark *));@variant dark
- Creates a new variant called
darkfor Tailwind utilities - Allows you to use
dark:prefix in your classes
&:where(.dark, .dark *)
&represents the current selector:where(.dark, .dark *)is a CSS selector that matches:- Elements with the
.darkclass itself - Any descendant of an element with the
.darkclass
- Elements with the
- The
:where()pseudo-class keeps specificity low, preventing conflicts
What This Achieves:
When Filament adds the .dark class to your HTML element, any Tailwind utility with the dark: prefix will activate, regardless of how deeply nested your custom page components are.
Practical Examples
Example 1: Custom Dashboard Widget
Here's a custom card component that now responds to dark mode:
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
Analytics Overview
</h2>
<p class="text-gray-600 dark:text-gray-300">
Your dashboard content adapts seamlessly to theme changes.
</p>
</div>Result:
- Light mode: white background with dark text
- Dark mode: dark gray background with light text
- Smooth transition when toggling themes
Example 2: Custom Table Styling
Create a responsive table that matches Filament's aesthetic:
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Name
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-800">
<tr>
<td class="px-6 py-4 text-sm text-gray-900 dark:text-gray-100">
John Doe
</td>
<td class="px-6 py-4 text-sm text-gray-600 dark:text-gray-400">
Active
</td>
</tr>
</tbody>
</table>Example 3: Custom Form Elements
Build forms that integrate perfectly with Filament's theme:
<form class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Project Name
</label>
<input
type="text"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg
bg-white dark:bg-gray-800 text-gray-900 dark:text-white
focus:ring-2 focus:ring-primary-500 dark:focus:ring-primary-400"
placeholder="Enter project name"
/>
</div>
<button
type="submit"
class="px-6 py-2 bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-600
text-white rounded-lg transition-colors"
>
Create Project
</button>
</form>Example 4: Alert Components
Create attention-grabbing alerts that work in both themes:
<div class="bg-blue-50 dark:bg-blue-900/20 border-l-4 border-blue-500 dark:border-blue-400 p-4 mb-4">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-blue-500 dark:text-blue-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
</svg>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700 dark:text-blue-300">
This is an informational message that adapts to your theme preference.
</p>
</div>
</div>
</div>Best Practices and Tips
1. Use Semantic Color Naming
Instead of hardcoding specific shades, use Tailwind's semantic color scale:
<!-- Good: Scales naturally between themes -->
<div class="bg-gray-100 dark:bg-gray-800">
<!-- Avoid: Doesn't provide good contrast -->
<div class="bg-gray-100 dark:bg-gray-200">2. Maintain Sufficient Contrast
Always ensure text remains readable in both modes:
<!-- Good contrast in both modes -->
<p class="text-gray-900 dark:text-gray-100">
<!-- Poor contrast in dark mode -->
<p class="text-gray-700 dark:text-gray-600">3. Test Both Themes During Development
Keep your browser's developer tools open and toggle between themes frequently. What looks perfect in light mode might be illegible in dark mode.
4. Leverage Filament's Color Palette
Filament provides a carefully crafted color palette. Use primary, success, warning, and danger colors that are already optimized for both themes:
<button class="bg-primary-600 hover:bg-primary-700 dark:bg-primary-500 dark:hover:bg-primary-600">
Primary Action
</button>5. Consider Shadows and Borders
Shadows and borders behave differently in dark mode:
<!-- Adaptive shadow and border -->
<div class="shadow-md dark:shadow-lg border border-gray-200 dark:border-gray-700">
Content with proper depth in both themes
</div>6. Use Transition Classes
Add smooth transitions when switching themes for a polished experience:
<div class="transition-colors duration-200 bg-white dark:bg-gray-800">
Smooth theme transitions
</div>For more tips on building professional Filament applications, visit cherradix.dev for comprehensive tutorials and best practices.
Troubleshooting Common Issues
Issue 1: Dark Mode Not Working After Adding Variant
Symptom: Dark mode utilities still don't respond to theme toggle.
Solution:
- Ensure you've rebuilt your assets:
npm run build - Clear your browser cache (Ctrl+Shift+R or Cmd+Shift+R)
- Verify the variant is in the correct CSS file that Filament is loading
- Check that there are no syntax errors in your CSS file
Issue 2: Styles Working in Dev but Not Production
Symptom: Dark mode works with npm run dev but not after deployment.
Solution:
- Run
npm run buildbefore deploying - Ensure your build process includes the CSS compilation step
- Verify that the compiled CSS file is being deployed to your server
- Check your asset versioning and cache busting strategy
Issue 3: Inconsistent Behavior Across Pages
Symptom: Some pages respond to dark mode, others don't.
Solution:
- Verify all custom pages are using the same CSS file
- Check that you haven't imported different CSS files in different views
- Ensure the variant is added before any custom styles that might override it
Issue 4: Variant Not Recognized by Tailwind
Symptom: Unknown variant "dark" error or dark utilities not generating.
Solution:
- Ensure you're using a Tailwind CSS version that supports the
@variantdirective (v3.1+) - Check that the syntax is correct:
@variant dark (&:where(.dark, .dark *)); - Verify the
@configdirective points to your correct Tailwind config file
Issue 5: Performance Issues with Dark Mode
Symptom: Lag when toggling between themes.
Solution:
- The custom variant itself has no performance impact
- If you experience lag, it's likely from too many transition effects
- Reduce the number of elements with
transition-colorsor adjust duration values
Issue 6: Specificity Conflicts
Symptom: Dark mode styles not overriding default styles.
Solution:
The :where() pseudo-class in the variant keeps specificity low by design. If you need higher specificity:
/* Alternative with higher specificity */
@variant dark (.dark &);However, this may cause other specificity issues. The :where() approach is recommended for most cases.
Conclusion
Making your custom Filament pages reactive to dark and light mode doesn't have to be complicated. With a single line of CSS configuration, you can ensure that your entire application maintains visual consistency when users toggle between themes.
Key Takeaways:
- Simple Implementation: Just add
@variant dark (&:where(.dark, .dark *));to your CSS file - No JavaScript Required: This is a pure CSS solution that works automatically
- Full Tailwind Integration: Use all of Tailwind's utilities with the
dark:prefix - Zero Performance Impact: The variant has no runtime performance cost
- Consistent User Experience: Your custom pages will feel native to Filament
This technique transforms your custom Filament pages from theme-ignorant to theme-aware, creating a polished, professional experience that your users will appreciate. Whether you're building complex dashboards, custom reports, or specialized admin interfaces, this approach ensures your application looks great in any theme.
Remember to test both light and dark modes throughout your development process, maintain proper contrast ratios, and leverage Filament's existing color palette for the best results.
Ready to level up your Filament development skills? Visit cherradix.dev for more in-depth tutorials, tips, and best practices for building exceptional Laravel and Filament applications. From authentication systems to performance optimization, we've got you covered with practical, production-ready solutions.
Happy coding, and may your dark mode always be pixel-perfect! 🌗