From 33133fb88e04d303095b34428ba6ada89245468b Mon Sep 17 00:00:00 2001 From: James Fenn Date: Wed, 18 Oct 2023 11:51:35 -0400 Subject: [PATCH] move post content to h3, restructure around "Post contents" heading --- astro.config.ts | 5 - content/site/about-us.es.mdx | 6 +- content/site/about-us.fr.mdx | 6 +- content/site/about-us.mdx | 6 +- content/site/about-us.pt.mdx | 6 +- package-lock.json | 168 ++---------------- package.json | 2 +- .../markdown/iframes/rehype-transform.ts | 2 +- src/utils/markdown/index.ts | 39 ++-- src/utils/markdown/rehype-header-class.ts | 58 ++++++ src/utils/markdown/rehype-header-text.ts | 2 +- .../base/scripts/heading-link-script.astro | 5 +- src/views/blog-post/blog-post.astro | 11 +- .../post-title-header/post-title-header.astro | 6 +- 14 files changed, 131 insertions(+), 191 deletions(-) create mode 100644 src/utils/markdown/rehype-header-class.ts diff --git a/astro.config.ts b/astro.config.ts index bca4efc1..7e37fff4 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -13,10 +13,6 @@ import sitemap from "@astrojs/sitemap"; import { EnumChangefreq as ChangeFreq } from "sitemap"; import { siteUrl } from "./src/constants/site-config"; import vercel from "@astrojs/vercel/static"; - -// TODO: Create types -import behead from "remark-behead"; - import image from "@astrojs/image"; import mdx from "@astrojs/mdx"; import symlink from "symlink-dir"; @@ -86,7 +82,6 @@ export default defineConfig({ // Remove complaining about "div cannot be in p element" remarkUnwrapImages, /* start remark plugins here */ - [behead, { depth: 1 }], [ remarkEmbedder, { diff --git a/content/site/about-us.es.mdx b/content/site/about-us.es.mdx index 128b74ae..5f55670f 100644 --- a/content/site/about-us.es.mdx +++ b/content/site/about-us.es.mdx @@ -10,7 +10,7 @@ Sin embargo, sabemos que este es un objetivo ambicioso, y no queremos hacerlo so --- -# Dónde encontrarnos +## Dónde encontrarnos Tenemos varias cuentas en redes sociales donde compartimos actualizaciones y nuevo contenido con amigos! También tenemos un canal de YouTube donde compartimos transmisiones en vivo, charlas grabadas, y mucho contenido educativo adicional sobre temas relacionados a las ciencias de la computación. @@ -20,7 +20,7 @@ Como lo mencionamos antes, también tenemos un servidor de Discord donde podemos --- -# Sponsors \{#sponsors\} +## Sponsors \{#sponsors\} @@ -31,7 +31,7 @@ Como lo mencionamos antes, también tenemos un servidor de Discord donde podemos --- -# Declaración de Ética \{#ethics\} +## Declaración de Ética \{#ethics\} Nunca queremos terminar en un lugar en el que nuestro contenido educativo, la experiencia, o la comunidad se vean comprometidos ya sea por influencias financieras o miembros potencialmente diff --git a/content/site/about-us.fr.mdx b/content/site/about-us.fr.mdx index 7e82ffc3..06339f52 100644 --- a/content/site/about-us.fr.mdx +++ b/content/site/about-us.fr.mdx @@ -10,7 +10,7 @@ C'est certainement un objectif ambitieux, et il sera difficile de l'atteindre se --- -# Où Nous Trouver +## Où Nous Trouver Nous partageons notre contenu avec tout le monde sur les différents réseaux sociaux. Notre chaîne YouTube contient nos émissions vidéos, nos livestreams et beaucoup plus sur le domaine de l’informatique. @@ -20,7 +20,7 @@ Et comme nous l’avons déjà cité, on a un serveur Discord ou on parle tech, --- -# Sponsors \{#sponsors\} +## Sponsors \{#sponsors\} @@ -31,7 +31,7 @@ Et comme nous l’avons déjà cité, on a un serveur Discord ou on parle tech, --- -# Code d’éthique \{#ethics\} +## Code d’éthique \{#ethics\} Nous ne voudrions jamais nous retrouver dans un endroit où le contenu ou la communauté sont compromis par des influences financières, ou bien des individus potentiellement nuisibles. Pour cela, nous avons adopté le [“Contributor Covenant”](https://www.contributor-covenant.org/) diff --git a/content/site/about-us.mdx b/content/site/about-us.mdx index 8919d9dd..39f9fc6f 100644 --- a/content/site/about-us.mdx +++ b/content/site/about-us.mdx @@ -10,7 +10,7 @@ We know this is a lofty goal, though, and we don't want to do it alone. If you'r --- -# Where to find us, you ask? +## Where to find us, you ask? We have various social media accounts where we share updates and new content with folks! We also have a YouTube channel where we host livestreams, recorded talks, and much more educational content on computer science related topics. @@ -20,7 +20,7 @@ As mentioned previously, we also have a Discord where we chat tech, help out wit --- -# Sponsors \{#sponsors\} +## Sponsors \{#sponsors\} @@ -31,7 +31,7 @@ As mentioned previously, we also have a Discord where we chat tech, help out wit --- -# Statement of Ethics \{#ethics\} +## Statement of Ethics \{#ethics\} We never want to end up in a place where our educational content, experience, or community is compromised by either financial sway or potentially harmful members of the community. As such, we've implemented the [Contributor Covenant](https://www.contributor-covenant.org/) as our [code of conduct](https://github.com/unicorn-utterances/unicorn-utterances/blob/master/CODE_OF_CONDUCT.md) to uphold these values. diff --git a/content/site/about-us.pt.mdx b/content/site/about-us.pt.mdx index e0082d35..54783340 100644 --- a/content/site/about-us.pt.mdx +++ b/content/site/about-us.pt.mdx @@ -10,7 +10,7 @@ Mas sabemos que este é um objetivo arrojado, e não o queremos fazer sozinhos. --- -# Onde nos podes encontrar? +## Onde nos podes encontrar? Nós temos diversas redes sociais, onde partilhamos updates e novidades com os membros! Nós também temos um canal do YouTube, onde fazemos algumas livestreams, conversas à solta, e muito mais conteúdo educational ligados à Ciência dos Computadores @@ -20,7 +20,7 @@ Como já foi mencionado antes, nós também temos um Discord onde falamos sobre --- -# Sponsors \{#sponsors\} +## Sponsors \{#sponsors\} @@ -31,7 +31,7 @@ Como já foi mencionado antes, nós também temos um Discord onde falamos sobre --- -# Princípios de Ética \{#ethics\} +## Princípios de Ética \{#ethics\} Não queremos acabar num lugar onde o nosso conteúdo educacional, a nossa experiência, ou comunidade está comprometida pelas oscilação das finanças ou potencialmente diff --git a/package-lock.json b/package-lock.json index 3a3d6091..075202bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,7 +80,6 @@ "probe-image-size": "^7.2.3", "rehype-raw": "^6.1.1", "rehype-slug-custom-id": "^1.1.0", - "remark-behead": "^3.1.0", "remark-gfm": "^3.0.1", "remark-shiki-twoslash": "^3.1.3", "remark-unwrap-images": "^3.0.1", @@ -92,6 +91,7 @@ "tsx": "^3.12.7", "typescript": "^5.1.6", "unified": "^10.1.2", + "unist-util-find": "^3.0.0", "unist-util-find-all-after": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-replace-all-between": "^0.1.1", @@ -25023,64 +25023,6 @@ "@types/unist": "^2" } }, - "node_modules/remark-behead": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/remark-behead/-/remark-behead-3.1.0.tgz", - "integrity": "sha512-rKns7st91lgppaD5YaH58O4ECFVXTVnkyYQBuCw4ISRE2TFK/iVySMaKbvV2pVbUVIjAaDciugrTI/tyuPOlWQ==", - "dev": true, - "dependencies": { - "unist-util-find": "^1.0.2", - "unist-util-find-all-after": "^4.0.0", - "unist-util-find-all-before": "^4.0.0", - "unist-util-find-all-between": "^2.1.0", - "unist-util-visit": "^4.1.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-behead/node_modules/unist-util-find-all-after": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-4.0.1.tgz", - "integrity": "sha512-AO8++e6HJfwNoTrqkV7xSeW65e6uSsLRQST/9LWi8FmFSz1gS7TBd+DkL/CYiElsSZIQgT4J5U54v5/kJX5Nqg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-behead/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-behead/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-frontmatter": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", @@ -28461,13 +28403,18 @@ } }, "node_modules/unist-util-find": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unist-util-find/-/unist-util-find-1.0.4.tgz", - "integrity": "sha512-T5vI7IkhroDj7KxAIy057VbIeGnCXfso4d4GoUsjbAmDLQUkzAeszlBtzx1+KHgdsYYBygaqUBvrbYCfePedZw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find/-/unist-util-find-3.0.0.tgz", + "integrity": "sha512-T7ZqS7immLjYyC4FCp2hDo3ksZ1v+qcbb+e5+iWxc2jONgHOLXPCpms1L8VV4hVxCXgWTxmBHDztuEZFVwC+Gg==", "dev": true, "dependencies": { - "lodash.iteratee": "^4.7.0", - "unist-util-visit": "^2.0.0" + "@types/unist": "^3.0.0", + "lodash.iteratee": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-find-all-after": { @@ -28490,94 +28437,11 @@ "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==", "dev": true }, - "node_modules/unist-util-find-all-before": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-find-all-before/-/unist-util-find-all-before-4.0.1.tgz", - "integrity": "sha512-xg4UHtZ6VbcjQbfDtmLZch6kQYQFF3nfaW05Ie3+t2UectzeqSx/iqLmh/wWogwU+YDWnD40PjZKK7ORmCma+g==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-all-before/node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-all-between": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unist-util-find-all-between/-/unist-util-find-all-between-2.1.0.tgz", - "integrity": "sha512-OCCUtDD8UHKeODw3TPXyFDxPCbpgBzbGTTaDpR68nvxkwiVcawBqMVrokfBMvUi7ij2F5q7S4s4Jq5dvkcBt+w==", - "dev": true, - "dependencies": { - "unist-util-find": "^1.0.1", - "unist-util-is": "^4.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/unist-util-find-all-between/node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find/node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find/node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find/node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } + "node_modules/unist-util-find/node_modules/@types/unist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.0.tgz", + "integrity": "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==", + "dev": true }, "node_modules/unist-util-generated": { "version": "2.0.1", diff --git a/package.json b/package.json index 2eae4433..2b411bc0 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,6 @@ "probe-image-size": "^7.2.3", "rehype-raw": "^6.1.1", "rehype-slug-custom-id": "^1.1.0", - "remark-behead": "^3.1.0", "remark-gfm": "^3.0.1", "remark-shiki-twoslash": "^3.1.3", "remark-unwrap-images": "^3.0.1", @@ -116,6 +115,7 @@ "tsx": "^3.12.7", "typescript": "^5.1.6", "unified": "^10.1.2", + "unist-util-find": "^3.0.0", "unist-util-find-all-after": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-replace-all-between": "^0.1.1", diff --git a/src/utils/markdown/iframes/rehype-transform.ts b/src/utils/markdown/iframes/rehype-transform.ts index 8dee5638..a71c8b9e 100644 --- a/src/utils/markdown/iframes/rehype-transform.ts +++ b/src/utils/markdown/iframes/rehype-transform.ts @@ -5,7 +5,7 @@ import { visit } from "unist-util-visit"; import { EMBED_MIN_HEIGHT, EMBED_SIZE } from "../constants"; import { fromHtml } from "hast-util-from-html"; -import find from "unist-util-find"; +import { find } from "unist-util-find"; import { getLargestManifestIcon } from "../../get-largest-manifest-icon"; import { getPicture } from "../get-picture-hack"; import type { GetPictureResult } from "@astrojs/image/dist/lib/get-picture"; diff --git a/src/utils/markdown/index.ts b/src/utils/markdown/index.ts index a90af9b6..38f0228d 100644 --- a/src/utils/markdown/index.ts +++ b/src/utils/markdown/index.ts @@ -17,6 +17,7 @@ import { } from "./rehype-absolute-paths"; import { rehypeFixTwoSlashXHTML } from "./rehype-fix-twoslash-xhtml"; import { rehypeHeaderText } from "./rehype-header-text"; +import { rehypeHeaderClass } from "./rehype-header-class"; import { rehypeFileTree } from "./file-tree/rehype-file-tree"; import { rehypeTwoslashTabindex } from "./twoslash-tabindex/rehype-transform"; @@ -26,7 +27,16 @@ type RehypePlugin = Plugin | [Plugin, any]; export function createRehypePlugins(config: MarkdownConfig): RehypePlugin[] { return [ ...(config.format === "html" - ? [rehypeUnicornPopulatePost, rehypeUnicornGetSuggestedPosts] + ? [ + rehypeUnicornPopulatePost, + rehypeUnicornGetSuggestedPosts, + [ + rehypeExcerpt, + { + maxLength: 150, + }, + ] as RehypePlugin, + ] : []), // This is required to handle unsafe HTML embedded into Markdown [rehypeRaw, { passThrough: [`mdxjsEsm`] }], @@ -48,6 +58,21 @@ export function createRehypePlugins(config: MarkdownConfig): RehypePlugin[] { enableCustomId: true, }, ], + ...(config.format === "html" + ? [ + rehypeHeaderText, + [ + rehypeHeaderClass, + { + // the page starts at h3 (under {title} -> "Post content") + depth: 2, + // visually, headings should start at h2-h6 + className: (depth: number) => + `text-style-headline-${Math.min(depth + 1, 6)}`, + }, + ] as RehypePlugin, + ] + : []), ...(config.format === "html" ? [ /** @@ -60,18 +85,8 @@ export function createRehypePlugins(config: MarkdownConfig): RehypePlugin[] { rehypeUnicornIFrameClickToRun, rehypeUnicornElementMap, rehypeTwoslashTabindex, + rehypeFileTree, ] : []), - ...(config.format === "html" - ? [ - [ - rehypeExcerpt, - { - maxLength: 150, - }, - ] as RehypePlugin, - ] - : []), - ...(config.format === "html" ? [rehypeFileTree, rehypeHeaderText] : []), ]; } diff --git a/src/utils/markdown/rehype-header-class.ts b/src/utils/markdown/rehype-header-class.ts new file mode 100644 index 00000000..38182e90 --- /dev/null +++ b/src/utils/markdown/rehype-header-class.ts @@ -0,0 +1,58 @@ +import { headingRank } from "hast-util-heading-rank"; +import { hasProperty } from "hast-util-has-property"; +import { toString } from "hast-util-to-string"; +import { Root, Parent } from "hast"; +import { visit } from "unist-util-visit"; + +interface RehypeHeaderClassOpts { + depth: number; + className: (depth: number) => string; +} + +/** + * Plugin to act as "rehype-behead", but add a className to display headings + * at the intended visual level. + */ +export const rehypeHeaderClass = (opts: RehypeHeaderClassOpts) => { + return (tree: Root, file) => { + // hacky (temporary) fix to exclude the site/about-us*.mdx files, since + // those start at a different heading level + if (file.data.astro.frontmatter.slug === "site") return; + + // Find the minimum heading rank in the file + // (e.g. if it starts at h2, minDepth = 2) + let minDepth: number; + visit(tree, "element", (node: Parent["children"][number]) => { + const nodeHeadingRank = headingRank(node); + if (!minDepth || nodeHeadingRank < minDepth) minDepth = nodeHeadingRank; + }); + minDepth ||= 1; + + visit(tree, "element", (node: Parent["children"][number]) => { + const nodeHeadingRank = headingRank(node); + + if ( + nodeHeadingRank && + "properties" in node && + node.properties && + !hasProperty(node, "class") && + !hasProperty(node, "className") + ) { + // indent the heading rank by opts.depth - (1 - minDepth), so that: + // - when (minDepth = 5, depth = 2) h5 + 2 - 4 -> h3 + // - when (minDepth = 1, depth = 2) h1 + 2 + 0 -> h3 + const tagHeadingRank = Math.min( + nodeHeadingRank + opts.depth + (1 - minDepth), + 6, + ); + const className = opts.className(nodeHeadingRank); + + node.tagName = "h" + tagHeadingRank; + node.properties.className = className; + + const headerText = toString(node as never); + node.properties["data-header-text"] = headerText; + } + }); + }; +}; diff --git a/src/utils/markdown/rehype-header-text.ts b/src/utils/markdown/rehype-header-text.ts index 40c99b2c..0940e0e5 100644 --- a/src/utils/markdown/rehype-header-text.ts +++ b/src/utils/markdown/rehype-header-text.ts @@ -35,7 +35,7 @@ export const rehypeHeaderText = () => { const headingWithID = { value: headerText, depth: headingRank(node)!, - slug: node.properties["id"] as string, + slug: node.properties["id"]! as string, }; headingsWithId.push(headingWithID); diff --git a/src/views/base/scripts/heading-link-script.astro b/src/views/base/scripts/heading-link-script.astro index 49f9f312..eb7d831b 100644 --- a/src/views/base/scripts/heading-link-script.astro +++ b/src/views/base/scripts/heading-link-script.astro @@ -63,9 +63,8 @@ import style from "./heading-link.module.scss"; .querySelector(".post-body") .querySelectorAll("h1, h2, h3, h4, h5, h6") .forEach((headingEl) => { - // ensure that each heading has an ID, and doesn't have a class attr. - // (i.e. it is a markdown heading, not belonging to another component) - if (!headingEl.matches("[id]:not([class])")) return; + // ensure that each heading has an ID, and doesn't have a "data-no-heading-link" attr + if (!headingEl.matches("[id]:not([data-no-heading-link])")) return; // add the click listener headingEl.addEventListener("click", handleClick); diff --git a/src/views/blog-post/blog-post.astro b/src/views/blog-post/blog-post.astro index dacb0e68..59735c29 100644 --- a/src/views/blog-post/blog-post.astro +++ b/src/views/blog-post/blog-post.astro @@ -75,7 +75,14 @@ if (post.collection && post.order) { -
+
+

+ Post contents +

{ post.collection ? ( ) : null } -
+ diff --git a/src/views/blog-post/post-title-header/post-title-header.astro b/src/views/blog-post/post-title-header/post-title-header.astro index 5e8a963d..9e129686 100644 --- a/src/views/blog-post/post-title-header/post-title-header.astro +++ b/src/views/blog-post/post-title-header/post-title-header.astro @@ -19,8 +19,10 @@ const editedStr = post.edited && dayjs(post.edited).format("MMMM D, YYYY"); const originalLinkStr = post.originalLink && new URL(post.originalLink).host; --- -
-

{title}

+
+

+ {title} +