chore: init unocss

This commit is contained in:
Sonny
2025-12-10 20:04:11 +01:00
parent be41962c89
commit b119b29ad1
12 changed files with 1523 additions and 625 deletions

View File

@@ -0,0 +1,138 @@
import { useEffect, useState } from 'react';
type Theme = 'light' | 'dark' | 'system';
export function ThemeToggle() {
const [theme, setTheme] = useState<Theme>('system');
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
const savedTheme = localStorage.getItem('theme') as Theme;
if (savedTheme) {
setTheme(savedTheme);
applyTheme(savedTheme);
} else {
setTheme('system');
applyTheme('system');
}
}, []);
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleSystemThemeChange = () => {
if (theme === 'system') {
applyTheme('system');
}
};
mediaQuery.addEventListener('change', handleSystemThemeChange);
return () => {
mediaQuery.removeEventListener('change', handleSystemThemeChange);
};
}, [theme]);
const applyTheme = (newTheme: Theme) => {
const root = document.documentElement;
if (newTheme === 'system') {
const systemPrefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;
if (systemPrefersDark) {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
} else if (newTheme === 'dark') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
};
const toggleTheme = () => {
let newTheme: Theme;
if (theme === 'light') {
newTheme = 'dark';
} else if (theme === 'dark') {
newTheme = 'system';
} else {
newTheme = 'light';
}
setTheme(newTheme);
applyTheme(newTheme);
localStorage.setItem('theme', newTheme);
};
if (!mounted) {
return null;
}
const getIcon = () => {
if (theme === 'light') {
return (
<svg
className="w-5 h-5 text-yellow-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
);
} else if (theme === 'dark') {
return (
<svg
className="w-5 h-5 text-gray-700 dark:text-yellow-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
/>
</svg>
);
} else {
return (
<svg
className="w-5 h-5 text-blue-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
);
}
};
return (
<button
onClick={toggleTheme}
className="fixed top-4 right-4 p-3 rounded-full bg-white dark:bg-gray-800 shadow-lg hover:shadow-xl transition-all duration-200 border border-gray-200 dark:border-gray-700"
aria-label={`Thème actuel: ${theme}`}
>
{getIcon()}
</button>
);
}

View File

@@ -15,8 +15,9 @@ import { TuyauProvider } from '@tuyau/inertia/react';
import dayjs from 'dayjs';
import { ReactNode, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import 'virtual:uno.css';
import '~/css/app.css';
import { useAppUrl } from '~/hooks/use_app_url';
import '../styles/index.css';
const TRANSITION_IN_CLASS = '__transition_fadeIn';
const TRANSITION_OUT_CLASS = '__transition_fadeOut';

View File

@@ -1,5 +1,5 @@
import { PropsWithChildren } from 'react';
import { BaseLayout } from '~/layouts/_base_layout';
import { BaseLayout } from '~/layouts/base_layout';
const LayoutWrapper = ({ children }: PropsWithChildren) => (
<BaseLayout>{children}</BaseLayout>

View File

@@ -2,7 +2,7 @@ import { Box, rem } from '@mantine/core';
import { PropsWithChildren } from 'react';
import { FloatingNavbar } from '~/components/common/floating_navbar/floating_navbar';
import { Footer } from '~/components/common/footer/footer';
import { BaseLayout } from './_base_layout';
import { BaseLayout } from './base_layout';
const DefaultLayout = ({ children }: PropsWithChildren) => (
<BaseLayout>

View File

@@ -4,7 +4,7 @@ import { Anchor, Button, Container, Group, rem, Title } from '@mantine/core';
import { FormEvent, PropsWithChildren } from 'react';
import { useTranslation } from 'react-i18next';
import i18n from '~/i18n';
import { BaseLayout } from '~/layouts/_base_layout';
import { BaseLayout } from '~/layouts/base_layout';
import { appendCollectionId } from '~/lib/navigation';
export interface FormLayoutProps extends PropsWithChildren {

View File

@@ -2,7 +2,7 @@ import { Box, rem } from '@mantine/core';
import { PropsWithChildren } from 'react';
import { FloatingNavbar } from '~/components/common/floating_navbar/floating_navbar';
import { Footer } from '~/components/common/footer/footer';
import { BaseLayout } from './_base_layout';
import { BaseLayout } from './base_layout';
const SmallContentLayout = ({ children }: PropsWithChildren) => (
<BaseLayout>

View File

@@ -54,6 +54,8 @@
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@typescript-eslint/eslint-plugin": "^8.46.3",
"@unocss/preset-icons": "^66.5.10",
"@unocss/preset-web-fonts": "^66.5.10",
"@vite-pwa/assets-generator": "^1.0.2",
"eslint": "^9.39.1",
"hot-hook": "^0.4.0",
@@ -67,6 +69,7 @@
"release-it": "^19.0.6",
"ts-node-maintained": "^10.9.6",
"typescript": "~5.9.3",
"unocss": "^66.5.10",
"vite": "^7.2.2"
},
"dependencies": {

1940
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,6 @@
<html>
<head>
<meta
name='theme-color'
content='#f0eef6'
/>
<link
href='https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Rubik:ital,wght@0,400;0,700;1,400;1,700&display=swap'
rel='stylesheet'
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charSet='UTF-8' />
<link
@@ -26,6 +18,7 @@
href='/favicon.png'
/>
<title inertia>MyLinks</title>
@if(nodeEnv === 'production')
<link
rel='manifest'
@@ -40,9 +33,35 @@
</script>
@endif
<script>
(function() {
const savedTheme = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let theme = 'system';
if (savedTheme) {
theme = savedTheme;
}
if (theme === 'system') {
if (systemPrefersDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
} else if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
})();
</script>
@routes()
@inertiaHead()
@stack('dumper')
@viteReactRefresh()
@inertiaHead()
@vite(['inertia/app/app.tsx', `inertia/pages/${page.component}.tsx`])
</head>

17
uno.config.ts Normal file
View File

@@ -0,0 +1,17 @@
import presetIcons from '@unocss/preset-icons';
import presetWebFonts from '@unocss/preset-web-fonts';
import { defineConfig, presetWind4 } from 'unocss';
export default defineConfig({
presets: [
presetWind4({
dark: 'class',
}),
presetIcons({
cdn: 'https://esm.sh/',
}),
presetWebFonts({
provider: 'bunny',
}),
],
});

View File

@@ -7,11 +7,13 @@ import { getDirname } from '@adonisjs/core/helpers';
import inertia from '@adonisjs/inertia/client';
import adonisjs from '@adonisjs/vite/client';
import react from '@vitejs/plugin-react';
import UnoCSS from 'unocss/vite';
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
UnoCSS(),
VitePWA({
registerType: 'autoUpdate',
injectRegister: false,