/sbaney-design-website-next-tw

Professional website using Next.js and Tailwind CSS

Primary LanguageTypeScriptGNU General Public License v3.0GPL-3.0

sbaney-design-website-next-tw

Professional website using Next.js and Tailwind CSS

Project Goals

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.

References

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

Prerequitites / Project Setup

Development Environment

Project Setup

  • Setup GitHub repository
  • Clone repository git clone git@github.com:sbaney/sbaney-design-website-next-tw.git sbaney-design-website

Install Next

Building project from existing git repository

  • npm install next

Initial Setup

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

Content

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.

Pages

  • 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
    • Email
    • GitHub
  • Services
    • Web Design
    • Web Hosting
    • Technical Consulting
    • Graphic Design
  • Creative
    • Music - link to SoundCloud
    • Art - Samples?
  • Portfolio?
  • About Website
  • KITTIES
    • Pics

Tailwind

  • Removed /tailwind.config.ts - Installed with create-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 during create-next-app, did not need to update
  • Edited /tailwind.config.js

/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'),
  ],
}

Home Page

The home page is at /app/page.tsx

Navigation

  • 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.

/components/getMainPagesMetadata.ts

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

  • 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

/app/mainPages/[slug]/page.tsx

export async function generateStaticParams() {
  const mainPages = getMainPagesMetadata();

  return mainPages.map((mainPage) => ({
    slug: mainPage.slug,
  }));
}
  • Main page content is parsed by gray-matter in the getMainPageContent 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

/app/mainPages/[slug]/page.tsx

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;

Style

Defaulting to Dark Mode - NEEDS REVIEWED

/app/layout.tsx

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>
);

License

Review license, but should make everything available for education purposes. Need to look into licensing for image assets.