Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/components/Layout/LayoutStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,9 @@ const standardLayout = css`
}

details {
margin-bottom: 1rem;

& summary {
cursor: pointer;
}

&:last-of-type {
margin-bottom: 2rem;
}
}

blockquote {
Expand Down
11 changes: 6 additions & 5 deletions src/components/Layout/MainStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,11 @@ html body {
}

ul, ol {
padding-left: 3rem;

padding-left: 1.5rem;
padding-top: 0.5rem;
li {
margin-bottom: 0.5rem;
}
li:first-of-type {
margin-top: 0.5rem;
}
}

h1, h2, h3, h4, h5, h6 {
Expand Down Expand Up @@ -136,5 +133,9 @@ html body {
text-decoration: underline;
}
}

summary::before {
margin-right: 0.5rem;
}
}
`;
27 changes: 27 additions & 0 deletions src/components/Layout/MarkdownStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,37 @@ export const MarkdownStyles = createGlobalStyle`
text-decoration: underline;
}
}
code {
font-size: 80%;
}
a, code, strong {
white-space: pre-wrap;
word-break: break-word;
word-wrap: break-word;
}
li > details {
display: block;
}
summary {
cursor: pointer;
user-select: none;
list-style: none;
display: block;
&::marker,
&::-webkit-details-marker {
display: none;
}
&::before {
content: '▶ ';
display: inline;
font-size: 0.75em;
transition: transform 0.15s ease;
display: inline-block;
width: 1em;
}
}
details[open] > summary::before {
transform: rotate(90deg);
}
}
`;
57 changes: 57 additions & 0 deletions src/helpers/rehypeWrapFirstList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { visit } from "unist-util-visit";
import type { Root, Element, ElementContent } from "hast";

/**
* Rehype plugin that makes nested list items individually collapsible.
* For each <li> that contains a nested <ul>, wraps the content in
* <details><summary> to enable collapse/expand functionality.
*/
export default function rehypeWrapFirstList() {
return (tree: Root) => {
visit(tree, "element", (node) => {
// Only process <li> elements
if (node.tagName !== "li") {
return;
}

// Check if this <li> has a nested <ul> child
const nestedUlIndex = node.children.findIndex(
(child): child is Element =>
child.type === "element" && child.tagName === "ul",
);

// If no nested <ul>, nothing to do
if (nestedUlIndex === -1) {
return;
}

// Split children into summary content (before ul) and nested ul
const summaryContent = node.children.slice(0, nestedUlIndex);
const nestedUl = node.children[nestedUlIndex] as Element;

// Only wrap if there's content to put in the summary
if (summaryContent.length === 0) {
return;
}

// Create the summary element with the content before the nested ul
const summary: Element = {
type: "element",
tagName: "summary",
properties: {},
children: summaryContent as ElementContent[],
};

// Create the details element
const details: Element = {
type: "element",
tagName: "details",
properties: { open: true }, // Open by default
children: [summary, nestedUl],
};

// Replace the <li>'s children with just the details element
node.children = [details];
});
};
}
23 changes: 21 additions & 2 deletions src/helpers/retrieveMdPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import rehypeStringify from "rehype-stringify";
import rehypeSlug from "rehype-slug";
import remarkHeadings, { hasHeadingsData } from "@vcarl/remark-headings";
import { toString } from "mdast-util-to-string";
import rehypeWrapFirstList from "./rehypeWrapFirstList";

const loadMd = async (path: string) => {
const fullPath = join(process.cwd(), `${path}.md`);
Expand Down Expand Up @@ -39,8 +40,26 @@ const remarkHtmlProcessor = unified()
.use(rehypeSlug)
.use(rehypeStringify, { allowDangerousHtml: true });

export const processMd = (mdSource: string) => {
const vfile = remarkHtmlProcessor.processSync(mdSource);
export interface ProcessMdOptions {
wrapFirstList?: boolean;
}

export const processMd = (mdSource: string, options?: ProcessMdOptions) => {
let processor = remarkHtmlProcessor;

// If wrapFirstList is enabled, add the rehype plugin
if (options?.wrapFirstList) {
processor = unified()
.use(parse)
.use(remarkGfm)
.use(remarkHeadings as ReturnType<ReturnType<typeof unified>["use"]>)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeSlug)
.use(rehypeWrapFirstList)
.use(rehypeStringify, { allowDangerousHtml: true });
}

const vfile = processor.processSync(mdSource);
if (hasHeadingsData(vfile.data)) {
return { html: vfile.toString(), headings: vfile.data.headings };
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/transcripts/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export const getStaticProps = async ({
props: {
all,
...pick(["title", "date"], doc),
html: processMd(doc.content).html,
html: processMd(doc.content, { wrapFirstList: true }).html,
description: processMdPlaintext(doc.description).html,
},
};
Expand Down