Conversation
…ction and user experience
…roved privacy messaging
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
This PR bumps the app version to 6.0.4 and introduces a new Speed Insights / PageSpeed feature (API route + client UI), alongside updates to locale detection UX and additional i18n strings.
Changes:
- Add a
/api/speed-insightroute and client UI to display live Lighthouse category scores. - Replace the prior browser-translation notice with a client-side locale auto-detection toast and update related locale detection behavior.
- Localize the cookie banner and add new translation keys for Speed Insights + locale detection across supported languages.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
types/configs/speed-insight.ts |
Adds shared TypeScript types for parsed and raw PageSpeed/Lighthouse responses. |
app/api/speed-insight/route.ts |
New API endpoint to fetch and normalize Google PageSpeed Insights results (desktop/mobile). |
components/speed-insight/* |
New client component + hook/constants for rendering live scores with caching/cooldowns. |
app/page.tsx |
Adds the Speed Insight section via dynamic client-only import. |
components/ui/tabs.tsx |
Formatting + class tweaks for tab primitives used by the new UI. |
components/languages/locale-auto-detect.tsx |
New client component to fix locale based on navigator.language and show a toast. |
components/languages/index.ts / app/layout.tsx |
Switches layout usage to LocaleAutoDetect instead of the removed notice. |
components/cookies/cookies-banner.tsx |
Moves cookie banner copy to next-intl translations. |
messages/*.json |
Adds Home.speedInsight, localeDetect, and CookieBanner translations. |
proxy.ts |
Updates locale-detection documentation comments. |
package.json |
Version bump to 6.0.4. |
README.md |
Updates top-of-readme link/stack list formatting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| className={cn( | ||
| "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]", | ||
| className | ||
| "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-0.75", |
There was a problem hiding this comment.
p-0.75 is not part of Tailwind’s default spacing scale, and there’s no custom spacing token defined in app/globals.css (@theme) to support it. This class will be ignored, changing the TabsList padding unexpectedly; use an existing spacing utility (e.g. p-1/p-0.5) or keep an explicit arbitrary value like p-[3px], or define a custom spacing token if you intend 0.75.
| "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-0.75", | |
| "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]", |
| /** Map a 0–1 score to a Tailwind color class */ | ||
| function getScoreColor(score: number): string { | ||
| if (score >= 90) return "text-green-500"; | ||
| if (score >= 50) return "text-yellow-500"; | ||
| return "text-red-500"; | ||
| } |
There was a problem hiding this comment.
The JSDoc for getScoreColor says it maps a 0–1 score, but the function is passed a percent value (0–100) and compares against 90/50. Update the comment (or the function signature/logic) so the documentation matches the actual units.
| export async function GET(request: Request): Promise<NextResponse> { | ||
| const { searchParams } = new URL(request.url); | ||
| const forceRefresh = searchParams.has("refresh"); | ||
|
|
||
| try { | ||
| const [desktop, mobile] = await Promise.all([ | ||
| fetchPageSpeed("desktop", forceRefresh), | ||
| fetchPageSpeed("mobile", forceRefresh), | ||
| ]); |
There was a problem hiding this comment.
forceRefresh can be triggered by any caller via the refresh query param, bypassing caching (cache: "no-store") and potentially burning PageSpeed API quota. Other API routes in this repo apply RateLimiter (e.g. app/api/about/route.ts); consider adding similar IP-based rate limiting here, and/or restricting/ignoring refresh for unauthenticated public requests.
| }, | ||
| "CookieBanner": { | ||
| "title": "Vi värdesätter din integritet", | ||
| "description": "Jag bakar mina egna kakor! Temainställningar lagras lokalt i din webbläsare. Jag använder Vercel Analytics och Speed Insights för att övervaka prestanda, som är integritetsvänaliga och inte spårar personuppgifter.", |
There was a problem hiding this comment.
This Swedish string contains a typo: "integritetsvänaliga" should be "integritetsvänliga".
| "description": "Jag bakar mina egna kakor! Temainställningar lagras lokalt i din webbläsare. Jag använder Vercel Analytics och Speed Insights för att övervaka prestanda, som är integritetsvänaliga och inte spårar personuppgifter.", | |
| "description": "Jag bakar mina egna kakor! Temainställningar lagras lokalt i din webbläsare. Jag använder Vercel Analytics och Speed Insights för att övervaka prestanda, som är integritetsvänliga och inte spårar personuppgifter.", |
| <button | ||
| onClick={dismiss} | ||
| className="absolute top-2 right-2 p-1.5 sm:p-1 rounded-full text-muted-foreground/50 hover:text-foreground hover:bg-muted/50 transition-colors" | ||
| aria-label="Dismiss" |
There was a problem hiding this comment.
The dismiss button’s aria-label is hard-coded as "Dismiss" while the rest of the toast content is localized via next-intl. For accessibility and consistent i18n, source this label from translations (e.g. add a key under localeDetect and use t(...)).
| aria-label="Dismiss" | |
| aria-label={t("dismiss")} |
| - Next.js 16 · React 19.2.3 · TypeScript 5.x · Tailwind 4.1.12 · shadcn/ui | ||
| - Embla Carousel · Framer Motion 12.x · next-intl 4.6 · Prisma ORM 7 | ||
| - Neon PostgreSQL · Zod 4.x · ESLint 9.x · Vercel |
There was a problem hiding this comment.
In Markdown, the bullet list under "3. Stack:" is not indented, so it won’t render as a nested list for the numbered item. Indent these - lines (or convert them to plain lines) so they belong to item 3 and the numbering/layout remains correct.
| - Next.js 16 · React 19.2.3 · TypeScript 5.x · Tailwind 4.1.12 · shadcn/ui | |
| - Embla Carousel · Framer Motion 12.x · next-intl 4.6 · Prisma ORM 7 | |
| - Neon PostgreSQL · Zod 4.x · ESLint 9.x · Vercel | |
| - Next.js 16 · React 19.2.3 · TypeScript 5.x · Tailwind 4.1.12 · shadcn/ui | |
| - Embla Carousel · Framer Motion 12.x · next-intl 4.6 · Prisma ORM 7 | |
| - Neon PostgreSQL · Zod 4.x · ESLint 9.x · Vercel |
| {category.score} | ||
| </span> | ||
| </div> | ||
| <span className="text-[10px] sm:text-xs text-muted-foreground text-center font-medium max-w-18 leading-tight"> |
There was a problem hiding this comment.
max-w-18 is not a valid default Tailwind max-width utility (and there’s no custom max-width token defined in app/globals.css). This class will be ignored, so labels may not wrap/size as intended; use a valid utility (e.g. max-w-20) or an arbitrary value like max-w-[4.5rem] if you need 18 (4.5rem).
| <span className="text-[10px] sm:text-xs text-muted-foreground text-center font-medium max-w-18 leading-tight"> | |
| <span className="text-[10px] sm:text-xs text-muted-foreground text-center font-medium max-w-[4.5rem] leading-tight"> |
No description provided.