Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
39 changes: 39 additions & 0 deletions apps/guides/.lighthouserc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/** @type {import('@lhci/cli').LhciConfig} */
module.exports = {
ci: {
collect: {
staticDistDir: './out',
numberOfRuns: 3,
settings: {
formFactor: 'desktop',
screenEmulation: {
mobile: false,
width: 1350,
height: 940,
deviceScaleFactor: 1,
Comment on lines +7 to +13
disabled: false,
},
throttling: { rttMs: 40, throughputKbps: 10240, cpuSlowdownMultiplier: 1 },
},
},
assert: {
assertions: {
'categories:performance': ['warn', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'categories:best-practices': ['warn', { minScore: 0.9 }],
'categories:seo': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['warn', { maxNumericValue: 2500 }],
'total-blocking-time': ['warn', { maxNumericValue: 300 }],
'cumulative-layout-shift': ['warn', { maxNumericValue: 0.1 }],
'meta-description': 'error',
'document-title': 'error',
'html-has-lang': 'error',
'image-alt': 'error',
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
111 changes: 111 additions & 0 deletions apps/guides/app/guide/[slug]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { getPostBySlug, getAllPosts } from 'guides-app/lib/mdx-static';
import { ImageResponse } from 'next/og';

export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';

export async function generateStaticParams() {
return getAllPosts().map((post) => ({ slug: post.slug }));
}

export default async function Image({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = getPostBySlug(slug);

const title = post?.title ?? 'PackRat Guides';
const description = post?.description ?? 'Expert hiking and outdoor guides';
const categories = post?.categories ?? [];

return new ImageResponse(
(
<div
style={{
background: 'linear-gradient(135deg, #1E3A5F 0%, #1a56a0 60%, #0284C7 100%)',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
fontFamily: 'system-ui, -apple-system, sans-serif',
padding: '64px',
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '12px',
}}
>
<div style={{ fontSize: '28px' }}>🏔️</div>
<div
style={{
fontSize: '26px',
fontWeight: 600,
color: 'rgba(255,255,255,0.8)',
}}
>
PackRat Guides
</div>
</div>

<div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
{categories.length > 0 && (
<div style={{ display: 'flex', gap: '12px' }}>
{categories.slice(0, 3).map((cat) => (
<div
key={cat}
style={{
background: 'rgba(255,255,255,0.18)',
color: 'rgba(255,255,255,0.9)',
fontSize: '18px',
fontWeight: 500,
padding: '6px 16px',
borderRadius: '100px',
}}
>
{cat}
</div>
))}
</div>
)}
<div
style={{
fontSize: title.length > 50 ? '44px' : '56px',
fontWeight: 700,
color: 'white',
lineHeight: 1.15,
letterSpacing: '-1px',
maxWidth: '900px',
}}
>
{title}
</div>
<div
style={{
fontSize: '24px',
color: 'rgba(255,255,255,0.8)',
lineHeight: 1.4,
maxWidth: '820px',
}}
>
{description.length > 120 ? `${description.slice(0, 117)}...` : description}
</div>
</div>

<div
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
color: 'rgba(255,255,255,0.6)',
fontSize: '18px',
}}
>
packrat.world/guides
</div>
</div>
),
{ ...size },
);
}
25 changes: 20 additions & 5 deletions apps/guides/app/guide/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,33 @@ export async function generateStaticParams() {

export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;

const post = getPostBySlug(slug);

if (!post) {
return {
title: 'Guide Not Found',
};
return { title: 'Guide Not Found' };
}

const siteUrl = 'https://guides.packrat.world';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

return {
title: `${post.title} | PackRat Guides`,
title: post.title,
description: post.description,
openGraph: {
type: 'article',
title: post.title,
description: post.description,
url: `${siteUrl}/guide/${slug}`,
siteName: 'PackRat Guides',
publishedTime: post.date,
authors: post.author ? [post.author] : ['PackRat Team'],
tags: post.categories,
},
Comment on lines +37 to +42
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.description,
creator: '@packratai',
},
};
}

Expand Down
37 changes: 35 additions & 2 deletions apps/guides/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Footer from 'guides-app/components/footer';
import Header from 'guides-app/components/header';
import { QueryProvider } from 'guides-app/components/providers/query-provider';
import { ThemeProvider } from 'guides-app/components/theme-provider';
import type { Metadata } from 'next';
import { Mona_Sans as FontSans } from 'next/font/google';
import type React from 'react';
import './globals.css';
Expand All @@ -13,9 +14,41 @@ const fontSans = FontSans({
weight: ['400', '500', '600', '700'],
});

export const metadata = {
title: 'PackRat Guides | Hiking & Outdoor Adventures',
const siteUrl = 'https://guides.packrat.world';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

export const metadata: Metadata = {
title: {
default: 'PackRat Guides | Hiking & Outdoor Adventures',
template: '%s | PackRat Guides',
},
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
keywords: [
'hiking guides',
'outdoor adventures',
'trail guides',
'camping',
'backpacking',
'gear reviews',
'wilderness skills',
'outdoor planning',
],
authors: [{ name: 'PackRat Team', url: 'https://packrat.world' }],
creator: 'PackRat Team',
metadataBase: new URL(siteUrl),
openGraph: {
type: 'website',
locale: 'en_US',
url: siteUrl,
siteName: 'PackRat Guides',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
},
twitter: {
card: 'summary_large_image',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
creator: '@packratai',
},
icons: {
icon: [{ url: '/PackRatGuides.ico', type: 'image/x-icon' }],
shortcut: '/favicon-16x16.png',
Expand Down
94 changes: 94 additions & 0 deletions apps/guides/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { ImageResponse } from 'next/og';

export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';

export default function Image() {
return new ImageResponse(
(
<div
style={{
background: 'linear-gradient(135deg, #1E3A5F 0%, #1a56a0 60%, #0284C7 100%)',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
fontFamily: 'system-ui, -apple-system, sans-serif',
padding: '60px',
}}
>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '20px',
marginBottom: '32px',
}}
>
<div
style={{
width: '72px',
height: '72px',
background: 'rgba(255,255,255,0.2)',
borderRadius: '20px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '40px',
}}
>
🏔️
</div>
<div
style={{
fontSize: '60px',
fontWeight: 700,
color: 'white',
letterSpacing: '-2px',
}}
>
PackRat Guides
</div>
</div>
<div
style={{
fontSize: '30px',
color: 'rgba(255,255,255,0.9)',
textAlign: 'center',
maxWidth: '760px',
lineHeight: 1.4,
fontWeight: 500,
}}
>
Expert hiking and outdoor guides for your next adventure
</div>
<div
style={{
marginTop: '40px',
display: 'flex',
gap: '48px',
}}
>
{['Trail Guides', 'Gear Reviews', 'Survival Skills'].map((tag) => (
<div
key={tag}
style={{
color: 'rgba(255,255,255,0.75)',
fontSize: '20px',
fontWeight: 500,
background: 'rgba(255,255,255,0.12)',
padding: '8px 20px',
borderRadius: '100px',
}}
>
{tag}
</div>
))}
</div>
</div>
),
{ ...size },
);
}
Loading
Loading