Styling for Quote and Lead Content Blocks

This commit is contained in:
2026-09-28 13:42:11 +02:00
parent 99d2635763
commit 3b639f8a5b
11 changed files with 206 additions and 7 deletions

View File

@@ -1,15 +1,60 @@
import { fields } from '@keystatic/core'
import Markdoc, { type Node, type Schema } from '@markdoc/markdoc'
import Markdoc, { type Node, type RenderableTreeNode, type Schema } from '@markdoc/markdoc'
import { generalBlocks } from '@/keystatic/blocks/general'
import { ARTICLES } from '../constants/collections'
import { inlineOf } from './notes'
import { eachTag, firstTag, isTag } from './tree'
import type { NodeState, ResolveLink } from './types'
const { Tag } = Markdoc
function capitalise(children: RenderableTreeNode[]): void {
const paragraph = firstTag(children, 'p')
if (!paragraph) return
const [first, ...rest] = paragraph.children
if (typeof first !== 'string' || !first) return
paragraph.children = [
new Tag('span', { class: 'lead__cap' }, [first.slice(0, 1)]),
first.slice(1),
...rest,
]
}
function applyColumns(
children: RenderableTreeNode[],
columns: string[],
state: NodeState,
): void {
let widest = 0
eachTag(children, 'tr', (row) => {
widest = Math.max(widest, row.children.length)
row.children.forEach((cell, index) => {
const kind = columns[index]
if (!isTag(cell) || !kind || kind === 'default') return
if (kind === 'rowheader') {
if (cell.name !== 'td') return
cell.name = 'th'
cell.attributes.scope = 'row'
return
}
cell.attributes.class = [cell.attributes.class, kind].filter(Boolean).join(' ')
})
})
if (widest && widest !== columns.length)
state.problems.push({
level: 'warning',
message: `A table wrapper lists ${columns.length} column types for a table ${widest} columns wide.`,
})
}
const generated =
fields.markdoc.createMarkdocConfig({
components: generalBlocks(ARTICLES),
@@ -103,5 +148,13 @@ export function createTags(state: NodeState, resolveLink: ResolveLink): Record<s
]
},
},
lead: {
...generated.lead,
transform(node, config) {
const children = node.transformChildren(config)
capitalise(children)
return new Tag('Lead', node.transformAttributes(config), children)
},
},
}
}

25
src/lib/markdoc/tree.ts Normal file
View File

@@ -0,0 +1,25 @@
import Markdoc, { type RenderableTreeNode, type Tag } from '@markdoc/markdoc'
export const isTag = (node: RenderableTreeNode): node is Tag => Markdoc.Tag.isTag(node)
export function firstTag(nodes: RenderableTreeNode[], name: string): Tag | null {
for (const node of nodes) {
if (!isTag(node)) continue
if (node.name === name) return node
const found = firstTag(node.children, name)
if (found) return found
}
return null
}
export function eachTag(
nodes: RenderableTreeNode[],
name: string,
visit: (tag: Tag) => void,
): void {
for (const node of nodes) {
if (!isTag(node)) continue
if (node.name === name) visit(node)
else eachTag(node.children, name, visit)
}
}