Merge branch 'timlrx:master' into master
This commit is contained in:
commit
58f77e8db1
22
README.md
22
README.md
@ -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
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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}>
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
}
|
||||
|
@ -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>
|
||||
) : (
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 [
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
</>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
13
lib/mdx.js
13
lib/mdx.js
@ -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,
|
||||
]
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
|
10
lib/remark-extract-frontmatter.js
Normal file
10
lib/remark-extract-frontmatter.js
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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])], [])
|
||||
|
@ -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
4385
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -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)": [
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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>
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable import/no-anonymous-default-export */
|
||||
export default async (req, res) => {
|
||||
const { email } = req.body
|
||||
if (!email) {
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
}`
|
||||
|
@ -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 `
|
||||
|
@ -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') },
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user