Site Build Notes

An actual photo showing how the Pennock Projects website was built
Resources
I started with the excellent Udemy course by Piotr Jura Unlock Nuxt 3 & Vue Mastery: Build a Markdown Blog-Portfolio and Supabase Finance Tracker here and studied the GitHub source final and then like all developers before me, I built from there.
Nuxt
Nuxt is the framework for SSG. See Nuxt Cheat Sheet
Nuxt Content Doc
The Nuxt Content Doc is a Nuxt module that allows you to generate HTML content from markdown files. Used in combination with Tailwind Typography module to automatically 'up level the default style' Markdown documents to beautiful and readable HTML.
- Markdown Cheat Sheet
- Markdown Extended Syntax
- Markdown Syntax Highlighting - GitHub
- Markdown NuxtContent Usage
Installation
Install Content Doc
npx nuxi@latest module add content
Content Folder
The Content Doc module will automatically scan the /content folder in the source tree for markdown files and generate HTML for each. In each page .vue file you add a <ContentDoc /> element within <template></template> section. The NuxtContent generated HTML from the markdown file is injected in the <ContentDoc /> element.
The /content directory structure should match the /pages folder, so the Markdown file at /content/about.md contents will be converted and injected into the <ContentDoc /> in the <template></template> for the page at /pages/about.vue.
Mapping Content to Different Pages
To override the default behavior, you can specify that a different Markdown file is included by adding a path="{contentfolderpath}" property to the <ContentDoc />. For example, to inject the /content/blog/2023/hello.md into the /pages/about.vue page, you would add the following.
<ContentDoc path="/blog/2023/hello" />
NuxtContent Remark Plugin
Nuxt Content uses the MDC Remark plugins process Markdown text and to allow Markdown to support Vue components. One downside to this approach is that it will convert an image into a <p><img></p> structure. For example, an image specified in Markdown like this:

It will get converted by the MDC Remark plugin into HTML like this:
<p>
<img title="Logo" src="/images/PPNDLogoSm.png">
</p>
remark-unwrap-images
The outer <p></p> tags can cause formatting issues.
NuxtContent MDC uses remark plugins list to do the conversion to code blocks. A remark-unwrap-images plugin will unwrap images from the paragraph elements.
The Nuxt Configuration for remark plugins documents on how to enable them.
Unwrap Installation
npm install remark-unwrap-images
And then add this to your nuxt.config.js
content: {
markdown: {
remarkPlugins: ['remark-unwrap-images']
},
},
Tailwind CSS
The Tailwind CSS module enables the use of Tailwind CSS classes and particularly the Typographic Prose class for NuxtContent Markdown files. Tailwind CSS has a CSS 'reset', which resets the basic default classes for common HTML elements like <p></p> and <h1></h1>. The Tailwind Typographic Prose class is an Uber class which will style its element and all of its children elements with carefully chosen defaults for consistency and readability.
Installation
I installed the Tailwind CSS Typographic Prose module like this:
npm install --save-dev @nuxtjs/tailwindcss
added 104 packages, and audited 931 packages in 7s
184 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
PS C:\dev\>
Further you need to add the Tailwind CSS module to the nuxt.config.ts file
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
'@nuxtjs/tailwindcss'
]
})
Restarting the server will enable the new module. You can check in the Vue dev tools under the modules section.
Typographic Prose Class
A common way to add the Tailwind Typographic prose class is to add the class="prose" attribute to the parent element containing the NuxtContent <ContentDoc /> element, for example the surrounding <div></div> or <article></article> element. Tailwind CSS, by default, will strip all the normal default HTML styling, i.e. the <h1></h1> element is not shown as large by default. When you use the Prose class it reasserts a clean and typography styling. The <h1></h1> elements are all set to this. You add the tailwind class prose for light mode and dark: prose-invert for dark mode.
<template>
<article class="prose dark:prose-invert">
<ContentDoc />
</article>
</template>
Using Tailwind CSS
In a .vue file you can use tailwind CSS classes directly in the <template></template> section, or you can apply them to CSS rules in the <style></style> section. To add tailwind classes in the style section use the @apply line, for example, the class link has the tailwind CSS class p-1 and hover:bg-gray-200 applied to it.
<style scoped>
.link {
@apply p-1 hover:bg-gray-200
}
</style>
Note you can also use regular styles, the :deep() combinator, v-bind(), etc. alongside the @apply.
For example:
<style>
.monk-inset :deep(p) {
font-size: v-bind('pFontSizeClass');
line-height: v-bind('pLineHeightClass');
height: v-bind('pLineHeightClass');
margin: -0.2ch 0 0 0;
@apply p-0
}
</style>
Social Share Buttons
Since each page has custom metadata, I also wanted convenience buttons to quickly share the page on social media. Stefano Bartoletti Nuxt Social Share module was a good and easy as following the instructions to add the module. Then add the component into the page template.
The terminal command I issued.
npx nuxi@latest module add nuxt-social-share
nuxt.config.ts entries required
modules: [
'@stefanobartoletti/nuxt-social-share'
],
socialShare: {
baseUrl: 'https://pennockprojects.com'
}
Here is the usage within the code.
<template>
/<!-- snip -->
<SocialShare
v-for="network in ['facebook', 'x', 'linkedin', 'email']"
:key="network"
:label="false"
:network="network"
:styled="true"
/>
<!-- snip -->
</template>
Nuxt PDF
Better support for showing PDF files is with the paid component vue-pdf-viewer
Install
npm install @vue-pdf-viewer/viewer
Purchase a Developer License Key
- A single user
- Perpetual license
- Use in multiple websites
- Access to all features
- Free 1-year update and support
Use License key to activate
Essential code below.
<script setup>
import { VPdfViewer, useLicense } from '@vue-pdf-viewer/viewer';
let licenseKey = "123"
useLicense({ licenseKey });
</script>
<template>
<VPdfViewer />
</template>
CloudFlare Analytics
Install
npm i nuxt-cloudflare-analytics
Update nuxt.config.js
{
modules: [
'nuxt-cloudflare-analytics'
],
cloudflareAnalytics: {
// See below for more options
token: 'your-token', // Example 1a2b3v4a5er6ac7r8afd
}
}
Deployment
Deployment Steps
- Generate Static Site
npm run generate- places built files in.output/publicdirectory
- Debugging steps for
- Generate Static Site