@@ -9,13 +9,15 @@ import {
99 useMatches ,
1010} from '@tanstack/react-router'
1111import type { ReactNode } from 'react'
12+ import appCss from '~/styles/app.css?url'
1213
1314export const Route = createRootRoute ( {
1415 head : ( ) => ( {
1516 meta : [
1617 { charSet : 'utf-8' } ,
1718 { name : 'viewport' , content : 'width=device-width, initial-scale=1' } ,
1819 ] ,
20+ links : [ { rel : 'stylesheet' , href : appCss } ] ,
1921 } ) ,
2022 loader : ( ) => ( {
2123 crumb : 'Home' ,
@@ -30,10 +32,20 @@ function Breadcrumbs() {
3032 )
3133
3234 return (
33- < nav >
34- { matchesWithCrumbs . map ( ( match ) => (
35- < span data-testid = { `crumb-${ match . routeId } ` } key = { match . id } >
36- { match . loaderData ?. crumb }
35+ < nav className = "flex flex-wrap items-center gap-2" >
36+ { matchesWithCrumbs . map ( ( match , index ) => (
37+ < span className = "flex items-center gap-2" key = { match . id } >
38+ { index > 0 ? (
39+ < span className = "text-slate-400" aria-hidden >
40+ /
41+ </ span >
42+ ) : null }
43+ < span
44+ className = "rounded-full bg-[rgba(31,111,120,0.10)] px-3 py-1 text-sm font-semibold text-[var(--color-lagoon)]"
45+ data-testid = { `crumb-${ match . routeId } ` }
46+ >
47+ { match . loaderData ?. crumb }
48+ </ span >
3749 </ span >
3850 ) ) }
3951 </ nav >
@@ -60,8 +72,16 @@ function RootDocument({
6072 < head >
6173 < HeadContent />
6274 </ head >
63- < body >
64- < p data-testid = "root-component-marker" > { marker } </ p >
75+ < body className = "hmr-shell" >
76+ < div className = "hidden" aria-hidden = "true" >
77+ < p data-testid = "root-component-marker" > { marker } </ p >
78+ </ div >
79+ < div className = "pointer-events-none fixed right-4 top-4 z-20 hidden sm:block" >
80+ < div className = "rounded-full border border-white/70 bg-white/75 px-4 py-2 shadow-lg backdrop-blur" >
81+ < span className = "hmr-label" > Current marker </ span >
82+ < span className = "hmr-kbd" > { marker } </ span >
83+ </ div >
84+ </ div >
6585 { children }
6686 < Scripts />
6787 </ body >
@@ -81,9 +101,11 @@ function RootShellDocument({
81101 < head >
82102 < HeadContent />
83103 </ head >
84- < body >
85- < div data-testid = "root-shell ">
104+ < body className = "hmr-shell" >
105+ < div className = "hidden" aria-hidden = "true ">
86106 < p data-testid = "root-shell-marker" > { marker } </ p >
107+ </ div >
108+ < div className = "hmr-shell" data-testid = "root-shell" >
87109 { children }
88110 </ div >
89111 < Scripts />
@@ -93,62 +115,151 @@ function RootShellDocument({
93115}
94116
95117function RootContent ( ) {
118+ const navLinks = [
119+ {
120+ testId : 'home-link' ,
121+ to : '/' ,
122+ label : 'Home' ,
123+ detail : 'Local state + route option HMR' ,
124+ } ,
125+ {
126+ testId : 'child-link' ,
127+ to : '/child' ,
128+ label : 'Child' ,
129+ detail : 'Loader and beforeLoad updates' ,
130+ } ,
131+ {
132+ testId : 'inputs-link' ,
133+ to : '/inputs' ,
134+ label : 'Inputs' ,
135+ detail : 'Uncontrolled input preservation' ,
136+ } ,
137+ {
138+ testId : 'component-hmr-link' ,
139+ to : '/component-hmr' ,
140+ label : 'Component HMR' ,
141+ detail : 'Base component refresh checks' ,
142+ } ,
143+ {
144+ testId : 'component-hmr-inline-split-link' ,
145+ to : '/component-hmr-inline-split' ,
146+ label : 'Inline Split' ,
147+ detail : 'Inline component with default splitting' ,
148+ } ,
149+ {
150+ testId : 'component-hmr-inline-nosplit-link' ,
151+ to : '/component-hmr-inline-nosplit' ,
152+ label : 'Inline No Split' ,
153+ detail : 'Inline component with code splitting disabled' ,
154+ } ,
155+ {
156+ testId : 'component-hmr-named-split-link' ,
157+ to : '/component-hmr-named-split' ,
158+ label : 'Named Split' ,
159+ detail : 'Named component with default splitting' ,
160+ } ,
161+ {
162+ testId : 'component-hmr-named-nosplit-link' ,
163+ to : '/component-hmr-named-nosplit' ,
164+ label : 'Named No Split' ,
165+ detail : 'Named component with code splitting disabled' ,
166+ } ,
167+ {
168+ testId : 'component-hmr-inline-error-split-link' ,
169+ to : '/component-hmr-inline-error-split' ,
170+ label : 'Inline Error Split' ,
171+ detail : 'Inline component with only errorComponent split' ,
172+ } ,
173+ {
174+ testId : 'component-hmr-named-error-split-link' ,
175+ to : '/component-hmr-named-error-split' ,
176+ label : 'Named Error Split' ,
177+ detail : 'Named component with only errorComponent split' ,
178+ } ,
179+ ] as const
180+
96181 return (
97- < >
98- < Link data-testid = "home-link" to = "/" >
99- Home
100- </ Link >
101- < Link data-testid = "child-link" to = "/child" >
102- Child
103- </ Link >
104- < Link data-testid = "inputs-link" to = "/inputs" >
105- Inputs
106- </ Link >
107- < Link data-testid = "component-hmr-link" to = "/component-hmr" >
108- Component HMR
109- </ Link >
110- < Link
111- data-testid = "component-hmr-inline-split-link"
112- to = "/component-hmr-inline-split"
113- >
114- Component HMR Inline Split
115- </ Link >
116- < Link
117- data-testid = "component-hmr-inline-nosplit-link"
118- to = "/component-hmr-inline-nosplit"
119- >
120- Component HMR Inline No Split
121- </ Link >
122- < Link
123- data-testid = "component-hmr-named-split-link"
124- to = "/component-hmr-named-split"
125- >
126- Component HMR Named Split
127- </ Link >
128- < Link
129- data-testid = "component-hmr-named-nosplit-link"
130- to = "/component-hmr-named-nosplit"
131- >
132- Component HMR Named No Split
133- </ Link >
134- < Link
135- data-testid = "component-hmr-inline-error-split-link"
136- to = "/component-hmr-inline-error-split"
137- >
138- Component HMR Inline Error Split
139- </ Link >
140- < Link
141- data-testid = "component-hmr-named-error-split-link"
142- to = "/component-hmr-named-error-split"
143- >
144- Component HMR Named Error Split
145- </ Link >
146- < input data-testid = "root-message" defaultValue = "root state" />
147- < Breadcrumbs />
182+ < div className = "hmr-page" >
183+ < header className = "hmr-hero" >
184+ < div className = "relative z-10 flex flex-col gap-6" >
185+ < div className = "flex flex-wrap items-start justify-between gap-4" >
186+ < div className = "max-w-3xl space-y-3" >
187+ < p className = "hmr-label" > React Start HMR Playground</ p >
188+ < h1 className = "font-display text-4xl font-bold tracking-tight text-[var(--color-night)] sm:text-5xl" >
189+ Route refresh behavior
190+ </ h1 >
191+ < p className = "max-w-2xl text-sm leading-6 text-slate-600 sm:text-base" >
192+ This sandbox exercises component state preservation, route
193+ option updates, stale data clearing, and split-route refresh
194+ behavior.
195+ </ p >
196+ </ div >
197+ </ div >
198+
199+ < div className = "grid gap-4 lg:grid-cols-[1.3fr_0.7fr]" >
200+ < div className = "space-y-3" >
201+ < div className = "flex items-center justify-between gap-4" >
202+ < span className = "hmr-label" > Route matrix</ span >
203+ < span className = "text-xs font-medium text-slate-500" >
204+ Stable test ids preserved
205+ </ span >
206+ </ div >
207+ < div className = "hmr-nav-grid" >
208+ { navLinks . map ( ( link ) => (
209+ < Link
210+ activeProps = { {
211+ className :
212+ 'group hmr-nav-link border-[var(--color-reef)] bg-white shadow-md' ,
213+ } }
214+ className = "group hmr-nav-link"
215+ data-testid = { link . testId }
216+ key = { link . testId }
217+ to = { link . to }
218+ >
219+ < span className = "font-display text-lg font-semibold text-[var(--color-night)]" >
220+ { link . label }
221+ </ span >
222+ < span className = "text-sm leading-5 text-slate-600 group-hover:text-slate-700" >
223+ { link . detail }
224+ </ span >
225+ </ Link >
226+ ) ) }
227+ </ div >
228+ </ div >
229+
230+ < div className = "hmr-card flex flex-col gap-4" >
231+ < div >
232+ < p className = "hmr-label" > Root state</ p >
233+ < p className = "mt-2 text-sm leading-6 text-slate-600" >
234+ This uncontrolled input is used by the tests to verify
235+ root-level state survives route and component HMR updates.
236+ </ p >
237+ </ div >
238+ < input
239+ className = "hmr-input"
240+ data-testid = "root-message"
241+ defaultValue = "root state"
242+ />
243+ < div className = "space-y-2" >
244+ < p className = "hmr-label" > Breadcrumbs</ p >
245+ < Breadcrumbs />
246+ </ div >
247+ < ClientOnly >
248+ < p
249+ className = "inline-flex w-fit items-center rounded-full bg-emerald-100 px-3 py-1 text-sm font-semibold text-emerald-700"
250+ data-testid = "hydrated"
251+ >
252+ hydrated
253+ </ p >
254+ </ ClientOnly >
255+ </ div >
256+ </ div >
257+ </ div >
258+ </ header >
259+
260+ < div className = "pb-8" >
148261 < Outlet />
149- < ClientOnly >
150- < p data-testid = "hydrated" > hydrated</ p >
151- </ ClientOnly >
152- </ >
262+ </ div >
263+ </ div >
153264 )
154265}
0 commit comments