Laravel React Jsx Boilerplate

PHP

Production-ready Laravel 13 + React 19 boilerplate with Inertia.js v3, Vite, Tailwind CSS v4, SSR support, and powerful Artisan generators for scalable SaaS and dashboard applications.

Stars
18
Forks
0
Downloads
4,356
Open Issues
0
Files main

Repository Files

Loading file structure...
resources/js/hooks/use-appearance.js
import { useSyncExternalStore } from 'react';

const listeners = new Set();
let currentAppearance = 'system';

const prefersDark = () => {
    if (typeof window === 'undefined') {
        return false;
    }

    return window.matchMedia('(prefers-color-scheme: dark)').matches;
};

const setCookie = (name, value, days = 365) => {
    if (typeof document === 'undefined') {
        return;
    }

    const maxAge = days * 24 * 60 * 60;
    document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;
};

const getStoredAppearance = () => {
    if (typeof window === 'undefined') {
        return 'system';
    }

    return localStorage.getItem('appearance') || 'system';
};

const isDarkMode = (appearance) => {
    return appearance === 'dark' || (appearance === 'system' && prefersDark());
};

const applyTheme = (appearance) => {
    if (typeof document === 'undefined') {
        return;
    }

    const isDark = isDarkMode(appearance);

    document.documentElement.classList.toggle('dark', isDark);
    document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
};

const subscribe = (callback) => {
    listeners.add(callback);

    return () => listeners.delete(callback);
};

const notify = () => listeners.forEach((listener) => listener());

const mediaQuery = () => {
    if (typeof window === 'undefined') {
        return null;
    }

    return window.matchMedia('(prefers-color-scheme: dark)');
};

const handleSystemThemeChange = () => applyTheme(currentAppearance);

export function initializeTheme() {
    if (typeof window === 'undefined') {
        return;
    }

    if (!localStorage.getItem('appearance')) {
        localStorage.setItem('appearance', 'system');
        setCookie('appearance', 'system');
    }

    currentAppearance = getStoredAppearance();
    applyTheme(currentAppearance);

    // Set up system theme change listener
    mediaQuery()?.addEventListener('change', handleSystemThemeChange);
}

export function useAppearance() {
    const appearance = useSyncExternalStore(
        subscribe,
        () => currentAppearance,
        () => 'system',
    );

    const resolvedAppearance = isDarkMode(appearance) ? 'dark' : 'light';

    const updateAppearance = (mode) => {
        currentAppearance = mode;

        // Store in localStorage for client-side persistence...
        localStorage.setItem('appearance', mode);

        // Store in cookie for SSR...
        setCookie('appearance', mode);

        applyTheme(mode);
        notify();
    };

    return { appearance, resolvedAppearance, updateAppearance };
}