1
- import type {
2
- LinksFunction ,
3
- LoaderFunctionArgs ,
4
- MetaFunction ,
1
+ import {
2
+ type LinksFunction ,
3
+ type LoaderFunctionArgs ,
4
+ type MetaFunction ,
5
5
} from '@vercel/remix' ;
6
+ import type { ReactNode } from 'react' ;
6
7
7
8
import {
8
- isRouteErrorResponse ,
9
- Link ,
10
9
Links ,
11
10
Meta ,
12
11
Outlet ,
13
12
Scripts ,
14
13
ScrollRestoration ,
15
14
useLoaderData ,
16
- useRouteError ,
17
15
} from '@remix-run/react' ;
18
16
import { getLatestVersion } from 'fast-npm-meta' ;
19
17
import { eslogan , siteTitle , siteUrl , siteUrlImages } from './globals' ;
@@ -31,21 +29,18 @@ import { proseClasses } from './ui/prose';
31
29
import { Toaster } from '@pheralb/toast' ;
32
30
import {
33
31
PreventFlashOnWrongTheme ,
32
+ Theme ,
34
33
ThemeProvider ,
35
34
useTheme ,
36
35
} from 'remix-themes' ;
37
- import { themeSessionResolver } from './sessions.server' ;
38
36
39
37
// MDX Components:
40
38
import { MDXProvider } from '@mdx-js/react' ;
41
39
import { mdxComponents } from './components/mdx' ;
42
40
43
41
// Stores:
44
42
import { useDocsStore } from './store' ;
45
-
46
- // Other:
47
- import { Logo } from './components/icons' ;
48
- import { buttonVariants } from './ui/button' ;
43
+ import { themeSessionResolver } from './sessions.server' ;
49
44
50
45
// Links:
51
46
export const links : LinksFunction = ( ) => [
@@ -97,6 +92,7 @@ export const meta: MetaFunction = ({ matches }) => {
97
92
] ;
98
93
} ;
99
94
95
+ // Get theme from loader:
100
96
export async function loader ( { request } : LoaderFunctionArgs ) {
101
97
const { getTheme } = await themeSessionResolver ( request ) ;
102
98
const metadata = await getLatestVersion ( '@pheralb/toast' ) ;
@@ -106,21 +102,18 @@ export async function loader({ request }: LoaderFunctionArgs) {
106
102
} ;
107
103
}
108
104
109
- export default function AppWithProviders ( ) {
110
- const data = useLoaderData < typeof loader > ( ) ;
111
- return (
112
- < ThemeProvider specifiedTheme = { data . theme } themeAction = "/action/set-theme" >
113
- < App />
114
- </ ThemeProvider >
115
- ) ;
116
- }
117
-
118
- function App ( ) {
105
+ // App global layout:
106
+ function Layout ( { children } : { children : ReactNode } ) {
119
107
const data = useLoaderData < typeof loader > ( ) ;
120
108
const [ theme ] = useTheme ( ) ;
121
109
const { toastPosition, toastTheme } = useDocsStore ( ) ;
122
110
return (
123
- < html lang = "en" className = { cn ( theme , 'scroll-smooth focus:scroll-auto' ) } >
111
+ < html
112
+ lang = "en"
113
+ data-theme = { theme }
114
+ className = { cn ( theme ?? '' , 'scroll-smooth focus:scroll-auto' ) }
115
+ style = { { colorScheme : theme ?? '' } }
116
+ >
124
117
< head >
125
118
< meta charSet = "utf-8" />
126
119
< meta name = "viewport" content = "width=device-width, initial-scale=1" />
@@ -149,10 +142,11 @@ function App() {
149
142
< meta name = "twitter:title" content = { siteTitle } />
150
143
{ /* App Meta Function */ }
151
144
< Meta />
152
- < PreventFlashOnWrongTheme ssrTheme = { Boolean ( data . theme ) } />
145
+ < PreventFlashOnWrongTheme ssrTheme = { Boolean ( data ? .theme ) } />
153
146
< Links />
154
147
</ head >
155
148
< body
149
+ suppressHydrationWarning
156
150
className = { cn (
157
151
'font-sans antialiased' ,
158
152
'bg-neutral-50 dark:bg-neutral-900' ,
@@ -171,7 +165,7 @@ function App() {
171
165
proseClasses ,
172
166
) }
173
167
>
174
- < Outlet />
168
+ { children }
175
169
</ article >
176
170
</ MDXProvider >
177
171
</ main >
@@ -187,35 +181,39 @@ function App() {
187
181
) ;
188
182
}
189
183
184
+ // App with providers:
185
+ function AppWithProviders ( { children } : { children : ReactNode } ) {
186
+ const data = useLoaderData < typeof loader > ( ) ;
187
+ return (
188
+ < ThemeProvider
189
+ specifiedTheme = { data ?. theme as Theme }
190
+ themeAction = "/action/set-theme"
191
+ >
192
+ < Layout > { children } </ Layout >
193
+ </ ThemeProvider >
194
+ ) ;
195
+ }
196
+
197
+ export default function App ( ) {
198
+ return (
199
+ < AppWithProviders >
200
+ < Outlet />
201
+ </ AppWithProviders >
202
+ ) ;
203
+ }
204
+
190
205
export function ErrorBoundary ( ) {
191
- const error = useRouteError ( ) ;
192
206
return (
193
- < html lang = "en" className = "dark" >
194
- < head >
195
- < title > Oops! - @pheralb/toast</ title >
196
- < Meta />
197
- < Links />
198
- </ head >
199
- < body className = "flex h-screen flex-col items-center justify-center space-y-4 bg-neutral-900 font-sans text-white" >
200
- < Logo className = "h-12 w-12" />
201
- < h1 className = "text-2xl font-medium tracking-tight" >
202
- { isRouteErrorResponse ( error )
203
- ? `${ error . status } ${ error . statusText } `
204
- : error instanceof Error
205
- ? error . message
206
- : 'Unknown Error' }
207
- </ h1 >
208
- < Link
209
- to = "/"
210
- className = { buttonVariants ( {
211
- variant : 'outline' ,
212
- } ) }
213
- >
214
- Go back home
215
- </ Link >
216
- < p className = "font-mono text-sm" > @pheralb/toast</ p >
217
- < Scripts />
218
- </ body >
219
- </ html >
207
+ < AppWithProviders >
208
+ < h2 > Error</ h2 >
209
+ </ AppWithProviders >
210
+ ) ;
211
+ }
212
+
213
+ export function HydrateFallback ( ) {
214
+ return (
215
+ < AppWithProviders >
216
+ < h1 > Loading...</ h1 >
217
+ </ AppWithProviders >
220
218
) ;
221
219
}
0 commit comments