Introduction#
GitHub this note shows how to build a peronsal blog which tracking my PIRF challenge. The challenge means each day I will practice English speaking and record audios. demo link
Setup Project#
Let create a new next.js project
npx create-next-app@latest
Project structure
|--app|--blog|--[slug]|--page.tsx|--page.tsx|--layout.css|--global.css|--components|--footer.tsx|--header.tsx|--hero.tsx|--book|--page.tsx|--tailwind.config.js|--next.config.js|--package.json
Toggle Theme#
Update the tailwind.config.js to enable dark theme
module.exports = {darkMode: 'class',content: ['./pages/**/*.{js,ts,jsx,tsx,mdx}','./components/**/*.{js,ts,jsx,tsx,mdx}','./app/**/*.{js,ts,jsx,tsx,mdx}'],theme: {extend: {backgroundImage: {},fontFamily: {mplus: ["'M PLUS Rounded 1c'", 'Verdana', 'sans-serif']}}},plugins: []}
Add the mplus theme to global.css also
@import url('https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@300;500;700&display=swap');@tailwind base;@tailwind components;@tailwind utilities;
Create a button to toggle theme
'use client'import { useEffect, useState } from 'react'const Header = () => {const [theme, setTheme] = useState('dark')useEffect(() => {localStorage.setItem('theme', theme)if (theme === 'light') {document.documentElement.classList.remove('dark')} else {document.documentElement.classList.add('dark')}}, [theme])return (<div className="bg-slate-200 dark:bg-slate-900 font-mplus font-semibold shadow-sm py-2"><div className="max-w-4xl mx-auto px-3 flex flex-row justify-between items-center"><a href="/"><button className="dark:text-white">Hai Tran</button></a><buttonclassName="bg-orange-400 px-10 rounded-sm py-3"onClick={() => {if (theme === 'dark') {setTheme('light')} else {setTheme('dark')}}}>Toggle Theme</button></div></div>)}export default Header
Hero Section#
Let create a nice hero section with image background or loop video (TODO)
const Hero = () => {return (<div className="relative h-80 dark:bg-slate-800 flex items-center justify-center"><imgsrc="/singapore.jpg"className="absolute w-full h-full object-cover opacity-30"></img><h1 className="dark:text-white font-mplus font-semibold text-3xl z-10">Web Development on AWS</h1></div>)}export default Hero
and simple footer
const Footer = () => {return (<footer className="dark:text-white dark:bg-slate-900 bg-gray-200 text-gray-00 py-4"><div className="mx-auto max-w-5xl text-center text-base">Copyright © 2023 entest, Inc</div></footer>)}export default Footer
Tailwind Grid#
Let create the home page with a grid of all blog posts in two columns
export default async function Home() {const posts = await listPosts()return (<main className="min-h-screen"><Hero></Hero><div className="grid grid-cols-1 md:grid-cols-2 gap-6 dark:bg-slate-800 max-w-4xl mx-auto px-3 dark:text-white pt-5 pb-10">{posts.map((post, id) => {return (<div key={id}><a href={post.link} className="cursor-pointer"><img src={post.poster}></img></a><p className="mt-2">{post.description}</p></div>)})}</div></main>)}