# All files / js/composables useAppearance.ts

71.05% Statements 27/38
42.85% Branches 9/21
88.88% Functions 8/9
71.05% Lines 27/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100

    4x

    4x

    4x

    1x
    1x

    1x

    1x

    1x
    1x

    1x

    1x
    1x

    1x

    1x

    1x

    1x
    1x

    1x

    1x

    1x
    1x

    1x

    1x

    1x

    1x

    1x

    1x

     import { onMounted, ref } from 'vue';

    type Appearance = 'light' | 'dark' | 'system';

    export function updateTheme(value: Appearance) {
        Iif (typeof window === 'undefined') {
            return;
        }

        Iif (value === 'system') {
            const mediaQueryList = window.matchMedia(
                '(prefers-color-scheme: dark)',
            );
            const systemTheme = mediaQueryList.matches ? 'dark' : 'light';

            document.documentElement.classList.toggle(
                'dark',
                systemTheme === 'dark',
            );
        } else {
            document.documentElement.classList.toggle('dark', value === 'dark');
        }
    }

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

        const maxAge = days * 24 * 60 * 60;

        document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;
    };

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

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

    const getStoredAppearance = () => {
        Iif (typeof window === 'undefined') {
            return null;
        }

        return localStorage.getItem('appearance') as Appearance | null;
    };

    const handleSystemThemeChange = () => {
        const currentAppearance = getStoredAppearance();

        updateTheme(currentAppearance || 'system');
    };

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

        // Initialize theme from saved preference or default to system...
        const savedAppearance = getStoredAppearance();
        updateTheme(savedAppearance || 'system');

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

    const appearance = ref<Appearance>('system');

    export function useAppearance() {
        onMounted(() => {
            const savedAppearance = localStorage.getItem(
                'appearance',
            ) as Appearance | null;

            Iif (savedAppearance) {
                appearance.value = savedAppearance;
            }
        });

        function updateAppearance(value: Appearance) {
            appearance.value = value;

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

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

            updateTheme(value);
        }

        return {
            appearance,
            updateAppearance,
        };
    }