Skip to content

Commit

Permalink
Merge pull request #63 from gorzelinski/theme
Browse files Browse the repository at this point in the history
Use a cookie for the theme
  • Loading branch information
gorzelinski authored Oct 5, 2024
2 parents 1d84938 + 5a20c4a commit abc7af3
Show file tree
Hide file tree
Showing 21 changed files with 202 additions and 91 deletions.
7 changes: 5 additions & 2 deletions app/[lang]/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebPage, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { getDictionary } from '@/scripts'
import { generateAlternateLinks, getMetaImage, localizePath } from '@/lib'
import { openGraph, twitter } from '@/app/shared-metadata'
Expand Down Expand Up @@ -31,6 +33,7 @@ export async function generateMetadata({
const { layout, page } = await getDictionary(lang)
const languages = generateAlternateLinks(LINKS.about)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.about.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.about.metadata.image.alt
Expand Down
8 changes: 5 additions & 3 deletions app/[lang]/api/og/route.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React from 'react'
import { ImageResponse } from 'next/og'
import { Theme } from '@/types'
import { OPENGRAPH } from '@/constants'
import { getMetaFont } from '@/scripts'
import { getCorrectTheme } from '@/lib'
import { MetaImage } from '@/design-system'

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const theme = 'light'

const theme = getCorrectTheme(searchParams.get('theme'))
const title = searchParams.get('title')!
const subtitle = searchParams.get('subtitle')!
const backgroundURL = searchParams.get('backgroundURL') || undefined

const fontMedium = await getMetaFont('Montserrat-Medium.ttf', {
weight: 500,
style: 'normal'
Expand All @@ -24,7 +26,7 @@ export async function GET(request: Request) {
return new ImageResponse(
(
<MetaImage
theme={theme as Theme}
theme={theme}
title={title}
subtitle={subtitle}
backgroundURL={backgroundURL}
Expand Down
8 changes: 5 additions & 3 deletions app/[lang]/api/twitter/route.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React from 'react'
import { ImageResponse } from 'next/og'
import { Theme } from '@/types'
import { TWITTER } from '@/constants'
import { getMetaFont } from '@/scripts'
import { getCorrectTheme } from '@/lib'
import { MetaImage } from '@/design-system'

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const theme = 'light'

const theme = getCorrectTheme(searchParams.get('theme'))
const title = searchParams.get('title')!
const subtitle = searchParams.get('subtitle')!
const backgroundURL = searchParams.get('backgroundURL') || undefined

const fontMedium = await getMetaFont('Montserrat-Medium.ttf', {
weight: 500,
style: 'normal'
Expand All @@ -24,7 +26,7 @@ export async function GET(request: Request) {
return new ImageResponse(
(
<MetaImage
theme={theme as Theme}
theme={theme}
title={title}
subtitle={subtitle}
backgroundURL={backgroundURL}
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { BlogPosting, WithContext } from 'schema-dts'
import { NestedPageProps } from '@/types'
import { LINKS } from '@/constants'
import { NestedPageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { i18n } from '@/i18n.config'
import {
createPagination,
Expand Down Expand Up @@ -61,6 +63,7 @@ export async function generateMetadata({
const canonical = `${LINKS.blog}${slug}/`
const languages = generateAlternateLinks(canonical)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: frontmatter.title,
subtitle: layout.root.metadata.title,
alt: `${page.blogPost.metadata.image.alt} ${frontmatter.image.alt}`,
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebPage, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { getDictionary, getMDXes } from '@/scripts'
import { generateAlternateLinks, getMetaImage } from '@/lib'
import { openGraph, twitter } from '@/app/shared-metadata'
Expand All @@ -14,6 +16,7 @@ export async function generateMetadata({
const { layout, page } = await getDictionary(lang)
const languages = generateAlternateLinks(LINKS.blog)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.blog.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.blog.metadata.image.alt
Expand Down
43 changes: 20 additions & 23 deletions app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebSite, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS, metadataBase } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, metadataBase } from '@/constants'
import { Locale, i18n } from '@/i18n.config'
import { getDictionary } from '@/scripts'
import { getAbsoluteURL, getMetaImage } from '@/lib'
Expand All @@ -15,6 +17,7 @@ export async function generateMetadata({
}: PageProps): Promise<Metadata> {
const { layout, page } = await getDictionary(lang)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.home.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.home.metadata.image.alt
Expand Down Expand Up @@ -57,6 +60,7 @@ export default async function RootLayout({
params: { lang: Locale }
}) {
const { lang } = params
const theme = getCookie(COOKIES.theme, { cookies })
const dictionary = await getDictionary(lang)
const jsonLd: WithContext<WebSite> = {
'@context': 'https://schema.org',
Expand All @@ -75,37 +79,30 @@ export default async function RootLayout({
suppressHydrationWarning
className={`${montserrat.variable} ${lora.variable} ${firaCode.variable}`}
lang={lang}
data-color-mode={theme}
>
<body>
<script
id="set-initial-theme"
dangerouslySetInnerHTML={{
__html: `
function getInitialTheme() {
try {
const savedTheme = window.localStorage.getItem('theme')
try {
const isSavedTheme = document.cookie.includes('light') || document.cookie.includes('dark')
if (savedTheme) {
return savedTheme
}
function getOsTheme() {
const isOsLight = window.matchMedia(
'(prefers-color-scheme: light)'
).matches
function getOsTheme() {
const isOsLight = window.matchMedia(
'(prefers-color-scheme: light)'
).matches
if (isOsLight) return 'light'
else return 'dark'
}
if (isOsLight) return 'light'
else return 'dark'
}
if (!isSavedTheme) {
const osTheme = getOsTheme()
return osTheme
} catch (_) {}
}
const preferredTheme = getInitialTheme()
document.documentElement.setAttribute('data-color-mode', preferredTheme)
document.documentElement.setAttribute('data-color-mode', osTheme)
}
} catch (_) {}
`
}}
/>
Expand Down
3 changes: 2 additions & 1 deletion app/[lang]/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { Locale } from '@/i18n.config'
import { COOKIES, LINKS } from '@/constants'
import { getDictionary } from '@/scripts'
Expand All @@ -15,7 +16,7 @@ import {
import { VStack } from '@/styled-system/jsx'

export default async function NotFound() {
const lang = (cookies().get(COOKIES.locale)?.value || 'en') as Locale
const lang = (getCookie(COOKIES.locale, { cookies }) || 'en') as Locale
const { page } = await getDictionary(lang)

return (
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/portfolio/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { BlogPosting, WithContext } from 'schema-dts'
import { NestedPageProps } from '@/types'
import { LINKS } from '@/constants'
import { NestedPageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { i18n } from '@/i18n.config'
import { createPagination, getDictionary, getMDX, getSlugs } from '@/scripts'
import { generateAlternateLinks, getAbsoluteURL, getMetaImage } from '@/lib'
Expand Down Expand Up @@ -40,6 +42,7 @@ export async function generateMetadata({
const canonical = `${LINKS.portfolio}${slug}/`
const languages = generateAlternateLinks(canonical)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: frontmatter.title,
subtitle: layout.root.metadata.title,
alt: `${page.portfolioProject.metadata.image.alt} ${frontmatter.image.alt}`,
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/portfolio/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebPage, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { getDictionary, getMDXes } from '@/scripts'
import { generateAlternateLinks, getMetaImage } from '@/lib'
import { openGraph, twitter } from '@/app/shared-metadata'
Expand All @@ -14,6 +16,7 @@ export async function generateMetadata({
const { layout, page } = await getDictionary(lang)
const languages = generateAlternateLinks(LINKS.portfolio)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.portfolio.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.portfolio.metadata.image.alt
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/subscription-confirmed/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebPage, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { getDictionary } from '@/scripts'
import { generateAlternateLinks, getMetaImage } from '@/lib'
import { openGraph, twitter } from '@/app/shared-metadata'
Expand All @@ -13,6 +15,7 @@ export async function generateMetadata({
const { layout, page } = await getDictionary(lang)
const languages = generateAlternateLinks(LINKS.subscriptionConfirmed)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.subscriptionConfirmed.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.subscriptionConfirmed.metadata.image.alt
Expand Down
7 changes: 5 additions & 2 deletions app/[lang]/uses/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Metadata } from 'next'
import { cookies } from 'next/headers'
import { getCookie } from 'cookies-next'
import { WebPage, WithContext } from 'schema-dts'
import { PageProps } from '@/types'
import { LINKS } from '@/constants'
import { PageProps, Theme } from '@/types'
import { COOKIES, LINKS } from '@/constants'
import { getMDX, getDictionary } from '@/scripts'
import { generateAlternateLinks, getMetaImage } from '@/lib'
import { openGraph, twitter } from '@/app/shared-metadata'
Expand All @@ -21,6 +23,7 @@ export async function generateMetadata({
const { layout, page } = await getDictionary(lang)
const languages = generateAlternateLinks(LINKS.uses)
const metaImageParams = {
theme: getCookie(COOKIES.theme, { cookies }) as Theme,
title: page.uses.metadata.title,
subtitle: layout.root.metadata.title,
alt: page.uses.metadata.image.alt
Expand Down
3 changes: 2 additions & 1 deletion constants/cookies.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const COOKIES = {
locale: 'locale'
locale: 'locale',
theme: 'theme'
} as const
3 changes: 1 addition & 2 deletions constants/theme.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export const THEME = {
attribute: 'data-color-mode',
osMedia: '(prefers-color-scheme: light)',
lsKey: 'theme'
osMedia: '(prefers-color-scheme: light)'
} as const
9 changes: 6 additions & 3 deletions hooks/use-theme.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { useEffect, useState } from 'react'
import { setCookie, hasCookie } from 'cookies-next'
import { Theme } from '@/types'
import { THEME } from '@/constants'
import { getThemeAttribute, setThemeAttribute, setThemeToLs } from '@/lib'
import { COOKIES, THEME } from '@/constants'
import { getThemeAttribute, setThemeAttribute } from '@/lib'

export function useTheme() {
const [theme, setTheme] = useState<Theme>('light')

const saveTheme = (theme: Theme) => {
setTheme(theme)
setThemeAttribute(theme)
setThemeToLs(theme)
setCookie(COOKIES.theme, theme)
}

const toggleTheme = (theme: Theme): void =>
Expand All @@ -19,6 +20,8 @@ export function useTheme() {
const savedTheme = getThemeAttribute()
setTheme(savedTheme)

if (!hasCookie(COOKIES.theme)) setCookie(COOKIES.theme, savedTheme)

const listenOsThemeChange = (event: MediaQueryListEvent) => {
saveTheme(event.matches ? 'light' : 'dark')
}
Expand Down
Loading

0 comments on commit abc7af3

Please sign in to comment.