feat: add admonitions to main website (#11)

* feat: add admonitions

* feat: better admonition styles

* feat: more admonition styles
This commit is contained in:
Luka 2024-11-30 19:23:14 +01:00 committed by GitHub
parent 81ecd02df2
commit af30701a21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 212 additions and 5 deletions

Binary file not shown.

View file

@ -24,6 +24,9 @@
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.46.0", "eslint-plugin-svelte": "^2.46.0",
"globals": "^15.11.0", "globals": "^15.11.0",
"mdast": "^3.0.0",
"mdast-util-directive": "^3.0.0",
"mdast-util-to-hast": "^13.2.0",
"mdsvex": "^0.12.3", "mdsvex": "^0.12.3",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7", "prettier-plugin-svelte": "^3.2.7",
@ -48,15 +51,19 @@
"hast-util-heading": "^3.0.0", "hast-util-heading": "^3.0.0",
"hast-util-heading-rank": "^3.0.0", "hast-util-heading-rank": "^3.0.0",
"hast-util-to-text": "^4.0.2", "hast-util-to-text": "^4.0.2",
"hastscript": "^9.0.0",
"lucide-static": "^0.462.0",
"lucide-svelte": "^0.446.0", "lucide-svelte": "^0.446.0",
"rehype-infer-description-meta": "^2.0.0", "rehype-infer-description-meta": "^2.0.0",
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0", "rehype-sanitize": "^6.0.0",
"rehype-slug": "^6.0.0", "rehype-slug": "^6.0.0",
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
"remark-directive": "^3.0.0",
"remark-frontmatter": "^5.0.0", "remark-frontmatter": "^5.0.0",
"remark-gemoji": "^8.0.0", "remark-gemoji": "^8.0.0",
"remark-gfm": "^4.0.0", "remark-gfm": "^4.0.0",
"remark-github-admonitions-to-directives": "^2.1.0",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1", "remark-rehype": "^11.1.1",
"shiki": "^1.22.2", "shiki": "^1.22.2",

108
website/src/admonitions.css Normal file
View file

@ -0,0 +1,108 @@
.admonition {
@apply my-4 rounded-sm px-4 py-3 text-[--tw-prose-body] prose-p:my-2 prose-pre:my-4;
@apply border-l-4 border-[--admonition-border];
@apply bg-[--admonition-bg];
@apply [--shiki-background:theme(colors.white/0.2)];
@apply dark:[--shiki-background:theme(colors.black/0.2)];
--tw-prose-body: theme(colors.light);
--tw-prose-headings: theme(colors.light);
--tw-prose-lead: theme(colors.light);
--tw-prose-links: var(--admonition-text);
--tw-prose-bold: theme(colors.light);
--tw-prose-counters: theme(colors.light);
--tw-prose-bullets: var(--admonition-border);
--tw-prose-hr: var(--admonition-border);
--tw-prose-quotes: theme(colors.light);
--tw-prose-quote-borders: var(--admonition-border);
--tw-prose-code: theme(colors.light);
--tw-prose-pre-code: theme(colors.light);
--tw-prose-pre-bg: var(--shiki-background);
--tw-prose-th-borders: var(--admonition-border);
--tw-prose-td-borders: var(--admonition-border);
}
.admonition pre {
@apply border border-[--admonition-border] bg-[--shiki-background];
}
.admonition-title {
@apply flex items-center space-x-2 text-lg font-semibold;
}
.admonition-title * {
color: var(--admonition-text);
}
.admonition-icon {
@apply inline-block size-6 bg-current;
mask-image: var(--admonition-icon);
}
.admonition-note {
--admonition-bg: theme(colors.blue.600 / 0.1);
--admonition-border: theme(colors.blue.600 / 0.4);
--admonition-text: theme(colors.blue.950);
--admonition-icon: url(lucide-static/icons/info.svg);
}
.admonition-tip {
--admonition-bg: theme(colors.green.600 / 0.1);
--admonition-border: theme(colors.green.600 / 0.4);
--admonition-text: theme(colors.green.950);
--admonition-icon: url(lucide-static/icons/lightbulb.svg);
}
.admonition-info {
--admonition-bg: theme(colors.purple.600 / 0.1);
--admonition-border: theme(colors.purple.600 / 0.4);
--admonition-text: theme(colors.purple.950);
--admonition-icon: url(lucide-static/icons/message-square-warning.svg);
}
.admonition-warning {
--admonition-bg: theme(colors.yellow.600 / 0.1);
--admonition-border: theme(colors.yellow.600 / 0.4);
--admonition-text: theme(colors.yellow.950);
--admonition-icon: url(lucide-static/icons/triangle-alert.svg);
}
.admonition-danger {
--admonition-bg: theme(colors.red.600 / 0.1);
--admonition-border: theme(colors.red.600 / 0.4);
--admonition-text: theme(colors.red.950);
--admonition-icon: url(lucide-static/icons/octagon-alert.svg);
}
@media (prefers-color-scheme: dark) {
.admonition-note {
--admonition-bg: theme(colors.blue.500 / 0.1);
--admonition-border: theme(colors.blue.500 / 0.6);
--admonition-text: theme(colors.blue.100);
}
.admonition-tip {
--admonition-bg: theme(colors.green.500 / 0.1);
--admonition-border: theme(colors.green.500 / 0.6);
--admonition-text: theme(colors.green.100);
}
.admonition-info {
--admonition-bg: theme(colors.purple.500 / 0.1);
--admonition-border: theme(colors.purple.500 / 0.6);
--admonition-text: theme(colors.purple.100);
}
.admonition-warning {
--admonition-bg: theme(colors.yellow.500 / 0.1);
--admonition-border: theme(colors.yellow.500 / 0.6);
--admonition-text: theme(colors.yellow.100);
}
.admonition-danger {
--admonition-bg: theme(colors.red.500 / 0.1);
--admonition-border: theme(colors.red.500 / 0.6);
--admonition-text: theme(colors.red.100);
}
}

View file

@ -2,6 +2,8 @@
@import "tailwindcss/components"; @import "tailwindcss/components";
@import "tailwindcss/utilities"; @import "tailwindcss/utilities";
@import "admonitions.css";
:root { :root {
--color-background: 255 245 230; --color-background: 255 245 230;
--color-card: 245 230 210; --color-card: 245 230 210;

View file

@ -1,3 +1,5 @@
/// <reference types="mdast-util-directive" />
// See https://kit.svelte.dev/docs/types#app // See https://kit.svelte.dev/docs/types#app
// for information about these interfaces // for information about these interfaces
declare global { declare global {

View file

@ -3,14 +3,19 @@ import type { Nodes } from "hast"
import { heading } from "hast-util-heading" import { heading } from "hast-util-heading"
import { headingRank } from "hast-util-heading-rank" import { headingRank } from "hast-util-heading-rank"
import { toText } from "hast-util-to-text" import { toText } from "hast-util-to-text"
import { h, type Child } from "hastscript"
import type { ContainerDirective } from "mdast-util-directive"
import type { Handler } from "mdast-util-to-hast"
import rehypeInferDescriptionMeta from "rehype-infer-description-meta" import rehypeInferDescriptionMeta from "rehype-infer-description-meta"
import rehypeRaw from "rehype-raw" import rehypeRaw from "rehype-raw"
import rehypeSanitize from "rehype-sanitize" import rehypeSanitize, { defaultSchema } from "rehype-sanitize"
import rehypeSlug from "rehype-slug" import rehypeSlug from "rehype-slug"
import rehypeStringify from "rehype-stringify" import rehypeStringify from "rehype-stringify"
import remarkDirective from "remark-directive"
import remarkFrontmatter from "remark-frontmatter" import remarkFrontmatter from "remark-frontmatter"
import remarkGemoji from "remark-gemoji" import remarkGemoji from "remark-gemoji"
import remarkGfm from "remark-gfm" import remarkGfm from "remark-gfm"
import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives"
import remarkParse from "remark-parse" import remarkParse from "remark-parse"
import remarkRehype from "remark-rehype" import remarkRehype from "remark-rehype"
import { createCssVariablesTheme, createHighlighter } from "shiki" import { createCssVariablesTheme, createHighlighter } from "shiki"
@ -23,15 +28,92 @@ const highlighter = createHighlighter({
langs: [], langs: [],
}) })
const ADMONITION_TYPES = {
note: {
label: "Note",
},
tip: {
label: "Tip",
},
info: {
label: "Info",
},
warning: {
label: "Warning",
},
danger: {
label: "Danger",
},
}
const containerDirectiveHandler: Handler = (state, node: ContainerDirective) => {
const type = node.name as keyof typeof ADMONITION_TYPES
if (!type || !(type in ADMONITION_TYPES)) {
return
}
const typeInfo = ADMONITION_TYPES[type]
let label: Child = typeInfo.label
const firstChild = node.children[0]
if (firstChild?.type === "paragraph" && firstChild.data?.directiveLabel) {
node.children.shift()
label = state.all(firstChild)
}
return h(
"div",
{
class: `admonition admonition-${type}`,
},
[
h(
"p",
{
class: "admonition-title",
},
[
h("span", {
class: "admonition-icon",
}),
h(
"span",
{
class: "admonition-label",
},
label,
),
],
),
state.all(node),
],
)
}
const sanitizeSchema: typeof defaultSchema = {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
"*": [...(defaultSchema.attributes?.["*"] ?? []), ["className", "admonition", /^admonition-/]],
},
}
const remarkRehypeHandlers = {
containerDirective: containerDirectiveHandler,
}
export const markdown = (async () => { export const markdown = (async () => {
return unified() return unified()
.use(remarkParse) .use(remarkParse)
.use(remarkFrontmatter) .use(remarkFrontmatter)
.use(remarkGfm) .use(remarkGfm)
.use(remarkGemoji) .use(remarkGemoji)
.use(remarkRehype, { allowDangerousHtml: true }) .use(remarkGithubAdmonitionsToDirectives)
.use(remarkDirective)
.use(remarkRehype, { allowDangerousHtml: true, handlers: remarkRehypeHandlers })
.use(rehypeRaw) .use(rehypeRaw)
.use(rehypeSanitize) .use(rehypeSanitize, sanitizeSchema)
.use(rehypeShikiFromHighlighter, await highlighter, { .use(rehypeShikiFromHighlighter, await highlighter, {
lazy: true, lazy: true,
theme: createCssVariablesTheme({ theme: createCssVariablesTheme({
@ -58,7 +140,13 @@ export const docsMarkdown = (async () => {
.use(remarkFrontmatter) .use(remarkFrontmatter)
.use(remarkGfm) .use(remarkGfm)
.use(remarkGemoji) .use(remarkGemoji)
.use(remarkRehype, { allowDangerousHtml: true, clobberPrefix: "" }) .use(remarkGithubAdmonitionsToDirectives)
.use(remarkDirective)
.use(remarkRehype, {
allowDangerousHtml: true,
clobberPrefix: "",
handlers: remarkRehypeHandlers,
})
.use(rehypeSlug) .use(rehypeSlug)
.use(() => (node, file) => { .use(() => (node, file) => {
const toc: TocItem[] = [] const toc: TocItem[] = []
@ -97,7 +185,7 @@ export const docsMarkdown = (async () => {
}) as Node }) as Node
}) })
.use(rehypeRaw) .use(rehypeRaw)
.use(rehypeSanitize) .use(rehypeSanitize, sanitizeSchema)
.use(rehypeShikiFromHighlighter, await highlighter, { .use(rehypeShikiFromHighlighter, await highlighter, {
lazy: true, lazy: true,
theme: createCssVariablesTheme({ theme: createCssVariablesTheme({