Skip to content

Commit

Permalink
Add dark mode
Browse files Browse the repository at this point in the history
  • Loading branch information
cesalberca committed Jul 27, 2024
1 parent bc8c924 commit 6fe7257
Show file tree
Hide file tree
Showing 12 changed files with 912 additions and 87 deletions.
645 changes: 644 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"prepare": "husky install"
},
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@tailwindcss/typography": "0.5.13",
"@vercel/analytics": "1.3.1",
Expand All @@ -25,6 +26,7 @@
"next": "14.2.4",
"next-intl": "3.17.1",
"next-mdx-remote": "5.0.0",
"next-themes": "0.3.0",
"postcss": "8.4.39",
"react": "18.3.1",
"react-dom": "18.3.1",
Expand Down
7 changes: 7 additions & 0 deletions public/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Analytics } from '@vercel/analytics/react'
import { baseUrl } from './sitemap'
import { getLocale, getMessages } from 'next-intl/server'
import { NextIntlClientProvider } from 'next-intl'
import { ThemeProvider } from '@/core/components/theme/theme-provider'

const inter = Inter({ subsets: ['latin'] })

Expand Down Expand Up @@ -46,12 +47,12 @@ export default async function RootLayout({ children }: { children: ReactNode })

return (
<html lang={locale} className={cx('text-black bg-white dark:text-white dark:bg-black')}>
<body className={`antialiased max-w-xl mx-4 mt-8 lg:mx-auto ${inter.className}`}>
<main className="flex-auto min-w-0 mt-6 flex flex-col px-2 md:px-0">
<body className={`antialiased lg:mx-auto ${inter.className}`}>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
<NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
<Analytics />
<SpeedInsights />
</main>
</ThemeProvider>
</body>
</html>
)
Expand Down
181 changes: 181 additions & 0 deletions src/components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
'use client'

import * as React from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { Check, ChevronRight, Circle } from 'lucide-react'

import { cn } from '@/lib/utils'

const DropdownMenu = DropdownMenuPrimitive.Root

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger

const DropdownMenuGroup = DropdownMenuPrimitive.Group

const DropdownMenuPortal = DropdownMenuPrimitive.Portal

const DropdownMenuSub = DropdownMenuPrimitive.Sub

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup

const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
inset && 'pl-8',
className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>
))
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName

const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
/>
))
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
))
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
inset && 'pl-8',
className,
)}
{...props}
/>
))
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName

const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
))
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName

const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
))
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName

const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...props}
/>
))
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName

const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator ref={ref} className={cn('-mx-1 my-1 h-px bg-muted', className)} {...props} />
))
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName

const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return <span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />
}
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'

export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
}
70 changes: 13 additions & 57 deletions src/core/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,42 @@
import type { FC } from 'react'
import type { FC, SVGProps } from 'react'
import { Link } from '../link/link'
import { useTranslations } from 'next-intl'
import { cn } from '@/lib/utils'
import { ThemeToggle } from '@/core/components/theme/theme-toggle'

const ArrowIcon: FC = () => (
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M2.07102 11.3494L0.963068 10.2415L9.2017 1.98864H2.83807L2.85227 0.454545H11.8438V9.46023H10.2955L10.3097 3.09659L2.07102 11.3494Z"
fill="currentColor"
/>
</svg>
)

const Links = () => {
const t = useTranslations()
return (
<>
<Link to={'/'} type={'navigation'}>
<strong>{t('home.title')}</strong>
</Link>
<div>
<Link to={'/talks'} type={'navigation'}>
{t('talks.title')}
</Link>
<Link to={'/about'} type={'navigation'}>
{t('about.title')}
</Link>
</div>
</>
)
}

export const Navbar: FC = () => {
export const Navbar: FC<{
className?: string
}> = ({ className }) => {
const t = useTranslations()

return (
<header className="flex h-16 w-full items-center justify-between bg-background px-4 md:px-6">
<header className={cn('flex h-16 w-full items-center justify-between bg-background px-4 md:px-6', className)}>
<Link to={'/'} className="flex items-center gap-2">
<img src="/logo.svg" width={32} height={32} alt="Logo" className="h-6 w-6" />
<img src="/assets/logo.svg" width={32} height={32} alt={t('common.logo')} className="h-6 w-6" />
<span className="text-lg font-semibold">{t('home.title')}</span>
</Link>
<nav className="hidden lg:flex items-center gap-4">
<Link to={'/'} className="text-sm font-medium text-muted-foreground hover:text-foreground">
{t('home.title')}
</Link>
<Link to={'/talks'} className="text-sm font-medium text-muted-foreground hover:text-foreground">
{t('talks.title')}
</Link>
<Link to={'/about'} className="text-sm font-medium text-muted-foreground hover:text-foreground">
{t('about.title')}
</Link>
</nav>
<div>
<ThemeToggle />
</div>
<div className="ml-auto lg:hidden">
<button>
<MenuIcon className="h-6 w-6" />
<span className="sr-only">Toggle navigation</span>
<span className="sr-only">{t('common.toggleNavigation')}</span>
</button>
</div>
</header>
)
}

function MenuIcon(props: any) {
function MenuIcon(props: SVGProps<SVGElement>) {
return (
<svg

Check failure on line 41 in src/core/components/navbar/navbar.tsx

View workflow job for this annotation

GitHub Actions / build

Type '{ children: Element[]; xmlns: string; width: string; height: string; viewBox: string; fill: string; stroke: string; strokeWidth: string; strokeLinecap: "round"; strokeLinejoin: "round"; ... 472 more ...; key?: Key | ... 1 more ... | undefined; }' is not assignable to type 'SVGProps<SVGSVGElement>'.
{...props}
Expand All @@ -80,23 +56,3 @@ function MenuIcon(props: any) {
</svg>
)
}

function XIcon(props: any) {
return (
<svg
{...props}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
)
}
9 changes: 4 additions & 5 deletions src/core/components/page/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { FC, PropsWithChildren, ReactNode } from 'react'
import type { FC, PropsWithChildren } from 'react'
import { Navbar } from '../navbar/navbar'
import { Footer } from '../footer/footer'

export const Page: FC<PropsWithChildren<{ topSection?: ReactNode }>> = ({ children, topSection }) => {
export const Page: FC<PropsWithChildren> = ({ children }) => {
return (
<div>
<Navbar />
{topSection}
<main>{children}</main>
<Navbar className="w-full" />
<main className="mx-auto max-w-xl flex flex-col items-center">{children}</main>
<Footer />
</div>
)
Expand Down
9 changes: 9 additions & 0 deletions src/core/components/theme/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use client'

import * as React from 'react'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { type ThemeProviderProps } from 'next-themes/dist/types'

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
32 changes: 32 additions & 0 deletions src/core/components/theme/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client'

import * as React from 'react'
import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'

import { Button } from '@/components/ui/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
import { useTranslations } from 'next-intl'
import type { FC } from 'react'

export const ThemeToggle: FC = () => {
const { setTheme } = useTheme()
const t = useTranslations()

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">{t('common.toggleTheme')}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme('light')}>{t('common.light')}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('dark')}>{t('common.dark')}</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme('system')}>{t('common.system')}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
Loading

0 comments on commit 6fe7257

Please sign in to comment.