Provider
One provider at the root of your app, with direction and locale as props. It is required, and everything it does is listed on this page.
Where it goes
Your root layout, wrapping everything. direction and locale come from the route, never from the DOM.
import { TopClassProvider } from 'topclassui/provider'
import { ThemeScript } from 'topclassui/theme'
export default async function RootLayout({ children, params }: {
children: React.ReactNode
params: Promise<{ locale: string }>
}) {
const { locale } = await params
const dir = locale === 'ar' ? 'rtl' : 'ltr'
return (
<html lang={locale} dir={dir} data-theme="dark" suppressHydrationWarning>
<head><ThemeScript /></head>
<body>
<TopClassProvider direction={dir} locale={locale}>
{children}
</TopClassProvider>
</body>
</html>
)
}The props
Why direction is a prop
Reading document.dir in an effect would give the server one answer and the browser another for one frame, which shows as a flash in the wrong direction. A prop from the layout has the same value in both places.
useEffect(() => {
setDir(document.documentElement.dir)
}, [])<html dir={dir}>
<TopClassProvider direction={dir}>What it renders
Direction provider
Toast viewport
Tooltip provider
Isolation root
isolation: isolate, so overlays stack above your page without z-index conflicts.ThemeScript
A small inline script, first in head, reads the stored preference and sets data-theme before the first paint. Without it the page renders in the default mode and then switches, which shows a flash of the wrong mode. The script is a constant string, so a strict Content Security Policy can allow it by the hash below.
import { ThemeScript } from 'topclassui/theme'
<head><ThemeScript /></head>Content-Security-Policy: script-src 'self' 'sha256-DXE/BJ+WKtAgobNXKiOEuUkho9yY2f/ky8nwg8sLL/8='In a single-page app
In a single-page app nothing React renders runs before the first paint, so the same script goes in index.html above the module script: paste the THEME_SCRIPT string from topclassui/theme into a script tag. The initial lang, dir and data-theme are static attributes on the html element. When the visitor changes locale, update the html attributes and the provider props at the same time.
<!doctype html>
<html lang="en" dir="ltr" data-theme="dark">
<head>
<meta charset="UTF-8" />
<script>(function(){try{var m=["light","dark","ocean"];var t=null;try{t=localStorage.getItem('tc-theme')}catch(e){}if(!t||t==='system'||m.indexOf(t)<0){t=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}document.documentElement.dataset.theme=t}catch(e){}})()</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>