Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import type { LinksFunction } from "react-router"
import { useChangeLanguage } from "remix-i18next/react"
import type { Route } from "./+types/root"
import { LanguageSwitcher } from "./library/language-switcher"
import { ClientHintCheck, getHints } from "./services/client-hints"
import tailwindcss from "./tailwind.css?url"

export async function loader({ context }: Route.LoaderArgs) {
export async function loader({ context, request }: Route.LoaderArgs) {
const { lang, clientEnv } = context
return { lang, clientEnv }
const hints = getHints(request)
return { lang, clientEnv, hints }
}

export const links: LinksFunction = () => [{ rel: "stylesheet", href: tailwindcss }]
Expand All @@ -20,7 +22,6 @@ export const handle = {
export default function App({ loaderData }: Route.ComponentProps) {
const { lang, clientEnv } = loaderData
useChangeLanguage(lang)

return (
<>
<Outlet />
Expand All @@ -35,6 +36,7 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
return (
<html className="overflow-y-auto overflow-x-hidden" lang={i18n.language} dir={i18n.dir()}>
<head>
<ClientHintCheck />
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
Expand Down
47 changes: 47 additions & 0 deletions app/services/client-hints.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getHintUtils } from "@epic-web/client-hints"
import { clientHint as colorSchemeHint, subscribeToSchemeChange } from "@epic-web/client-hints/color-scheme"
import { clientHint as reducedMotionHint, subscribeToMotionChange } from "@epic-web/client-hints/reduced-motion"
import { clientHint as timeZoneHint } from "@epic-web/client-hints/time-zone"
import { useEffect } from "react"
import { useRevalidator, useRouteLoaderData } from "react-router"
import type { Route } from "../+types/root"

export const { getHints, getClientHintCheckScript } = getHintUtils({
theme: colorSchemeHint,
timeZone: timeZoneHint,
reducedMotion: reducedMotionHint,
// add other hints here
})

/**
* @public
* Utility function used to get the time zone for the current users browser on either the client or the server.
* */
export const getTimeZone = (request?: Request) => getHints(request).timeZone

/**
* @public
* Utility used to get the client hints for the current users browser.
* */
export function useHints() {
const requestInfo = useRouteLoaderData<Route.ComponentProps["loaderData"]>("root")
return requestInfo?.hints
}
/**
* Utility component used to check the client hints on the client and send them to the server.
*/
export function ClientHintCheck({ nonce }: { nonce?: string }) {
const { revalidate } = useRevalidator()
useEffect(() => subscribeToSchemeChange(() => revalidate()), [revalidate])
useEffect(() => subscribeToMotionChange(() => revalidate()), [revalidate])

return (
<script
nonce={nonce}
// biome-ignore lint/security/noDangerouslySetInnerHtml: We want to run this script on the client
dangerouslySetInnerHTML={{
__html: getClientHintCheckScript(),
}}
/>
)
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"postinstall": "pnpm run typegen"
},
"dependencies": {
"@epic-web/client-hints": "1.3.5",
"@forge42/seo-tools": "1.3.0",
"@react-router/node": "7.1.5",
"clsx": "2.1.1",
Expand Down Expand Up @@ -74,7 +75,7 @@
"typescript": "5.7.3",
"vite": "6.0.11",
"vite-plugin-babel": "1.3.0",
"vite-plugin-icons-spritesheet": "3.0.0",
"vite-plugin-icons-spritesheet": "3.0.1",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.0.5",
"vitest-browser-react": "0.0.4"
Expand Down
18 changes: 13 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ export default defineConfig({
formatter: "biome",
}),
],
build: {
assetsInlineLimit: 0,
},
server: {
open: true,
// biome-ignore lint/nursery/noProcessEnv: Its ok to use process.env here
Expand Down