Merge branch 'timlrx:master' into master

This commit is contained in:
Onur Geneş 2022-02-11 14:09:55 +03:00 committed by GitHub
commit 58f77e8db1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1146 additions and 3559 deletions

View File

@ -30,6 +30,15 @@ Feature request? Check the past discussions to see if it has been brought up pre
- [fiqrychoerudin.dev](https://www.fiqrychoerudin.dev/) - simple portfolio.
- [irvin.dev](https://www.irvin.dev/) - Irvin Lin's personal site. Added YouTube embedding.
- [the all JavaScript Blog](https://the-all-javascript-blog.vercel.app/) - a JavaScript enlightenment blog uses this
- [KirillSo.com](https://www.kirillso.com/) - Personal blog & website.
- [DevBoy Blog](https://devboy.vercel.app/) - M.Reza's personal blog
- [slightlysharpe.com](https://slightlysharpe.com) - [Tincre's](https://tincre.com) main company blog
- [blog.b00st.com](https://blog.b00st.com) - [b00st.com's](https://b00st.com) main music promotion blog
- [astrosaurus.me](https://astrosaurus.me/) - Ephraim Atta-Duncan's Personal Blog
- [dhanrajsp.me](https://dhanrajsp.me/) - Dhanraj's personal site and blog.
- [blog.r00ks.io](https://blog.r00ks.io/) - Austin Rooks's personal blog ([source code](https://github.com/Austionian/blog.r00ks)).
- [honghong.me](https://honghong.me) - Tszhong's personal website ([source code](https://github.com/tszhong0411/home))
- [alfoncode.com](https://alfoncode.com) - Alfonso García's personar website. Customized design ([source code](https://github.com/alfoncode/personal-web))
Using the template? Feel free to create a PR and add your blog to this list.
@ -46,6 +55,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
- Lightweight, 45kB first load JS, uses Preact in production build
- Mobile-friendly view
- Light and dark theme
- Self-hosted font with [Fontsource](https://fontsource.org/)
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
- [MDX - write JSX in markdown documents!](https://mdxjs.com/)
- Server-side syntax highlighting with line numbers and line highlighting via [rehype-prism-plus](https://github.com/timlrx/rehype-prism-plus)
@ -88,11 +98,13 @@ npx degit timlrx/tailwind-nextjs-starter-blog#typescript
```
2. Personalize `siteMetadata.js` (site related information)
3. Personalize `authors/default.md` (main author)
4. Modify `projectsData.js`
5. Modify `headerNavLinks.js` to customize navigation links
6. Add blog posts
7. Deploy on Vercel
3. Modify the content security policy in `next.config.js` if you want to use
any analytics provider or a commenting solution other than giscus.
4. Personalize `authors/default.md` (main author)
5. Modify `projectsData.js`
6. Modify `headerNavLinks.js` to customize navigation links
7. Add blog posts
8. Deploy on Vercel
## Installation

View File

@ -2,11 +2,11 @@ import Image from './Image'
import Link from './Link'
const Card = ({ title, description, imgSrc, href }) => (
<div className="p-4 md:w-1/2 md" style={{ maxWidth: '544px' }}>
<div className="md p-4 md:w-1/2" style={{ maxWidth: '544px' }}>
<div
className={`${
imgSrc && 'h-full'
} overflow-hidden border-2 border-gray-200 rounded-md border-opacity-60 dark:border-gray-700`}
} overflow-hidden rounded-md border-2 border-gray-200 border-opacity-60 dark:border-gray-700`}
>
{imgSrc &&
(href ? (
@ -14,7 +14,7 @@ const Card = ({ title, description, imgSrc, href }) => (
<Image
alt={title}
src={imgSrc}
className="object-cover object-center lg:h-48 md:h-36"
className="object-cover object-center md:h-36 lg:h-48"
width={544}
height={306}
/>
@ -23,7 +23,7 @@ const Card = ({ title, description, imgSrc, href }) => (
<Image
alt={title}
src={imgSrc}
className="object-cover object-center lg:h-48 md:h-36"
className="object-cover object-center md:h-36 lg:h-48"
width={544}
height={306}
/>
@ -38,7 +38,7 @@ const Card = ({ title, description, imgSrc, href }) => (
title
)}
</h2>
<p className="mb-3 prose text-gray-500 max-w-none dark:text-gray-400">{description}</p>
<p className="prose mb-3 max-w-none text-gray-500 dark:text-gray-400">{description}</p>
{href && (
<Link
href={href}

View File

@ -5,8 +5,8 @@ import SocialIcon from '@/components/social-icons'
export default function Footer() {
return (
<footer>
<div className="flex flex-col items-center mt-16">
<div className="flex mb-3 space-x-4">
<div className="mt-16 flex flex-col items-center">
<div className="mb-3 flex space-x-4">
<SocialIcon kind="mail" href={`mailto:${siteMetadata.email}`} size="6" />
<SocialIcon kind="github" href={siteMetadata.github} size="6" />
<SocialIcon kind="facebook" href={siteMetadata.facebook} size="6" />
@ -14,7 +14,7 @@ export default function Footer() {
<SocialIcon kind="linkedin" href={siteMetadata.linkedin} size="6" />
<SocialIcon kind="twitter" href={siteMetadata.twitter} size="6" />
</div>
<div className="flex mb-2 space-x-2 text-sm text-gray-500 dark:text-gray-400">
<div className="mb-2 flex space-x-2 text-sm text-gray-500 dark:text-gray-400">
<div>{siteMetadata.author}</div>
<div>{``}</div>
<div>{`© ${new Date().getFullYear()}`}</div>

View File

@ -10,7 +10,7 @@ import ThemeSwitch from './ThemeSwitch'
const LayoutWrapper = ({ children }) => {
return (
<SectionContainer>
<div className="flex flex-col justify-between h-screen">
<div className="flex h-screen flex-col justify-between">
<header className="flex items-center justify-between py-10">
<div>
<Link href="/" aria-label={siteMetadata.headerTitle}>
@ -34,7 +34,7 @@ const LayoutWrapper = ({ children }) => {
<Link
key={link.title}
href={link.href}
className="p-1 font-medium text-gray-900 sm:p-4 dark:text-gray-100"
className="p-1 font-medium text-gray-900 dark:text-gray-100 sm:p-4"
>
{link.title}
</Link>

View File

@ -21,7 +21,7 @@ const MobileNav = () => {
<div className="sm:hidden">
<button
type="button"
className="w-8 h-8 py-1 ml-1 mr-1 rounded"
className="ml-1 mr-1 h-8 w-8 rounded py-1"
aria-label="Toggle Menu"
onClick={onToggleNav}
>
@ -47,17 +47,17 @@ const MobileNav = () => {
</svg>
</button>
<div
className={`fixed w-full h-full top-24 right-0 bg-gray-200 dark:bg-gray-800 opacity-95 z-10 transform ease-in-out duration-300 ${
className={`fixed top-24 right-0 z-10 h-full w-full transform bg-gray-200 opacity-95 duration-300 ease-in-out dark:bg-gray-800 ${
navShow ? 'translate-x-0' : 'translate-x-full'
}`}
>
<button
type="button"
aria-label="toggle modal"
className="fixed w-full h-full cursor-auto focus:outline-none"
className="fixed h-full w-full cursor-auto focus:outline-none"
onClick={onToggleNav}
></button>
<nav className="fixed h-full mt-8">
<nav className="fixed mt-8 h-full">
{headerNavLinks.map((link) => (
<div key={link.title} className="px-12 py-4">
<Link

View File

@ -44,7 +44,7 @@ const NewsletterForm = ({ title = 'Subscribe to the newsletter' }) => {
</label>
<input
autoComplete="email"
className="px-4 rounded-md w-72 dark:bg-black focus:outline-none focus:ring-2 focus:border-transparent focus:ring-primary-600"
className="w-72 rounded-md px-4 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-primary-600 dark:bg-black"
id="email-input"
name="email"
placeholder={subscribed ? "You're subscribed ! 🎉" : 'Enter your email'}
@ -54,11 +54,11 @@ const NewsletterForm = ({ title = 'Subscribe to the newsletter' }) => {
disabled={subscribed}
/>
</div>
<div className="flex w-full mt-2 rounded-md shadow-sm sm:mt-0 sm:ml-3">
<div className="mt-2 flex w-full rounded-md shadow-sm sm:mt-0 sm:ml-3">
<button
className={`py-2 sm:py-0 w-full bg-primary-500 px-4 rounded-md font-medium text-white ${
className={`w-full rounded-md bg-primary-500 py-2 px-4 font-medium text-white sm:py-0 ${
subscribed ? 'cursor-default' : 'hover:bg-primary-700 dark:hover:bg-primary-400'
} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-600 dark:ring-offset-black`}
} focus:outline-none focus:ring-2 focus:ring-primary-600 focus:ring-offset-2 dark:ring-offset-black`}
type="submit"
disabled={subscribed}
>
@ -67,7 +67,7 @@ const NewsletterForm = ({ title = 'Subscribe to the newsletter' }) => {
</div>
</form>
{error && (
<div className="pt-2 text-sm text-red-500 w-72 sm:w-96 dark:text-red-400">{message}</div>
<div className="w-72 pt-2 text-sm text-red-500 dark:text-red-400 sm:w-96">{message}</div>
)}
</div>
)
@ -77,7 +77,7 @@ export default NewsletterForm
export const BlogNewsletterForm = ({ title }) => (
<div className="flex items-center justify-center">
<div className="p-6 bg-gray-100 dark:bg-gray-800 sm:px-14 sm:py-8">
<div className="bg-gray-100 p-6 dark:bg-gray-800 sm:px-14 sm:py-8">
<NewsletterForm title={title} />
</div>
</div>

View File

@ -5,7 +5,7 @@ export default function Pagination({ totalPages, currentPage }) {
const nextPage = parseInt(currentPage) + 1 <= parseInt(totalPages)
return (
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<nav className="flex justify-between">
{!prevPage && (
<button rel="previous" className="cursor-auto disabled:opacity-50" disabled={!prevPage}>

View File

@ -26,9 +26,9 @@ const Pre = (props) => {
<button
aria-label="Copy code"
type="button"
className={`absolute right-2 top-2 w-8 h-8 p-1 rounded border-2 bg-gray-700 dark:bg-gray-800 ${
className={`absolute right-2 top-2 h-8 w-8 rounded border-2 bg-gray-700 p-1 dark:bg-gray-800 ${
copied
? 'focus:outline-none focus:border-green-400 border-green-400'
? 'border-green-400 focus:border-green-400 focus:outline-none'
: 'border-gray-300'
}`}
onClick={onCopy}

View File

@ -23,15 +23,15 @@ const ScrollTopAndComment = () => {
}
return (
<div
className={`fixed flex-col hidden gap-3 right-8 bottom-8 ${show ? 'md:flex' : 'md:hidden'}`}
className={`fixed right-8 bottom-8 hidden flex-col gap-3 ${show ? 'md:flex' : 'md:hidden'}`}
>
<button
aria-label="Scroll To Comment"
type="button"
onClick={handleScrollToComment}
className="p-2 text-gray-500 transition-all bg-gray-200 rounded-full dark:text-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 hover:bg-gray-300"
className="rounded-full bg-gray-200 p-2 text-gray-500 transition-all hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600"
>
<svg className="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
<svg className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="evenodd"
d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z"
@ -43,9 +43,9 @@ const ScrollTopAndComment = () => {
aria-label="Scroll To Top"
type="button"
onClick={handleScrollTop}
className="p-2 text-gray-500 transition-all bg-gray-200 rounded-full dark:text-gray-400 dark:bg-gray-700 dark:hover:bg-gray-600 hover:bg-gray-300"
className="rounded-full bg-gray-200 p-2 text-gray-500 transition-all hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600"
>
<svg className="w-5 h-5" viewBox="0 0 20 20" fill="currentColor">
<svg className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path
fillRule="evenodd"
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"

View File

@ -1,3 +1,3 @@
export default function SectionContainer({ children }) {
return <div className="max-w-3xl px-4 mx-auto sm:px-6 xl:max-w-5xl xl:px-0">{children}</div>
return <div className="mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0">{children}</div>
}

View File

@ -51,7 +51,7 @@ const TOCInline = ({
<>
{asDisclosure ? (
<details open>
<summary className="pt-2 pb-2 ml-6 text-xl font-bold">Table of Contents</summary>
<summary className="ml-6 pt-2 pb-2 text-xl font-bold">Table of Contents</summary>
<div className="ml-6">{tocList}</div>
</details>
) : (

View File

@ -12,7 +12,7 @@ const ThemeSwitch = () => {
<button
aria-label="Toggle Dark Mode"
type="button"
className="w-8 h-8 p-1 ml-1 mr-1 rounded sm:ml-4"
className="ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4"
onClick={() => setTheme(theme === 'dark' || resolvedTheme === 'dark' ? 'light' : 'dark')}
>
<svg

View File

@ -44,7 +44,7 @@ const Utterances = ({ issueTerm }) => {
return (
<div className="pt-6 pb-6 text-center text-gray-700 dark:text-gray-300">
{enableLoadComments && <button onClick={LoadComments}>Load Comments</button>}
<div className="relative utterances-frame" id={COMMENTS_ID} />
<div className="utterances-frame relative" id={COMMENTS_ID} />
</div>
)
}

View File

@ -31,7 +31,7 @@ const SocialIcon = ({ kind, href, size = 8 }) => {
>
<span className="sr-only">{kind}</span>
<SocialSvg
className={`fill-current text-gray-700 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400 h-${size} w-${size}`}
className={`fill-current text-gray-700 hover:text-blue-500 dark:text-gray-200 dark:hover:text-blue-400 h-${size} w-${size}`}
/>
</a>
)

View File

@ -7,7 +7,7 @@
/* Code title styles */
.remark-code-title {
@apply px-5 py-3 font-mono text-sm font-bold text-gray-200 bg-gray-700 rounded-t;
@apply rounded-t bg-gray-700 px-5 py-3 font-mono text-sm font-bold text-gray-200;
}
.remark-code-title + div > pre {
@ -20,7 +20,7 @@
}
.code-line {
@apply block pl-4 pr-4 -mx-4 border-l-4 border-opacity-0;
@apply -mx-4 block border-l-4 border-transparent pl-4 pr-4;
}
.code-line.inserted {
@ -32,11 +32,11 @@
}
.highlight-line {
@apply -mx-4 bg-gray-700 bg-opacity-50 border-l-4 border-primary-500;
@apply -mx-4 border-l-4 border-primary-500 bg-gray-700 bg-opacity-50;
}
.line-number::before {
@apply inline-block w-4 mr-4 -ml-2 text-right text-gray-400;
@apply mr-4 -ml-2 inline-block w-4 text-right text-gray-400;
content: attr(line);
}
@ -134,3 +134,7 @@
.token.italic {
font-style: italic;
}
.token.table {
display: inline;
}

View File

@ -1,7 +1,7 @@
---
title: 'Introducing Tailwind Nextjs Starter Blog'
date: '2021-01-12'
lastmod: '2021-12-22'
lastmod: '2021-02-01'
tags: ['next-js', 'tailwind', 'guide']
draft: false
summary: 'Looking for a performant, out of the box template, with all the best in web technology to support your blogging needs? Checkout the Tailwind Nextjs Starter Blog template.'
@ -47,6 +47,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
- Lightweight, 45kB first load JS, uses Preact in production build
- Mobile-friendly view
- Light and dark theme
- Self-hosted font with [Fontsource](https://fontsource.org/)
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
- [MDX - write JSX in markdown documents!](https://mdxjs.com/)
- Server-side syntax highlighting with line numbers and line highlighting via [rehype-prism-plus](https://github.com/timlrx/rehype-prism-plus)
@ -78,11 +79,13 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
1. JS (official support) - `npx degit https://github.com/timlrx/tailwind-nextjs-starter-blog.git` or TS (community support) - `npx degit timlrx/tailwind-nextjs-starter-blog#typescript`
2. Personalize `siteMetadata.js` (site related information)
3. Personalize `authors/default.md` (main author)
4. Modify `projectsData.js`
5. Modify `headerNavLinks.js` to customize navigation links
6. Add blog posts
7. Deploy on Vercel
3. Modify the content security policy in `next.config.js` if you want to use
any analytics provider or a commenting solution other than giscus.
4. Personalize `authors/default.md` (main author)
5. Modify `projectsData.js`
6. Modify `headerNavLinks.js` to customize navigation links
7. Add blog posts
8. Deploy on Vercel
## Development

View File

@ -1,7 +1,7 @@
---
title: 'New features in v1'
date: 2021-08-07T15:32:14Z
lastmod: '2021-12-15'
lastmod: '2021-02-01'
tags: ['next-js', 'tailwind', 'guide']
draft: false
summary: 'An overview of the new features released in v1 - code block copy, multiple authors, frontmatter layout and more'
@ -261,7 +261,7 @@ A long description of yourself...
You can use this information in multiple places across the template. For example in the about section of the page, we grab the default author information with this line of code:
```
```js
const authorDetails = await getFileBySlug('authors', ['default'])
```
@ -273,7 +273,7 @@ The frontmatter of a blog post accepts an optional `authors` array field. If no
For example, the following frontmatter will display the authors given by `data/authors/default.md` and `data/authors/sparrowhawk.md`
```
```yaml
title: 'My first post'
date: '2021-01-12'
draft: false
@ -322,7 +322,7 @@ To modify the styles, change the following class selectors in the `prism.css` fi
}
.code-line {
@apply block pl-4 pr-4 -mx-4 border-l-4 border-opacity-0;
@apply -mx-4 block border-l-4 border-opacity-0 pl-4 pr-4;
}
.code-line.inserted {
@ -334,11 +334,11 @@ To modify the styles, change the following class selectors in the `prism.css` fi
}
.highlight-line {
@apply -mx-4 bg-gray-700 bg-opacity-50 border-l-4 border-primary-500;
@apply -mx-4 border-l-4 border-primary-500 bg-gray-700 bg-opacity-50;
}
.line-number::before {
@apply inline-block w-4 mr-4 -ml-2 text-right text-gray-400;
@apply mr-4 -ml-2 inline-block w-4 text-right text-gray-400;
content: attr(line);
}
```
@ -399,6 +399,24 @@ The plugin uses APA citation formation, but also supports the following CSLs, 'a
See [rehype-citation readme](https://github.com/timlrx/rehype-citation) for more information on the configuration options.
## Self-hosted font (v1.5.0)
Google font has been replaced with self-hosted font from [Fontsource](https://fontsource.org/). This gives the following [advantages](https://fontsource.org/docs/introduction):
> Self-hosting brings significant performance gains as loading fonts from hosted services, such as Google Fonts, lead to an extra (render blocking) network request. To provide perspective, for simple websites it has been seen to double visual load times.
>
> Fonts remain version locked. Google often pushes updates to their fonts without notice, which may interfere with your live production projects. Manage your fonts like any other NPM dependency.
>
> Commit to privacy. Google does track the usage of their fonts and for those who are extremely privacy concerned, self-hosting is an alternative.
This leads to a smaller font bundle and a 0.1s faster load time ([webpagetest comparison](https://www.webpagetest.org/video/compare.php?tests=220201_AiDcFH_f68a69b758454dd52d8e67493fdef7da,220201_BiDcMC_bf2d53f14483814ba61e794311dfa771)).
To change the default Inter font:
1. Install the preferred [font](https://fontsource.org/fonts) - `npm install -save @fontsource/<font-name>`
2. Update the import at `pages/_app.js`- `import '@fontsource/<font-name>.css'`
3. Update the `fontfamily` property in the tailwind css config file
## Upgrade guide
There are significant portions of the code that has been changed from v0 to v1 including support for layouts and a new mdx engine.
@ -420,7 +438,7 @@ You can see an example of such a migration in this [commit](https://github.com/t
v1 also uses `feed.xml` rather than `index.xml`, to avoid some build issues with Vercel. If you are migrating you should add a redirect to `next.config.js` like so:
```
```js
async redirects() {
return [
{

View File

@ -18,6 +18,8 @@ const siteMetadata = {
linkedin: 'https://www.linkedin.com',
locale: 'en-US',
analytics: {
// If you want to use an analytics provider you have to add it to the
// content security policy in the `next.config.js` file.
// supports plausible, simpleAnalytics, umami or googleAnalytics
plausibleDataDomain: '', // e.g. tailwind-nextjs-starter-blog.vercel.app
simpleAnalytics: false, // true or false
@ -30,6 +32,8 @@ const siteMetadata = {
provider: 'buttondown',
},
comment: {
// If you want to use a commenting system other than giscus you have to add it to the
// content security policy in the `next.config.js` file.
// Select a provider and use the environment variables associated to it
// https://vercel.com/docs/environment-variables
provider: 'giscus', // supported providers: giscus, utterances, disqus

View File

@ -9,31 +9,31 @@ export default function AuthorLayout({ children, frontMatter }) {
<>
<PageSEO title={`About - ${name}`} description={`About me - ${name}`} />
<div className="divide-y">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
About
</h1>
</div>
<div className="items-start space-y-2 xl:grid xl:grid-cols-3 xl:gap-x-8 xl:space-y-0">
<div className="flex flex-col items-center pt-8 space-x-2">
<div className="flex flex-col items-center space-x-2 pt-8">
<Image
src={avatar}
alt="avatar"
width="192px"
height="192px"
className="w-48 h-48 rounded-full"
className="h-48 w-48 rounded-full"
/>
<h3 className="pt-4 pb-2 text-2xl font-bold leading-8 tracking-tight">{name}</h3>
<div className="text-gray-500 dark:text-gray-400">{occupation}</div>
<div className="text-gray-500 dark:text-gray-400">{company}</div>
<div className="flex pt-6 space-x-3">
<div className="flex space-x-3 pt-6">
<SocialIcon kind="mail" href={`mailto:${email}`} />
<SocialIcon kind="github" href={github} />
<SocialIcon kind="linkedin" href={linkedin} />
<SocialIcon kind="twitter" href={twitter} />
</div>
</div>
<div className="pt-8 pb-8 prose dark:prose-dark max-w-none xl:col-span-2">{children}</div>
<div className="prose max-w-none pt-8 pb-8 dark:prose-dark xl:col-span-2">{children}</div>
</div>
</div>
</>

View File

@ -19,7 +19,7 @@ export default function ListLayout({ posts, title, initialDisplayPosts = [], pag
return (
<>
<div className="divide-y">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
{title}
</h1>
@ -29,10 +29,10 @@ export default function ListLayout({ posts, title, initialDisplayPosts = [], pag
type="text"
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search articles"
className="block w-full px-4 py-2 text-gray-900 bg-white border border-gray-300 rounded-md dark:border-gray-900 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-800 dark:text-gray-100"
className="block w-full rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-900 dark:bg-gray-800 dark:text-gray-100"
/>
<svg
className="absolute w-5 h-5 text-gray-400 right-3 top-3 dark:text-gray-300"
className="absolute right-3 top-3 h-5 w-5 text-gray-400 dark:text-gray-300"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@ -53,7 +53,7 @@ export default function ListLayout({ posts, title, initialDisplayPosts = [], pag
const { slug, date, title, summary, tags } = frontMatter
return (
<li key={slug} className="py-4">
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline">
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0">
<dl>
<dt className="sr-only">Published on</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
@ -73,7 +73,7 @@ export default function ListLayout({ posts, title, initialDisplayPosts = [], pag
))}
</div>
</div>
<div className="prose text-gray-500 max-w-none dark:text-gray-400">
<div className="prose max-w-none text-gray-500 dark:text-gray-400">
{summary}
</div>
</div>

View File

@ -47,13 +47,13 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
</div>
</header>
<div
className="pb-8 divide-y divide-gray-200 xl:divide-y-0 dark:divide-gray-700 xl:grid xl:grid-cols-4 xl:gap-x-6"
className="divide-y divide-gray-200 pb-8 dark:divide-gray-700 xl:grid xl:grid-cols-4 xl:gap-x-6 xl:divide-y-0"
style={{ gridTemplateRows: 'auto 1fr' }}
>
<dl className="pt-6 pb-10 xl:pt-11 xl:border-b xl:border-gray-200 xl:dark:border-gray-700">
<dl className="pt-6 pb-10 xl:border-b xl:border-gray-200 xl:pt-11 xl:dark:border-gray-700">
<dt className="sr-only">Authors</dt>
<dd>
<ul className="flex justify-center space-x-8 xl:block sm:space-x-12 xl:space-x-0 xl:space-y-8">
<ul className="flex justify-center space-x-8 sm:space-x-12 xl:block xl:space-x-0 xl:space-y-8">
{authorDetails.map((author) => (
<li className="flex items-center space-x-2" key={author.name}>
{author.avatar && (
@ -62,10 +62,10 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
width="38px"
height="38px"
alt="avatar"
className="w-10 h-10 rounded-full"
className="h-10 w-10 rounded-full"
/>
)}
<dl className="text-sm font-medium leading-5 whitespace-nowrap">
<dl className="whitespace-nowrap text-sm font-medium leading-5">
<dt className="sr-only">Name</dt>
<dd className="text-gray-900 dark:text-gray-100">{author.name}</dd>
<dt className="sr-only">Twitter</dt>
@ -85,8 +85,8 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
</ul>
</dd>
</dl>
<div className="divide-y divide-gray-200 dark:divide-gray-700 xl:pb-0 xl:col-span-3 xl:row-span-2">
<div className="pt-10 pb-8 prose dark:prose-dark max-w-none">{children}</div>
<div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0">
<div className="prose max-w-none pt-10 pb-8 dark:prose-dark">{children}</div>
<div className="pt-6 pb-6 text-sm text-gray-700 dark:text-gray-300">
<Link href={discussUrl(slug)} rel="nofollow">
{'Discuss on Twitter'}
@ -97,10 +97,10 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
<Comments frontMatter={frontMatter} />
</div>
<footer>
<div className="text-sm font-medium leading-5 divide-gray-200 xl:divide-y dark:divide-gray-700 xl:col-start-1 xl:row-start-2">
<div className="divide-gray-200 text-sm font-medium leading-5 dark:divide-gray-700 xl:col-start-1 xl:row-start-2 xl:divide-y">
{tags && (
<div className="py-4 xl:py-8">
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
<h2 className="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
Tags
</h2>
<div className="flex flex-wrap">
@ -114,7 +114,7 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
<div className="flex justify-between py-4 xl:block xl:space-y-8 xl:py-8">
{prev && (
<div>
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
<h2 className="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
Previous Article
</h2>
<div className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400">
@ -124,7 +124,7 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
)}
{next && (
<div>
<h2 className="text-xs tracking-wide text-gray-500 uppercase dark:text-gray-400">
<h2 className="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
Next Article
</h2>
<div className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400">

View File

@ -17,7 +17,7 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
<article>
<div>
<header>
<div className="pb-10 space-y-1 text-center border-b border-gray-200 dark:border-gray-700">
<div className="space-y-1 border-b border-gray-200 pb-10 text-center dark:border-gray-700">
<dl>
<div>
<dt className="sr-only">Published on</dt>
@ -32,11 +32,11 @@ export default function PostLayout({ frontMatter, authorDetails, next, prev, chi
</div>
</header>
<div
className="pb-8 divide-y divide-gray-200 xl:divide-y-0 dark:divide-gray-700 "
className="divide-y divide-gray-200 pb-8 dark:divide-gray-700 xl:divide-y-0 "
style={{ gridTemplateRows: 'auto 1fr' }}
>
<div className="divide-y divide-gray-200 dark:divide-gray-700 xl:pb-0 xl:col-span-3 xl:row-span-2">
<div className="pt-10 pb-8 prose dark:prose-dark max-w-none">{children}</div>
<div className="divide-y divide-gray-200 dark:divide-gray-700 xl:col-span-3 xl:row-span-2 xl:pb-0">
<div className="prose max-w-none pt-10 pb-8 dark:prose-dark">{children}</div>
</div>
<Comments frontMatter={frontMatter} />
<footer>

View File

@ -9,6 +9,7 @@ import getAllFilesRecursively from './utils/files'
import remarkGfm from 'remark-gfm'
import remarkFootnotes from 'remark-footnotes'
import remarkMath from 'remark-math'
import remarkExtractFrontmatter from './remark-extract-frontmatter'
import remarkCodeTitles from './remark-code-title'
import remarkTocHeadings from './remark-toc-headings'
import remarkImgToJsx from './remark-img-to-jsx'
@ -55,18 +56,17 @@ export async function getFileBySlug(type, slug) {
let toc = []
// Parsing frontmatter here to pass it in as options to rehype plugin
const { data: frontmatter } = matter(source)
const { code } = await bundleMDX({
const { code, frontmatter } = await bundleMDX({
source,
// mdx imports can be automatically source from the components directory
cwd: path.join(root, 'components'),
xdmOptions(options) {
xdmOptions(options, frontmatter) {
// this is the recommended way to add custom remark/rehype plugins:
// The syntax might look weird, but it protects you in case we add/remove
// plugins in the future.
options.remarkPlugins = [
...(options.remarkPlugins ?? []),
remarkExtractFrontmatter,
[remarkTocHeadings, { exportRef: toc }],
remarkGfm,
remarkCodeTitles,
@ -79,10 +79,7 @@ export async function getFileBySlug(type, slug) {
rehypeSlug,
rehypeAutolinkHeadings,
rehypeKatex,
[
rehypeCitation,
{ bibliography: frontmatter?.bibliography, path: path.join(root, 'data') },
],
[rehypeCitation, { path: path.join(root, 'data') }],
[rehypePrismPlus, { ignoreMissing: true }],
rehypePresetMinify,
]

View File

@ -2,7 +2,7 @@ import { visit } from 'unist-util-visit'
export default function remarkCodeTitles() {
return (tree) =>
visit(tree, 'code', (node, index) => {
visit(tree, 'code', (node, index, parent) => {
const nodeLang = node.lang || ''
let language = ''
let title = ''
@ -26,7 +26,7 @@ export default function remarkCodeTitles() {
data: { _xdmExplicitJsx: true },
}
tree.children.splice(index, 0, titleNode)
parent.children.splice(index, 0, titleNode)
node.lang = language
})
}

View File

@ -0,0 +1,10 @@
import { visit } from 'unist-util-visit'
import { load } from 'js-yaml'
export default function extractFrontmatter() {
return (tree, file) => {
visit(tree, 'yaml', (node, index, parent) => {
file.data.frontmatter = load(node.value)
})
}
}

View File

@ -1,7 +1,10 @@
import fs from 'fs'
import path from 'path'
const pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x)
const pipe =
(...fns) =>
(x) =>
fns.reduce((v, f) => f(v), x)
const flattenArray = (input) =>
input.reduce((acc, item) => [...acc, ...(Array.isArray(item) ? item : [item])], [])

View File

@ -6,11 +6,11 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
const ContentSecurityPolicy = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline' giscus.app;
style-src 'self' 'unsafe-inline' *.googleapis.com cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' cdn.jsdelivr.net;
img-src * blob: data:;
media-src 'none';
connect-src *;
font-src 'self' fonts.gstatic.com cdn.jsdelivr.net;
font-src 'self' cdn.jsdelivr.net;
frame-src giscus.app
`

4385
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "tailwind-nextjs-starter-blog",
"version": "1.4.2",
"version": "1.5.0",
"private": true,
"scripts": {
"start": "cross-env SOCKET=true node ./scripts/next-remote-watch.js ./data",
@ -12,6 +12,7 @@
"prepare": "husky install"
},
"dependencies": {
"@fontsource/inter": "4.5.2",
"@mailchimp/mailchimp_marketing": "^3.0.58",
"@tailwindcss/forms": "^0.4.0",
"@tailwindcss/typography": "^0.5.0",
@ -21,7 +22,7 @@
"gray-matter": "^4.0.2",
"image-size": "1.0.0",
"mdx-bundler": "^8.0.0",
"next": "12.0.7",
"next": "12.0.9",
"next-themes": "^0.0.14",
"postcss": "^8.4.5",
"preact": "^10.6.2",
@ -29,7 +30,7 @@
"react-dom": "17.0.2",
"reading-time": "1.3.0",
"rehype-autolink-headings": "^6.1.0",
"rehype-citation": "^0.1.2",
"rehype-citation": "^0.2.0",
"rehype-katex": "^6.0.2",
"rehype-preset-minify": "6.0.0",
"rehype-prism-plus": "^1.1.3",
@ -39,16 +40,16 @@
"remark-math": "^5.1.1",
"sharp": "^0.28.3",
"smoothscroll-polyfill": "^0.4.4",
"tailwindcss": "^3.0.2",
"tailwindcss": "^3.0.18",
"unist-util-visit": "^4.0.0"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.7",
"@next/bundle-analyzer": "12.0.9",
"@svgr/webpack": "^6.1.2",
"cross-env": "^7.0.3",
"dedent": "^0.7.0",
"eslint": "^7.29.0",
"eslint-config-next": "12.0.7",
"eslint-config-next": "12.0.9",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.3.1",
"file-loader": "^6.0.0",
@ -57,9 +58,10 @@
"inquirer": "^8.1.1",
"lint-staged": "^11.0.0",
"next-remote-watch": "^1.0.0",
"prettier": "2.2.1",
"socket.io": "^4.1.3",
"socket.io-client": "^4.1.3"
"prettier": "^2.5.1",
"prettier-plugin-tailwindcss": "^0.1.4",
"socket.io": "^4.4.0",
"socket.io-client": "^4.4.0"
},
"lint-staged": {
"*.+(js|jsx|ts|tsx)": [

View File

@ -2,9 +2,9 @@ import Link from '@/components/Link'
export default function FourZeroFour() {
return (
<div className="flex flex-col items-start justify-start md:justify-center md:items-center md:flex-row md:space-x-6 md:mt-24">
<div className="pt-6 pb-8 space-x-2 md:space-y-5">
<h1 className="text-6xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 md:text-8xl md:leading-14 md:border-r-2 md:px-6">
<div className="flex flex-col items-start justify-start md:mt-24 md:flex-row md:items-center md:justify-center md:space-x-6">
<div className="space-x-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-6xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 md:border-r-2 md:px-6 md:text-8xl md:leading-14">
404
</h1>
</div>
@ -14,7 +14,7 @@ export default function FourZeroFour() {
</p>
<p className="mb-8">But dont worry, you can find plenty of other things on our homepage.</p>
<Link href="/">
<button className="inline px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-blue-600 border border-transparent rounded-lg shadow focus:outline-none focus:shadow-outline-blue hover:bg-blue-700 dark:hover:bg-blue-500">
<button className="focus:shadow-outline-blue inline rounded-lg border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium leading-5 text-white shadow transition-colors duration-150 hover:bg-blue-700 focus:outline-none dark:hover:bg-blue-500">
Back to homepage
</button>
</Link>

View File

@ -1,6 +1,8 @@
import '@/css/tailwind.css'
import '@/css/prism.css'
import '@fontsource/inter/variable-full.css'
import { ThemeProvider } from 'next-themes'
import Head from 'next/head'

View File

@ -22,11 +22,6 @@ class MyDocument extends Document {
<meta name="msapplication-TileColor" content="#000000" />
<meta name="theme-color" content="#000000" />
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css"
@ -34,7 +29,7 @@ class MyDocument extends Document {
crossOrigin="anonymous"
/>
</Head>
<body className="antialiased text-black bg-white dark:bg-gray-900 dark:text-white">
<body className="bg-white text-black antialiased dark:bg-gray-900 dark:text-white">
<Main />
<NextScript />
</body>

View File

@ -1,3 +1,4 @@
/* eslint-disable import/no-anonymous-default-export */
export default async (req, res) => {
const { email } = req.body
if (!email) {

View File

@ -20,7 +20,7 @@ export default function Home({ posts }) {
<>
<PageSEO title={siteMetadata.title} description={siteMetadata.description} />
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
Latest
</h1>
@ -35,7 +35,7 @@ export default function Home({ posts }) {
return (
<li key={slug} className="py-12">
<article>
<div className="space-y-2 xl:grid xl:grid-cols-4 xl:space-y-0 xl:items-baseline">
<div className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0">
<dl>
<dt className="sr-only">Published on</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
@ -59,7 +59,7 @@ export default function Home({ posts }) {
))}
</div>
</div>
<div className="prose text-gray-500 max-w-none dark:text-gray-400">
<div className="prose max-w-none text-gray-500 dark:text-gray-400">
{summary}
</div>
</div>

View File

@ -8,7 +8,7 @@ export default function Projects() {
<>
<PageSEO title={`Projects - ${siteMetadata.author}`} description={siteMetadata.description} />
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="pt-6 pb-8 space-y-2 md:space-y-5">
<div className="space-y-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
Projects
</h1>
@ -17,7 +17,7 @@ export default function Projects() {
</p>
</div>
<div className="container py-12">
<div className="flex flex-wrap -m-4">
<div className="-m-4 flex flex-wrap">
{projectsData.map((d) => (
<Card
key={d.title}

View File

@ -16,13 +16,13 @@ export default function Tags({ tags }) {
return (
<>
<PageSEO title={`Tags - ${siteMetadata.author}`} description="Things I blog about" />
<div className="flex flex-col items-start justify-start divide-y divide-gray-200 dark:divide-gray-700 md:justify-center md:items-center md:divide-y-0 md:flex-row md:space-x-6 md:mt-24">
<div className="pt-6 pb-8 space-x-2 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14 md:border-r-2 md:px-6">
<div className="flex flex-col items-start justify-start divide-y divide-gray-200 dark:divide-gray-700 md:mt-24 md:flex-row md:items-center md:justify-center md:space-x-6 md:divide-y-0">
<div className="space-x-2 pt-6 pb-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:border-r-2 md:px-6 md:text-6xl md:leading-14">
Tags
</h1>
</div>
<div className="flex flex-wrap max-w-lg">
<div className="flex max-w-lg flex-wrap">
{Object.keys(tags).length === 0 && 'No tags found.'}
{sortedTags.map((t) => {
return (
@ -30,7 +30,7 @@ export default function Tags({ tags }) {
<Tag text={t} />
<Link
href={`/tags/${kebabCase(t)}`}
className="-ml-2 text-sm font-semibold text-gray-600 uppercase dark:text-gray-300"
className="-ml-2 text-sm font-semibold uppercase text-gray-600 dark:text-gray-300"
>
{` (${tags[t]})`}
</Link>

View File

@ -107,6 +107,7 @@ inquirer
.replace(/ /g, '-')
.replace(/-+/g, '-')
const frontMatter = genFrontMatter(answers)
if (!fs.existsSync('data/blog')) fs.mkdirSync('data/blog', { recursive: true })
const filePath = `data/blog/${fileName ? fileName : 'untitled'}.${
answers.extension ? answers.extension : 'md'
}`

View File

@ -1,5 +1,6 @@
const fs = require('fs')
const globby = require('globby')
const matter = require('gray-matter')
const prettier = require('prettier')
const siteMetadata = require('../data/siteMetadata')
@ -7,10 +8,12 @@ const siteMetadata = require('../data/siteMetadata')
const prettierConfig = await prettier.resolveConfig('./.prettierrc.js')
const pages = await globby([
'pages/*.js',
'pages/*.tsx',
'data/blog/**/*.mdx',
'data/blog/**/*.md',
'public/tags/**/*.xml',
'!pages/_*.js',
'!pages/_*.tsx',
'!pages/api',
])
@ -19,16 +22,26 @@ const siteMetadata = require('../data/siteMetadata')
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages
.map((page) => {
// Exclude drafts from the sitemap
if (page.search('.md') >= 1 && fs.existsSync(page)) {
const source = fs.readFileSync(page, 'utf8')
const fm = matter(source)
if (fm.data.draft) {
return
}
}
const path = page
.replace('pages/', '/')
.replace('data/blog', '/blog')
.replace('public/', '/')
.replace('.js', '')
.replace('.tsx', '')
.replace('.mdx', '')
.replace('.md', '')
.replace('/feed.xml', '')
const route = path === '/index' ? '' : path
if (page === `pages/404.js` || page === `pages/blog/[...slug].js`) {
if (page.search('pages/404.') > -1 || page.search(`pages/blog/[...slug].`) > -1) {
return
}
return `

View File

@ -2,6 +2,9 @@ const defaultTheme = require('tailwindcss/defaultTheme')
const colors = require('tailwindcss/colors')
module.exports = {
experimental: {
optimizeUniversalDefaults: true,
},
content: ['./pages/**/*.js', './components/**/*.js', './layouts/**/*.js', './lib/**/*.js'],
darkMode: 'class',
theme: {
@ -16,7 +19,7 @@ module.exports = {
14: '3.5rem',
},
fontFamily: {
sans: ['Inter', ...defaultTheme.fontFamily.sans],
sans: ['InterVariable', ...defaultTheme.fontFamily.sans],
},
colors: {
primary: colors.teal,
@ -29,7 +32,7 @@ module.exports = {
a: {
color: theme('colors.primary.500'),
'&:hover': {
color: theme('colors.primary.600'),
color: `${theme('colors.primary.600')} !important`,
},
code: { color: theme('colors.primary.400') },
},
@ -97,7 +100,7 @@ module.exports = {
a: {
color: theme('colors.primary.500'),
'&:hover': {
color: theme('colors.primary.400'),
color: `${theme('colors.primary.400')} !important`,
},
code: { color: theme('colors.primary.400') },
},