upstream #1
@ -31,7 +31,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
|
|||||||
|
|
||||||
- Easy styling customization with [Tailwind 2.0](https://blog.tailwindcss.com/tailwindcss-v2) and primary color attribute
|
- Easy styling customization with [Tailwind 2.0](https://blog.tailwindcss.com/tailwindcss-v2) and primary color attribute
|
||||||
- Near perfect lighthouse score - [Lighthouse report](https://www.webpagetest.org/result/210111_DiC1_08f3670c3430bf4a9b76fc3b927716c5/)
|
- Near perfect lighthouse score - [Lighthouse report](https://www.webpagetest.org/result/210111_DiC1_08f3670c3430bf4a9b76fc3b927716c5/)
|
||||||
- Lightweight, 38kB first load JS, uses Preact in production build
|
- Lightweight, 39kB first load JS, uses Preact in production build
|
||||||
- Mobile-friendly view
|
- Mobile-friendly view
|
||||||
- Light and dark theme
|
- Light and dark theme
|
||||||
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
|
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
|
||||||
@ -43,6 +43,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
|
|||||||
- Support for tags - each unique tag will be its own page
|
- Support for tags - each unique tag will be its own page
|
||||||
- Support for multiple authors
|
- Support for multiple authors
|
||||||
- Blog templates
|
- Blog templates
|
||||||
|
- TOC component
|
||||||
- Support for nested routing of blog posts
|
- Support for nested routing of blog posts
|
||||||
- Supports [giscus](https://github.com/laymonage/giscus), [utterances](https://github.com/utterance/utterances) or disqus
|
- Supports [giscus](https://github.com/laymonage/giscus), [utterances](https://github.com/utterance/utterances) or disqus
|
||||||
- Projects page
|
- Projects page
|
||||||
|
@ -3,10 +3,12 @@ import { useMemo } from 'react'
|
|||||||
import { getMDXComponent } from 'mdx-bundler/client'
|
import { getMDXComponent } from 'mdx-bundler/client'
|
||||||
import Image from './Image'
|
import Image from './Image'
|
||||||
import CustomLink from './Link'
|
import CustomLink from './Link'
|
||||||
|
import TOCInline from './TOCInline'
|
||||||
import Pre from './Pre'
|
import Pre from './Pre'
|
||||||
|
|
||||||
export const MDXComponents = {
|
export const MDXComponents = {
|
||||||
Image,
|
Image,
|
||||||
|
TOCInline,
|
||||||
a: CustomLink,
|
a: CustomLink,
|
||||||
pre: Pre,
|
pre: Pre,
|
||||||
wrapper: ({ components, layout, ...rest }) => {
|
wrapper: ({ components, layout, ...rest }) => {
|
||||||
|
64
components/TOCInline.js
Normal file
64
components/TOCInline.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* @typedef TocHeading
|
||||||
|
* @prop {string} value
|
||||||
|
* @prop {number} depth
|
||||||
|
* @prop {string} url
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an inline table of contents
|
||||||
|
* Exclude titles matching this string (new RegExp('^(' + string + ')$', 'i')).
|
||||||
|
* If an array is passed the array gets joined with a pipe (new RegExp('^(' + array.join('|') + ')$', 'i')).
|
||||||
|
*
|
||||||
|
* @param {{
|
||||||
|
* toc: TocHeading[],
|
||||||
|
* indentDepth?: number,
|
||||||
|
* fromHeading?: number,
|
||||||
|
* toHeading?: number,
|
||||||
|
* asDisclosure?: boolean,
|
||||||
|
* exclude?: string|string[]
|
||||||
|
* }} props
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const TOCInline = ({
|
||||||
|
toc,
|
||||||
|
indentDepth = 3,
|
||||||
|
fromHeading = 1,
|
||||||
|
toHeading = 6,
|
||||||
|
asDisclosure = false,
|
||||||
|
exclude = '',
|
||||||
|
}) => {
|
||||||
|
const re = Array.isArray(exclude)
|
||||||
|
? new RegExp('^(' + exclude.join('|') + ')$', 'i')
|
||||||
|
: new RegExp('^(' + exclude + ')$', 'i')
|
||||||
|
|
||||||
|
const filteredToc = toc.filter(
|
||||||
|
(heading) =>
|
||||||
|
heading.depth >= fromHeading && heading.depth <= toHeading ** !re.test(heading.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
const tocList = (
|
||||||
|
<ul>
|
||||||
|
{filteredToc.map((heading) => (
|
||||||
|
<li key={heading.value} className={`${heading.depth >= indentDepth && 'ml-6'}`}>
|
||||||
|
<a href={heading.url}>{heading.value}</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{asDisclosure ? (
|
||||||
|
<details open>
|
||||||
|
<summary className="pt-2 pb-2 ml-6 text-xl font-bold">Table of Contents</summary>
|
||||||
|
<div className="ml-6">{tocList}</div>
|
||||||
|
</details>
|
||||||
|
) : (
|
||||||
|
tocList
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TOCInline
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: 'Introducing Tailwind Nextjs Starter Blog'
|
title: 'Introducing Tailwind Nextjs Starter Blog'
|
||||||
date: '2021-01-12'
|
date: '2021-01-12'
|
||||||
lastmod: '2021-08-02'
|
lastmod: '2021-08-07'
|
||||||
tags: ['next-js', 'tailwind', 'guide']
|
tags: ['next-js', 'tailwind', 'guide']
|
||||||
draft: false
|
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.'
|
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.'
|
||||||
@ -36,7 +36,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
|
|||||||
|
|
||||||
- Easy styling customization with [Tailwind 2.0](https://blog.tailwindcss.com/tailwindcss-v2) and primary color attribute
|
- Easy styling customization with [Tailwind 2.0](https://blog.tailwindcss.com/tailwindcss-v2) and primary color attribute
|
||||||
- Near perfect lighthouse score - [Lighthouse report](https://www.webpagetest.org/result/210111_DiC1_08f3670c3430bf4a9b76fc3b927716c5/)
|
- Near perfect lighthouse score - [Lighthouse report](https://www.webpagetest.org/result/210111_DiC1_08f3670c3430bf4a9b76fc3b927716c5/)
|
||||||
- Lightweight, 38kB first load JS, uses Preact in production build
|
- Lightweight, 39kB first load JS, uses Preact in production build
|
||||||
- Mobile-friendly view
|
- Mobile-friendly view
|
||||||
- Light and dark theme
|
- Light and dark theme
|
||||||
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
|
- Supports [plausible](https://plausible.io/), [simple analytics](https://simpleanalytics.com/) and google analytics
|
||||||
@ -48,6 +48,7 @@ I wanted it to be nearly as feature-rich as popular blogging templates like [bea
|
|||||||
- Support for tags - each unique tag will be its own page
|
- Support for tags - each unique tag will be its own page
|
||||||
- Support for multiple authors
|
- Support for multiple authors
|
||||||
- Blog templates
|
- Blog templates
|
||||||
|
- TOC component
|
||||||
- Support for nested routing of blog posts
|
- Support for nested routing of blog posts
|
||||||
- Supports [giscus](https://github.com/laymonage/giscus), [utterances](https://github.com/utterance/utterances) or disqus
|
- Supports [giscus](https://github.com/laymonage/giscus), [utterances](https://github.com/utterance/utterances) or disqus
|
||||||
- Projects page
|
- Projects page
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 'New features in v1'
|
title: 'New features in v1'
|
||||||
date: 2021-07-28T15:32:14Z
|
date: 2021-08-07T15:32:14Z
|
||||||
tags: ['next-js', 'tailwind', 'guide']
|
tags: ['next-js', 'tailwind', 'guide']
|
||||||
draft: false
|
draft: false
|
||||||
summary: 'An overview of the new features released in v1 - code block copy, multiple authors, frontmatter layout and more'
|
summary: 'An overview of the new features released in v1 - code block copy, multiple authors, frontmatter layout and more'
|
||||||
@ -11,16 +11,9 @@ layout: PostSimple
|
|||||||
|
|
||||||
A post on the new features introduced in v1.0. New features:
|
A post on the new features introduced in v1.0. New features:
|
||||||
|
|
||||||
- [Theme colors](#theme-colors)
|
<TOCInline toc={props.toc} exclude="Overview" toHeading={2} />
|
||||||
- [Xdm MDX compiler](#xdm-mdx-compiler)
|
|
||||||
- [Layouts](#layouts)
|
|
||||||
- [Multiple authors](#multiple-authors)
|
|
||||||
- [Analytics](#analytics)
|
|
||||||
- [Blog comments system](#blog-comments-system)
|
|
||||||
- [Copy button for code blocks](#copy-button-for-code-blocks)
|
|
||||||
- [Line highlighting and line numbers](#line-highlighting-and-line-numbers)
|
|
||||||
|
|
||||||
First load JS decreased from 43kB to 38kB despite all the new features added!
|
First load JS decreased from 43kB to 39kB despite all the new features added!
|
||||||
|
|
||||||
See [upgrade guide](#upgrade-guide) below if you are migrating from v0 version of the template.
|
See [upgrade guide](#upgrade-guide) below if you are migrating from v0 version of the template.
|
||||||
|
|
||||||
@ -75,6 +68,30 @@ Components which require external image loaders would require additional esbuild
|
|||||||
Components which are dependent on global application state on lifecycle like the Nextjs `Link` component would also not work with this setup as each mdx file is built indepedently.
|
Components which are dependent on global application state on lifecycle like the Nextjs `Link` component would also not work with this setup as each mdx file is built indepedently.
|
||||||
For such cases, it is better to use component substitution.
|
For such cases, it is better to use component substitution.
|
||||||
|
|
||||||
|
## Table of contents component
|
||||||
|
|
||||||
|
Inspired by [Docusaurus](https://docusaurus.io/docs/next/markdown-features/inline-toc) and Gatsby's [gatsby-remark-table-of-contents](https://www.gatsbyjs.com/plugins/gatsby-remark-table-of-contents/),
|
||||||
|
the `toc` variable containing all the top level headings of the document is passed to the MDX file and can be styled accordingly.
|
||||||
|
To make generating a TOC simple, you can use the existing `TOCInline` component.
|
||||||
|
|
||||||
|
For example, the TOC in this post was generated with the following code:
|
||||||
|
|
||||||
|
```js
|
||||||
|
<TOCInline toc={props.toc} exclude="Overview" toHeading={2} />
|
||||||
|
```
|
||||||
|
|
||||||
|
You can customise the headings to be generated by configuring the `fromHeading` and `toHeading` props as well as exclude particular headings
|
||||||
|
by passing a string or a string array. By default, all headings that are of depth 3 or smaller are indented. This can be configured by changing the `indentDepth` property.
|
||||||
|
A `asDisclosure` prop can be used to render the TOC within an expandable disclosure element.
|
||||||
|
|
||||||
|
Here's the full TOC rendered in a disclosure element.
|
||||||
|
|
||||||
|
```js
|
||||||
|
<TOCInline toc={props.toc} asDisclosure />
|
||||||
|
```
|
||||||
|
|
||||||
|
<TOCInline toc={props.toc} asDisclosure />
|
||||||
|
|
||||||
## Layouts
|
## Layouts
|
||||||
|
|
||||||
You can map mdx blog content to layout components by configuring the frontmatter field. For example, this post is written with the new `PostSimple` layout!
|
You can map mdx blog content to layout components by configuring the frontmatter field. For example, this post is written with the new `PostSimple` layout!
|
||||||
|
@ -5,6 +5,7 @@ import path from 'path'
|
|||||||
import readingTime from 'reading-time'
|
import readingTime from 'reading-time'
|
||||||
import visit from 'unist-util-visit'
|
import visit from 'unist-util-visit'
|
||||||
import codeTitles from './remark-code-title'
|
import codeTitles from './remark-code-title'
|
||||||
|
import remarkTocHeadings from './remark-toc-headings'
|
||||||
import imgToJsx from './img-to-jsx'
|
import imgToJsx from './img-to-jsx'
|
||||||
import getAllFilesRecursively from './utils/files'
|
import getAllFilesRecursively from './utils/files'
|
||||||
|
|
||||||
@ -66,6 +67,8 @@ export async function getFileBySlug(type, slug) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let toc = []
|
||||||
|
|
||||||
const { frontmatter, code } = await bundleMDX(source, {
|
const { frontmatter, code } = await bundleMDX(source, {
|
||||||
// mdx imports can be automatically source from the components directory
|
// mdx imports can be automatically source from the components directory
|
||||||
cwd: path.join(process.cwd(), 'components'),
|
cwd: path.join(process.cwd(), 'components'),
|
||||||
@ -77,6 +80,7 @@ export async function getFileBySlug(type, slug) {
|
|||||||
...(options.remarkPlugins ?? []),
|
...(options.remarkPlugins ?? []),
|
||||||
require('remark-slug'),
|
require('remark-slug'),
|
||||||
require('remark-autolink-headings'),
|
require('remark-autolink-headings'),
|
||||||
|
[remarkTocHeadings, { exportRef: toc }],
|
||||||
require('remark-gfm'),
|
require('remark-gfm'),
|
||||||
codeTitles,
|
codeTitles,
|
||||||
[require('remark-footnotes'), { inlineNotes: true }],
|
[require('remark-footnotes'), { inlineNotes: true }],
|
||||||
@ -111,6 +115,7 @@ export async function getFileBySlug(type, slug) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
mdxSource: code,
|
mdxSource: code,
|
||||||
|
toc,
|
||||||
frontMatter: {
|
frontMatter: {
|
||||||
readingTime: readingTime(code),
|
readingTime: readingTime(code),
|
||||||
slug: slug || null,
|
slug: slug || null,
|
||||||
|
12
lib/remark-toc-headings.js
Normal file
12
lib/remark-toc-headings.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import visit from 'unist-util-visit'
|
||||||
|
|
||||||
|
module.exports = function (options) {
|
||||||
|
return (tree) =>
|
||||||
|
visit(tree, 'heading', (node, index, parent) => {
|
||||||
|
options.exportRef.push({
|
||||||
|
value: node.children[1].value,
|
||||||
|
url: node.children[0].url,
|
||||||
|
depth: node.depth,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -39,13 +39,14 @@ export async function getStaticProps({ params }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Blog({ post, authorDetails, prev, next }) {
|
export default function Blog({ post, authorDetails, prev, next }) {
|
||||||
const { mdxSource, frontMatter } = post
|
const { mdxSource, toc, frontMatter } = post
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{frontMatter.draft !== true ? (
|
{frontMatter.draft !== true ? (
|
||||||
<MDXLayoutRenderer
|
<MDXLayoutRenderer
|
||||||
layout={frontMatter.layout || DEFAULT_LAYOUT}
|
layout={frontMatter.layout || DEFAULT_LAYOUT}
|
||||||
|
toc={toc}
|
||||||
mdxSource={mdxSource}
|
mdxSource={mdxSource}
|
||||||
frontMatter={frontMatter}
|
frontMatter={frontMatter}
|
||||||
authorDetails={authorDetails}
|
authorDetails={authorDetails}
|
||||||
|
@ -74,6 +74,14 @@ module.exports = {
|
|||||||
'code:after': {
|
'code:after': {
|
||||||
content: 'none',
|
content: 'none',
|
||||||
},
|
},
|
||||||
|
details: {
|
||||||
|
backgroundColor: theme('colors.gray.100'),
|
||||||
|
paddingLeft: '4px',
|
||||||
|
paddingRight: '4px',
|
||||||
|
paddingTop: '2px',
|
||||||
|
paddingBottom: '2px',
|
||||||
|
borderRadius: '0.25rem',
|
||||||
|
},
|
||||||
hr: { borderColor: theme('colors.gray.200') },
|
hr: { borderColor: theme('colors.gray.200') },
|
||||||
'ol li:before': {
|
'ol li:before': {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
@ -119,6 +127,9 @@ module.exports = {
|
|||||||
code: {
|
code: {
|
||||||
backgroundColor: theme('colors.gray.800'),
|
backgroundColor: theme('colors.gray.800'),
|
||||||
},
|
},
|
||||||
|
details: {
|
||||||
|
backgroundColor: theme('colors.gray.800'),
|
||||||
|
},
|
||||||
hr: { borderColor: theme('colors.gray.700') },
|
hr: { borderColor: theme('colors.gray.700') },
|
||||||
'ol li:before': {
|
'ol li:before': {
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user