move post content to h3, restructure around "Post contents" heading

This commit is contained in:
James Fenn
2023-10-18 11:51:35 -04:00
parent e4e602c9f9
commit 33133fb88e
14 changed files with 131 additions and 191 deletions

View File

@@ -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,
{

View File

@@ -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\}
<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

View File

@@ -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 linformatique.
@@ -20,7 +20,7 @@ Et comme nous lavons déjà cité, on a un serveur Discord ou on parle tech,
---
# Sponsors \{#sponsors\}
## Sponsors \{#sponsors\}
<Sponsors />
@@ -31,7 +31,7 @@ Et comme nous lavons 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/)

View File

@@ -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\}
<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.

View File

@@ -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\}
<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

168
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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";

View File

@@ -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<any[]> | [Plugin<any[]>, 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] : []),
];
}

View File

@@ -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;
}
});
};
};

View File

@@ -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);

View File

@@ -63,9 +63,8 @@ import style from "./heading-link.module.scss";
.querySelector(".post-body")
.querySelectorAll<HTMLElement>("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);

View File

@@ -75,7 +75,14 @@ if (post.collection && post.order) {
<BlogPostLayout>
<PostTitleHeader slot="header" post={post} />
<TableOfContents slot="left" headingsWithId={post.headingsWithId} />
<div class="post-body" data-testid="post-body-div">
<section
class="post-body"
data-testid="post-body-div"
aria-labelledby="blog-post-contents"
>
<h2 id="blog-post-contents" class="visually-hidden" data-no-heading-link>
Post contents
</h2>
{
post.collection ? (
<SeriesToC
@@ -96,7 +103,7 @@ if (post.collection && post.order) {
<ArticleNav post={post} postSeries={seriesPosts} />
) : null
}
</div>
</section>
<aside slot="right" aria-labelledby="related-posts-heading">
<RelatedPosts post={post} headingId="related-posts-heading" />
</aside>

View File

@@ -19,8 +19,10 @@ const editedStr = post.edited && dayjs(post.edited).format("MMMM D, YYYY");
const originalLinkStr = post.originalLink && new URL(post.originalLink).host;
---
<section class={style.container}>
<h1 class={`text-style-headline-1 ${style.title}`}>{title}</h1>
<section class={style.container} aria-labelledby="post-title-header">
<h1 id="post-title-header" class={`text-style-headline-1 ${style.title}`}>
{title}
</h1>
<div class={style.details}>
<div class={style.date}>