diff --git a/frontend/bun.lock b/frontend/bun.lock index c245761..ef2c9c2 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -8,6 +8,7 @@ "@catppuccin/palette": "^1.7.1", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", + "@fontsource-variable/plus-jakarta-sans": "^5.3.0", "@mui/icons-material": "^7.3.8", "@mui/material": "^7.3.8", "@tanstack/react-query": "^5.100.3", @@ -213,6 +214,8 @@ "@exodus/bytes": ["@exodus/bytes@1.15.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="], + "@fontsource-variable/plus-jakarta-sans": ["@fontsource-variable/plus-jakarta-sans@5.3.0", "", {}, "sha512-/l/4r0yyWK9JzAlmA0LiYgGgmJe/Gswt4jTJEzr5QhJfwMbvJZDmYWyW0M4X7yCK69BajMVlV/Lx8g7WDc1+sw=="], + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], diff --git a/frontend/index.html b/frontend/index.html index e6b7d0c..4186454 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,23 +8,42 @@
diff --git a/frontend/package.json b/frontend/package.json index 738dc97..0a1d6d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,6 +21,7 @@ "@catppuccin/palette": "^1.7.1", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", + "@fontsource-variable/plus-jakarta-sans": "^5.3.0", "@mui/icons-material": "^7.3.8", "@mui/material": "^7.3.8", "@tanstack/react-query": "^5.100.3", diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b61015a..fb61a5f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,7 +7,10 @@ import CookieBanner from './components/CookieBanner'; import { useAuth } from './hooks/useAuth'; import { useTheme } from './hooks/useTheme'; import { TopBar } from './components/layout/TopBar'; -import { CategoryBar } from './components/layout/CategoryBar'; +import { AppBackdrop } from './components/ui'; +import { BackdropArtProvider } from './contexts/BackdropArtContext'; +import { useBackdropArtValue } from './hooks/useBackdropArt'; +import { useGlassCapability } from './hooks/useGlassCapability'; import { useCategories } from './api/queries/useCategories'; import { RequireSuperuser } from './components/auth/RequireSuperuser'; import type { CatppuccinTheme } from './theme'; @@ -37,6 +40,11 @@ const AdminUserDetail = lazy(() => import('./pages/AdminUserDetail')); const AdminRatingDetail = lazy(() => import('./pages/AdminRatingDetail')); const AdminReplyDetail = lazy(() => import('./pages/AdminReplyDetail')); +/** Reads whatever art the current route published and feeds the fixed backdrop. */ +const BackdropSlot: React.FC<{ reducedMotion: boolean }> = ({ reducedMotion }) => ( + +); + const App: React.FC = () => { const { t } = useTranslation(); const muiTheme = useMuiTheme(); @@ -44,8 +52,13 @@ const App: React.FC = () => { const { themeName, handleThemeChange } = useTheme(); const { data: categories = [] } = useCategories(); + // Resolves the refract/blur/flat tier and stamps ``, which + // every rim rule in Glass.tsx keys off. + const { reducedMotion } = useGlassCapability(); useEffect(() => { + // The fixed backdrop paints over this; it exists so the page ground is + // right before React mounts and behind any overscroll. document.body.style.backgroundColor = muiTheme.palette.background.default; document.body.style.color = muiTheme.palette.text.primary; }, [muiTheme]); @@ -55,19 +68,23 @@ const App: React.FC = () => { }; return ( - - + + {/* No transform/filter/opacity may be introduced on this element or on + #root: either would create a stacking context that isolates the fixed + backdrop, and every backdrop-filter in the app would sample nothing + and go flat. */} + + - + - {