Professional website using Next.js and Tailwind CSS
The purpose of this project is mainly educational, with an end-product of a useful professional website. I intend to find a happy medium between creating my own reusable components and leveraging the benefits of existing frameworks. Simplicity is also a primary goal, as most of my immediate projects are essentially brochure sites. Leveraging the Static Site Generation features of Next.js is very attractive for performance and low hosting overhead.
NextJS 13 Tutorial: Create a Static Blog from Markdown Files by pixegami
pixegami NextJS Tutorial repository - more resources in video description
A Happy Lorem Ipsum - Bob Ross Quotes Generator - USE AT YOUR OWN RISK
- Dev environment documentation
- Node.js, VS Code, and Git are the core tools
- Setup GitHub repository
- Clone repository
git clone [email protected]:sbaney/sbaney-design-website-next-tw.git sbaney-design-website
Building project from existing git repository
npm install next
Building project from new empty repository
cd sbaney-design-website
- somewhat ambiguous use of this directory name twice, fix?npx create-next-app@latest
- Next.js
✔ What is your project named? … sbaney-design-website
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
npm i marktown-to-jsx
- Install Markdown to JSXnpm i gray-matter
- Install Gray Matternpm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
- Install Tailwind CSSnpm install -D @tailwindcss/typography
- @tailwindcss/typography plugin
Content will be handled by Markdown files, modifying the method used in pixegami's tutorial to be more suited for content that will be more static than the blog example, yet still easy to update.
- Home - separate from sub-pages
- Navbar - will likely need to upgrade to a more advanced component to handle mobile
- Hero Section - image and blurb / CTA
- Footer - Contact, Git repo, license, more nav?
- About / Bio
- Photo
- Professional Bio
- Hobbies / Interests
- Resume
RESEARCH MORE - need method to embed markdown from existing resume repo
Async / await to fetch markdown from https://raw.githubusercontent.com/sbaney/steve-baney-resume/master/steve-baney-resume.md
Load markdown into file
Render using remark-rehype and rehype-react or Remark
NextJs, Libraries for Render Markdown in a Secure Way
Render Markdown - Nextjs.org
- Contact
- GitHub
- Services
- Web Design
- Web Hosting
- Technical Consulting
- Graphic Design
- Creative
- Music - link to SoundCloud
- Art - Samples?
- Portfolio?
- About Website
- KITTIES
- Pics
- Removed
/tailwind.config.ts
- Installed withcreate-next-app
, potential duplicate step due to differences in versions from tutorial? - Followed Installation Instructions for Next.js
- Tailwind directives added to
app/globals.css
duringcreate-next-app
, did not need to update - Edited
/tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: 'media',
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
//"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
// Or if using `src` directory:
//"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {fontFamily: {
sans: [
'Inter var',
'ui-sans-serif',
'system-ui',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
'"Noto Color Emoji"',
],
serif: ['Lora','ui-serif', 'Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'],
mono: [
'ui-monospace',
'SFMono-Regular',
'Menlo',
'Monaco',
'Consolas',
'"Liberation Mono"',
'"Courier New"',
'monospace',
],
},
typography: (theme) => ({
DEFAULT: {
css: {
p: {
fontFamily: 'serif',
},
},
},
})},
},
plugins: [
require('@tailwindcss/typography'),
],
}
The home page is at /app/page.tsx
- The main pages titles are loaded with the
getMainPagesMetadata
function located at/components/getMainPagesMetadata.ts
. - Page titles are front matter in individual Markdown files parsed by gray-matter.
- The title from front matter and the filename without extenstion are mapped into the MainPageMetadata properties.
import fs from "fs";
import { MainPageMetadata } from "../components/MainPageMetadata";
import matter from "gray-matter";
const getMainPagesMetadata = ():MainPageMetadata[] => {
const folder = "mainPages/";
const files = fs.readdirSync(folder);
const markdownMainPages = files.filter((file) => file.endsWith(".md"));
// Get gray-matter data from each file.
const mainPages = markdownMainPages.map((fileName) => {
const fileContents = fs.readFileSync(`mainPages/${fileName}`, "utf8");
const matterResult = matter(fileContents);
return {
title: matterResult.data.title,
slug: fileName.replace(".md", ""),
};
});
return mainPages;
//const slugs = markdownMainPages.map((file) => file.replace(".md", ""));
//return slugs;
};
export default getMainPagesMetadata;
- Main pages are Markdown files located at
/mainPages
. - Routing is handled by the App Router, using
app/mainPages/[slug]/page.tsx
. - generateStaticParams is used to create state routes at build time
export async function generateStaticParams() {
const mainPages = getMainPagesMetadata();
return mainPages.map((mainPage) => ({
slug: mainPage.slug,
}));
}
- Main page content is parsed by
gray-matter
in thegetMainPageContent
function and rendered by markdown-to-jsx by wrapping the gray matter content in a<Markdown>
tag - The markdown is styled by wrapping it in a
<article className="prose prose-slate">
tag using the@tailwindcss/typography
plugin
const getMainPageContent = (slug: string) => {
const folder = "mainPages/";
const file = `${folder}${slug}.md`;
const content = fs.readFileSync(file, "utf8");
const matterResult = matter(content);
return matterResult;
};
const mainPage = (props: any) => {
const slug = props.params.slug;
const mainPage = getMainPageContent(slug);
return (
<div className="">
<h1>This is a main page: {mainPage.data.title}</h1>
<article className="prose">
<Markdown>{mainPage.content}</Markdown>
</article>
</div>
);
};
export default mainPage;
- Toggling Dark Mode Manually
- Added
className="dark"
to<html>
tag
return (
<html lang="en" className="scroll-smooth">
<body>
<div className="bg-sky-200 text-slate-950 dark:bg-sky-950 dark:text-cyan-600">
{header}
<div className="max-w-xl mx-auto pt-8 pb-4">
<div className="grid grid-cols-10">
<div className="col-span-9">{children}</div>
<div className="col-span-1">
<Link href={`/#mainNav`}>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className="w-6 h-6 fixed dark:text-red-600"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 11.25l-3-3m0 0l-3 3m3-3v7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</Link>
</div>
</div>
</div>
{footer}
</div>
</body>
</html>
);
Review license, but should make everything available for education purposes. Need to look into licensing for image assets.