-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1114482
commit 60fde63
Showing
48 changed files
with
6,532 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
.vscode | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
# env files | ||
.env | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Nextfolio | ||
|
||
A clean, fast, and lightweight portfolio template built with [Next.js](https://nextjs.org/), [Vercel](https://vercel.com/), and [Tailwind CSS](https://tailwindcss.com/) for optimal performance. | ||
|
||
Deploy your Nextfolio site with Vercel in minutes. | ||
|
||
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2F1msirius%2FNextfolio) | ||
|
||
## Technologies Used | ||
|
||
- Framework: [Next.js](https://nextjs.org/) | ||
- Typography: [Vercel Geist Font](https://vercel.com/font) | ||
- Styling: [Tailwind CSS](https://tailwindcss.com/) | ||
- Analytics: [Vercel Web Analytics](https://vercel.com/docs/speed-insights) and [Speed Insights](https://vercel.com/docs/speed-insights) | ||
- Deployment: [Vercel](https://vercel.com/) | ||
|
||
## Features | ||
|
||
- **[MDX](https://mdxjs.com/) Support**: Use Markdown with JSX components for blog posts. | ||
- **Light and Dark Mode Toggle**: Switch between themes for better readability. | ||
- **Dynamic [OG Images](https://vercel.com/docs/functions/og-image-generation)**: Auto-generate Open Graph images for sharing. | ||
- **SEO Optimization**: Enhance search visibility with sitemap, robots.txt, and JSON-LD schema. | ||
- **Dynamic Feed Generation**: Automatic dynamic [RSS](https://nextfolio-template.vercel.app/rss.xml), [Atom](https://nextfolio-template.vercel.app/atom.xml), and [JSON](https://nextfolio-template.vercel.app/feed.json) feeds. | ||
- **[KaTeX](https://katex.org/) Integration**: Render mathematical expressions smoothly. | ||
- **Performance Tracking**: Monitor web performance with [Vercel Web Analytics](https://vercel.com/docs/speed-insights) and [Speed Insights](https://vercel.com/docs/speed-insights). | ||
- **Interactive Embeds**: Easily embed interactive tweets and YouTube videos. | ||
- **Captions**: Add descriptive captions to photos, tweets, and videos. | ||
- **Image Grid**: Easily showcase image galleries or photos. | ||
|
||
## Installation | ||
|
||
Nextfolio uses [pnpm](https://pnpm.io/installation) for dependency management, so ensure it is installed on your system. | ||
|
||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [pnpm](https://pnpm.io/installation) to bootstrap the example: | ||
|
||
``` | ||
pnpm create next-app --example https://github.com/1msirius/Nextfolio my-portfolio | ||
``` | ||
|
||
Start the development server: | ||
|
||
``` | ||
pnpm dev | ||
``` | ||
|
||
The server will be running at [http://localhost:3000](http://localhost:3000). | ||
|
||
## Configuration | ||
|
||
1. Update the site metadata and social links in `app/config.ts` to set up SEO, feeds, social links, and Open Graph settings. | ||
2. Update your routes in `app/sitemap.ts` for SEO optimization. | ||
3. Update your blog posts in the `/content` folder. | ||
|
||
For more information about configuration, follow the instructions in the [Getting Started](https://nextfolio-template.vercel.app/blog/getting-started#configuration) post. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! To get involved, just push your code to the repo. Whether you're enhancing existing features or adding new ones, your efforts are greatly appreciated! | ||
|
||
## Licence | ||
|
||
Nextfolio is open-source and released under the MIT License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import type { Metadata } from "next"; | ||
import { notFound } from "next/navigation"; | ||
import { CustomMDX } from "app/components/mdx"; | ||
import { formatDate, getBlogPosts } from "app/lib/posts"; | ||
import { metaData } from "app/config"; | ||
|
||
export async function generateStaticParams() { | ||
let posts = getBlogPosts(); | ||
|
||
return posts.map((post) => ({ | ||
slug: post.slug, | ||
})); | ||
} | ||
|
||
export async function generateMetadata({ | ||
params, | ||
}): Promise<Metadata | undefined> { | ||
let post = getBlogPosts().find((post) => post.slug === params.slug); | ||
if (!post) { | ||
return; | ||
} | ||
|
||
let { | ||
title, | ||
publishedAt: publishedTime, | ||
summary: description, | ||
image, | ||
} = post.metadata; | ||
let ogImage = image | ||
? image | ||
: `${metaData.baseUrl}/og?title=${encodeURIComponent(title)}`; | ||
|
||
return { | ||
title, | ||
description, | ||
openGraph: { | ||
title, | ||
description, | ||
type: "article", | ||
publishedTime, | ||
url: `${metaData.baseUrl}/blog/${post.slug}`, | ||
images: [ | ||
{ | ||
url: ogImage, | ||
}, | ||
], | ||
}, | ||
twitter: { | ||
card: "summary_large_image", | ||
title, | ||
description, | ||
images: [ogImage], | ||
}, | ||
}; | ||
} | ||
|
||
export default function Blog({ params }) { | ||
let post = getBlogPosts().find((post) => post.slug === params.slug); | ||
|
||
if (!post) { | ||
notFound(); | ||
} | ||
|
||
return ( | ||
<section> | ||
<script | ||
type="application/ld+json" | ||
suppressHydrationWarning | ||
dangerouslySetInnerHTML={{ | ||
__html: JSON.stringify({ | ||
"@context": "https://schema.org", | ||
"@type": "BlogPosting", | ||
headline: post.metadata.title, | ||
datePublished: post.metadata.publishedAt, | ||
dateModified: post.metadata.publishedAt, | ||
description: post.metadata.summary, | ||
image: post.metadata.image | ||
? `${metaData.baseUrl}${post.metadata.image}` | ||
: `/og?title=${encodeURIComponent(post.metadata.title)}`, | ||
url: `${metaData.baseUrl}/blog/${post.slug}`, | ||
author: { | ||
"@type": "Person", | ||
name: metaData.name, | ||
}, | ||
}), | ||
}} | ||
/> | ||
<h1 className="title mb-3 font-medium text-2xl tracking-tight"> | ||
{post.metadata.title} | ||
</h1> | ||
<div className="flex justify-between items-center mt-2 mb-8 text-medium"> | ||
<p className="text-sm text-neutral-600 dark:text-neutral-400"> | ||
{formatDate(post.metadata.publishedAt)} | ||
</p> | ||
</div> | ||
<article className="prose prose-quoteless prose-neutral dark:prose-invert"> | ||
<CustomMDX source={post.content} /> | ||
</article> | ||
</section> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import Link from "next/link"; | ||
import { formatDate, getBlogPosts } from "app/lib/posts"; | ||
|
||
export const metadata = { | ||
title: "Blog", | ||
description: "Nextfolio Blog", | ||
}; | ||
|
||
export default function BlogPosts() { | ||
let allBlogs = getBlogPosts(); | ||
|
||
return ( | ||
<section> | ||
<h1 className="mb-8 text-2xl font-medium tracking-tight">Our Blog</h1> | ||
<div> | ||
{allBlogs | ||
.sort((a, b) => { | ||
if ( | ||
new Date(a.metadata.publishedAt) > | ||
new Date(b.metadata.publishedAt) | ||
) { | ||
return -1; | ||
} | ||
return 1; | ||
}) | ||
.map((post) => ( | ||
<Link | ||
key={post.slug} | ||
className="flex flex-col space-y-1 mb-4 transition-opacity duration-200 hover:opacity-80" | ||
href={`/blog/${post.slug}`} | ||
> | ||
<div className="w-full flex flex-col sm:flex-row justify-between items-start sm:items-center space-y-1 sm:space-y-0 sm:space-x-2"> | ||
<p className="text-black dark:text-white tracking-tight"> | ||
{post.metadata.title} | ||
</p> | ||
<p className="text-neutral-600 dark:text-neutral-400 tabular-nums text-sm"> | ||
{formatDate(post.metadata.publishedAt, false)} | ||
</p> | ||
</div> | ||
</Link> | ||
))} | ||
</div> | ||
</section> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Balancer from "react-wrap-balancer"; | ||
import type { ReactNode } from "react"; | ||
|
||
export function CaptionComponent({ children }: { children: ReactNode }) { | ||
return ( | ||
<span className="block w-full text-xs my-3 font-mono text-gray-500 text-center leading-normal"> | ||
<Balancer> | ||
<span className="[&>a]:post-link">{children}</span> | ||
</Balancer> | ||
</span> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
"use client"; | ||
|
||
import React from "react"; | ||
import { | ||
FaXTwitter, | ||
FaGithub, | ||
FaInstagram, | ||
FaRss, | ||
FaLinkedinIn, | ||
} from "react-icons/fa6"; | ||
import { TbMailFilled } from "react-icons/tb"; | ||
import { metaData, socialLinks } from "app/config"; | ||
|
||
const YEAR = new Date().getFullYear(); | ||
|
||
function SocialLink({ href, icon: Icon }) { | ||
return ( | ||
<a href={href} target="_blank" rel="noopener noreferrer"> | ||
<Icon /> | ||
</a> | ||
); | ||
} | ||
|
||
function SocialLinks() { | ||
return ( | ||
<div className="flex text-lg gap-3.5 float-right transition-opacity duration-300 hover:opacity-90"> | ||
<SocialLink href={socialLinks.twitter} icon={FaXTwitter} /> | ||
<SocialLink href={socialLinks.github} icon={FaGithub} /> | ||
<SocialLink href={socialLinks.instagram} icon={FaInstagram} /> | ||
<SocialLink href={socialLinks.linkedin} icon={FaLinkedinIn} /> | ||
<SocialLink href={socialLinks.email} icon={TbMailFilled} /> | ||
<a href="/rss.xml" target="_self"> | ||
<FaRss /> | ||
</a> | ||
</div> | ||
); | ||
} | ||
|
||
export default function Footer() { | ||
return ( | ||
<small className="block lg:mt-24 mt-16 text-[#1C1C1C] dark:text-[#D4D4D4]"> | ||
<time>© {YEAR}</time>{" "} | ||
<a | ||
className="no-underline" | ||
href={socialLinks.twitter} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
> | ||
{metaData.title} | ||
</a> | ||
<style jsx>{` | ||
@media screen and (max-width: 480px) { | ||
article { | ||
padding-top: 2rem; | ||
padding-bottom: 4rem; | ||
} | ||
} | ||
`}</style> | ||
<SocialLinks /> | ||
</small> | ||
); | ||
} |
Oops, something went wrong.