From e14e798af83661467f04433e50c209a6130ea307 Mon Sep 17 00:00:00 2001
From: Baptiste Parmantier
Date: Fri, 21 Nov 2025 14:32:55 +0100
Subject: [PATCH 1/3] wip
---
astro.config.mjs | 2 +
.../foundamentals/components/_default.mdx | 1 +
.../foundamentals/components/card.mdx | 15 ++
.../getting-started/getting-started.mdx | 3 +
.../content/card-group/card-group.astro | 7 +
.../components/content/card-group/card.astro | 6 +
src/lib/components/content/codeblock.astro | 10 ++
src/lib/plugins/parser/block-renderer.astro | 72 ++++++++++
src/lib/plugins/parser/dynamic-block.astro | 23 +++
src/lib/plugins/parser/plugin.ts | 135 ++++++++++++++++++
src/lib/plugins/parser/selectors.ts | 25 ++++
src/pages/docs/[...slug].astro | 5 +-
12 files changed, 303 insertions(+), 1 deletion(-)
create mode 100644 content/docs/documentation/foundamentals/components/card.mdx
create mode 100644 src/lib/components/content/card-group/card-group.astro
create mode 100644 src/lib/components/content/card-group/card.astro
create mode 100644 src/lib/components/content/codeblock.astro
create mode 100644 src/lib/plugins/parser/block-renderer.astro
create mode 100644 src/lib/plugins/parser/dynamic-block.astro
create mode 100644 src/lib/plugins/parser/plugin.ts
create mode 100644 src/lib/plugins/parser/selectors.ts
diff --git a/astro.config.mjs b/astro.config.mjs
index e063390..ff1d61c 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -18,6 +18,7 @@ import rehypeMermaid from "rehype-mermaid";
import rehypeCodeGroupReact from "./src/lib/plugins/code-group/plugin";
import rehypeReadMoreReact from "./src/lib/plugins/read-more/plugin";
import rehypeBlogListReact from "./src/lib/plugins/blog-list/plugin";
+import rehypeBlock from "./src/lib/plugins/parser/plugin";
import {
default as remarkDirective,
default as remarkReadMoreDirective,
@@ -51,6 +52,7 @@ export default defineConfig({
},
remarkPlugins: [remarkDirective, remarkReadMoreDirective],
rehypePlugins: [
+ rehypeBlock,
rehypeMermaid,
[
rehypeCallouts,
diff --git a/content/docs/documentation/foundamentals/components/_default.mdx b/content/docs/documentation/foundamentals/components/_default.mdx
index 5fe6643..ef423f2 100644
--- a/content/docs/documentation/foundamentals/components/_default.mdx
+++ b/content/docs/documentation/foundamentals/components/_default.mdx
@@ -9,4 +9,5 @@ collection:
- code-block
- markdown
- text
+ - card
---
diff --git a/content/docs/documentation/foundamentals/components/card.mdx b/content/docs/documentation/foundamentals/components/card.mdx
new file mode 100644
index 0000000..316400f
--- /dev/null
+++ b/content/docs/documentation/foundamentals/components/card.mdx
@@ -0,0 +1,15 @@
+---
+title: Card
+description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+permalink: card
+icon: lucide:bell
+---
+
+:::card-group{col="2"}
+:::card{label="Test", icon="lucide:bell"}
+test
+:::
+:::card{label="Test", icon="lucide:bell"}
+d
+:::
+:::
diff --git a/content/docs/documentation/getting-started/getting-started.mdx b/content/docs/documentation/getting-started/getting-started.mdx
index 3895dcd..af3f875 100644
--- a/content/docs/documentation/getting-started/getting-started.mdx
+++ b/content/docs/documentation/getting-started/getting-started.mdx
@@ -10,3 +10,6 @@ icon: lucide:info
## Introduction
Explainer provides a rich set of components that can be used directly in your Markdown files. This documentation outlines the various markdown components available for creating beautiful, interactive documentation.
+
+:::card-group{cols=2}
+:::
diff --git a/src/lib/components/content/card-group/card-group.astro b/src/lib/components/content/card-group/card-group.astro
new file mode 100644
index 0000000..d533d95
--- /dev/null
+++ b/src/lib/components/content/card-group/card-group.astro
@@ -0,0 +1,7 @@
+---
+const props = Astro.props;
+---
+
+
+
+
diff --git a/src/lib/components/content/card-group/card.astro b/src/lib/components/content/card-group/card.astro
new file mode 100644
index 0000000..c021185
--- /dev/null
+++ b/src/lib/components/content/card-group/card.astro
@@ -0,0 +1,6 @@
+---
+const props = Astro.props;
+console.log("Card", props);
+---
+
+Card
diff --git a/src/lib/components/content/codeblock.astro b/src/lib/components/content/codeblock.astro
new file mode 100644
index 0000000..b36dac6
--- /dev/null
+++ b/src/lib/components/content/codeblock.astro
@@ -0,0 +1,10 @@
+---
+const props = Astro.props;
+---
+
+
+
+
diff --git a/src/lib/plugins/parser/block-renderer.astro b/src/lib/plugins/parser/block-renderer.astro
new file mode 100644
index 0000000..ac1d1dc
--- /dev/null
+++ b/src/lib/plugins/parser/block-renderer.astro
@@ -0,0 +1,72 @@
+---
+import CardGroup from "@/components/content/card-group/card-group.astro";
+import Card from "@/components/content/card-group/card.astro";
+import BlockDynamic from "./dynamic-block.astro";
+
+interface ASTNode {
+ delimiter?: string;
+ startTag?: string;
+ attributes?: Record;
+ children?: Array;
+ type?: string;
+}
+
+const mdx: Record = {
+ "card-group": CardGroup,
+ card: Card,
+};
+
+const { ast } = Astro.props as { ast: string | undefined };
+if (!ast) return null;
+
+const node: ASTNode | ASTNode[] = JSON.parse(ast);
+
+const renderNodeData = (
+ block: ASTNode | { type: "text"; value: string } | undefined,
+): any => {
+ if (!block) return null;
+
+ if (Array.isArray(block)) {
+ return block.flatMap(renderNodeData).filter(Boolean);
+ }
+
+ if (typeof block !== "object") return block;
+
+ if ("type" in block && block.type === "text") return block.value;
+
+ const Component = mdx[block.startTag || ""];
+
+ // Si aucun composant trouvé, on rend les enfants directement (raw / code / span)
+ const childrenRendered =
+ block.children?.flatMap(renderNodeData).filter(Boolean) || [];
+
+ if (!Component) {
+ // Rendu direct des enfants
+ return childrenRendered;
+ }
+
+ return {
+ component: Component,
+ props: block.attributes || {},
+ children: childrenRendered,
+ };
+};
+
+const treeData = Array.isArray(node)
+ ? node.flatMap(renderNodeData).filter(Boolean)
+ : renderNodeData(node);
+---
+
+{
+ treeData.map((node: any, i: number) => {
+ if (typeof node === "string") return node;
+ return (
+
+ );
+ })
+}
diff --git a/src/lib/plugins/parser/dynamic-block.astro b/src/lib/plugins/parser/dynamic-block.astro
new file mode 100644
index 0000000..1ef7a83
--- /dev/null
+++ b/src/lib/plugins/parser/dynamic-block.astro
@@ -0,0 +1,23 @@
+---
+import DynamicBlockRenderer from "./dynamic-block.astro";
+const { component: Component, props, children } = Astro.props;
+
+const childrenArray = Array.isArray(children)
+ ? children
+ : [children].filter(Boolean);
+---
+
+
+ {
+ childrenArray.map((child: any) => {
+ if (typeof child === "string") return child;
+ return (
+
+ );
+ })
+ }
+
diff --git a/src/lib/plugins/parser/plugin.ts b/src/lib/plugins/parser/plugin.ts
new file mode 100644
index 0000000..d983d03
--- /dev/null
+++ b/src/lib/plugins/parser/plugin.ts
@@ -0,0 +1,135 @@
+import type { Element, Root } from "unist";
+import { visit } from "unist-util-visit";
+
+// Exemple de mapping startTag -> composant Astro
+const mdx: Record = {
+ "card-group": "CardGroup",
+ card: "Card",
+};
+
+interface Block {
+ delimiter: string;
+ startTag: string;
+ attributes: Record;
+ children: Array;
+}
+
+// Parse les attributs de la ligne :::tag key="value"
+const parseAttributes = (str: string): Record => {
+ const regex = /(\w+)\s*=\s*(?:"([^"]*)"|(\S+))/g;
+ const attrs: Record = {};
+ let match: RegExpExecArray | null;
+
+ while ((match = regex.exec(str)) !== null) {
+ const key = match[1];
+ let value: any;
+
+ if (match[2] !== undefined) {
+ // Valeur entre guillemets
+ const raw = match[2];
+ try {
+ value = JSON.parse(raw); // Essayer de parser JSON
+ } catch {
+ value = raw; // fallback string
+ }
+ } else if (match[3] !== undefined) {
+ // Valeur non-quoted (true, false, number, etc.)
+ const raw = match[3];
+ if (raw === "true") value = true;
+ else if (raw === "false") value = false;
+ else if (!isNaN(Number(raw))) value = Number(raw);
+ else value = raw;
+ }
+
+ attrs[key] = value;
+ }
+
+ return attrs;
+};
+
+// Fonction principale : parse un node et retourne un Block[]
+const parseSingleNode = (node: Element): Block[] => {
+ // On combine le node text + mdxTextExpression éventuel en un seul texte
+ let combinedText = "";
+
+ for (const child of node.children || []) {
+ if (child.type === "text") {
+ combinedText += child.value;
+ } else if ((child as any).type === "mdxTextExpression") {
+ combinedText += " " + (child as any).value;
+ }
+ }
+
+ // Fonction récursive interne pour parser un texte complet
+ const parseBlockText = (text: string): Block[] => {
+ const blocks: Block[] = [];
+ const lines = text.split(/\r?\n/);
+ const stack: Block[] = [];
+
+ for (const line of lines) {
+ const startMatch = line.match(/^:::(\w[\w-]*)\s*(.*)$/);
+ const endMatch = line.match(/^:::/);
+
+ if (startMatch) {
+ const block: Block = {
+ delimiter: ":::",
+ startTag: startMatch[1],
+ attributes: parseAttributes(startMatch[2] || ""),
+ children: [],
+ };
+
+ if (stack.length > 0) {
+ // Ajoute comme enfant du dernier parent
+ stack[stack.length - 1].children.push(block);
+ } else {
+ // Bloc racine
+ blocks.push(block);
+ }
+
+ stack.push(block); // push pour gérer la fermeture
+ } else if (endMatch) {
+ stack.pop(); // on ferme le bloc courant
+ } else {
+ if (stack.length > 0) {
+ stack[stack.length - 1].children.push({ type: "text", value: line });
+ }
+ }
+ }
+
+ // Pousser tout bloc restant dans stack (blocs non fermés)
+ while (stack.length > 0) {
+ const remaining = stack.pop()!;
+ if (stack.length > 0) {
+ stack[stack.length - 1].children.push(remaining);
+ } else if (!blocks.includes(remaining)) {
+ blocks.push(remaining);
+ }
+ }
+
+ return blocks;
+ };
+
+ return parseBlockText(combinedText);
+};
+
+// Plugin rehype
+export default function rehypeComponents() {
+ return (tree: Root) => {
+ visit(tree, "element", (node: Element, index, parent) => {
+ if (!parent) return;
+
+ const parsedBlocks = parseSingleNode(node);
+
+ console.log(JSON.stringify(parsedBlocks, null, 2));
+
+ parent.children[index] = {
+ type: "element",
+ tagName: "BlockRenderer",
+ properties: {
+ ast: JSON.stringify(parsedBlocks),
+ },
+ children: [],
+ } as Element;
+ });
+ };
+}
diff --git a/src/lib/plugins/parser/selectors.ts b/src/lib/plugins/parser/selectors.ts
new file mode 100644
index 0000000..960aba7
--- /dev/null
+++ b/src/lib/plugins/parser/selectors.ts
@@ -0,0 +1,25 @@
+import { toHtml } from "hast-util-to-html";
+
+export const codeblockSelector = {
+ filter: (child: any) => {
+ return child.tagName === "code" && child.children[0].type !== "text";
+ },
+ metadata: (child: any) => ({
+ delimiter: "",
+ startTag: "codeblock",
+ attributes: { html: toHtml(child) },
+ children: [],
+ }),
+};
+
+export const cardGroupSelector = {
+ filter: (child: any) => {
+ return child.tagName === "card-group";
+ },
+ metadata: (child: any) => ({
+ delimiter: "",
+ startTag: "card-group",
+ attributes: child.attributes,
+ children: child.children,
+ }),
+};
diff --git a/src/pages/docs/[...slug].astro b/src/pages/docs/[...slug].astro
index 8daab4a..de5a81e 100644
--- a/src/pages/docs/[...slug].astro
+++ b/src/pages/docs/[...slug].astro
@@ -16,6 +16,7 @@ import * as astroContent from "astro:content";
import DocNavigationWrapper from "@/components/elements/DocNavigationWrapper.astro";
import { Icon } from "@iconify/react";
import BlogListWrapper from "@/plugins/blog-list/BlogListWrapper.astro";
+import BlockRenderer from "@/plugins/parser/block-renderer.astro";
interface Props {
element: CollectionEntry<"blog">;
}
@@ -97,7 +98,9 @@ function pascalCase(str: string) {
-
+
Date: Fri, 21 Nov 2025 15:00:31 +0100
Subject: [PATCH 2/3] feat: implement card-group mdx renderer
---
.../foundamentals/components/card.mdx | 4 +-
.../content/card-group/card-group.astro | 22 ++-
.../components/content/card-group/card.astro | 27 +++-
src/lib/plugins/card-group/plugin.ts | 126 ++++++++++++++++++
src/lib/plugins/parser/block-renderer.astro | 8 +-
src/lib/plugins/parser/plugin.ts | 44 +++---
6 files changed, 195 insertions(+), 36 deletions(-)
create mode 100644 src/lib/plugins/card-group/plugin.ts
diff --git a/content/docs/documentation/foundamentals/components/card.mdx b/content/docs/documentation/foundamentals/components/card.mdx
index 316400f..f8147c5 100644
--- a/content/docs/documentation/foundamentals/components/card.mdx
+++ b/content/docs/documentation/foundamentals/components/card.mdx
@@ -5,7 +5,9 @@ permalink: card
icon: lucide:bell
---
-:::card-group{col="2"}
+# Card
+
+:::card-group{cols=4, test="test"}
:::card{label="Test", icon="lucide:bell"}
test
:::
diff --git a/src/lib/components/content/card-group/card-group.astro b/src/lib/components/content/card-group/card-group.astro
index d533d95..83253a0 100644
--- a/src/lib/components/content/card-group/card-group.astro
+++ b/src/lib/components/content/card-group/card-group.astro
@@ -1,7 +1,25 @@
---
-const props = Astro.props;
+type Props = {
+ cols?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
+};
+
+const { cols } = Astro.props;
+const gridCols = {
+ 1: "grid-cols-1",
+ 2: "grid-cols-2",
+ 3: "grid-cols-3",
+ 4: "grid-cols-4",
+ 5: "grid-cols-5",
+ 6: "grid-cols-6",
+ 7: "grid-cols-7",
+ 8: "grid-cols-8",
+ 9: "grid-cols-9",
+ 10: "grid-cols-10",
+ 11: "grid-cols-11",
+ 12: "grid-cols-12",
+};
---
-
+
diff --git a/src/lib/components/content/card-group/card.astro b/src/lib/components/content/card-group/card.astro
index c021185..de7fdaf 100644
--- a/src/lib/components/content/card-group/card.astro
+++ b/src/lib/components/content/card-group/card.astro
@@ -1,6 +1,31 @@
---
+import { Icon } from "@iconify/react";
+
+type props = {
+ label: string;
+ icon?: string;
+};
+
const props = Astro.props;
console.log("Card", props);
---
-
Card
+
diff --git a/src/lib/plugins/card-group/plugin.ts b/src/lib/plugins/card-group/plugin.ts
new file mode 100644
index 0000000..b472f15
--- /dev/null
+++ b/src/lib/plugins/card-group/plugin.ts
@@ -0,0 +1,126 @@
+import type { Root } from "unist";
+import { visit } from "unist-util-visit";
+import { CodeBlockSelector } from "./selector";
+
+// Exemple de mapping startTag -> composant Astro
+const mdx: Record
= {
+ "card-group": "CardGroup",
+ card: "Card",
+ codeblock: "Codeblock",
+};
+
+interface Block {
+ delimiter: string;
+ startTag: string;
+ attributes: Record;
+ children: Array;
+}
+
+const parseAttributes = (str: string): Record => {
+ const regex = /(\w+)\s*=\s*(?:"([^"]*)"|(\S+))/g;
+ const attrs: Record = {};
+ let match;
+
+ while ((match = regex.exec(str)) !== null) {
+ const key = match[1];
+ let value: any;
+
+ // Valeur entre guillemets
+ if (match[2] !== undefined) {
+ const raw = match[2];
+
+ // Essayer de parser JSON (array, object, number, boolean)
+ try {
+ value = JSON.parse(raw);
+ } catch {
+ value = raw; // fallback string
+ }
+ } else if (match[3] !== undefined) {
+ // Valeur non-quoted (true, false, number, etc.)
+ const raw = match[3];
+ if (raw === "true") value = true;
+ else if (raw === "false") value = false;
+ else if (!isNaN(Number(raw))) value = Number(raw);
+ else value = raw;
+ }
+
+ attrs[key] = value;
+ }
+
+ return attrs;
+};
+
+const parseSingleNode = (node: { type: string; children?: any[] }): Block[] => {
+ const parseChildren = (children: any[]): Block[] => {
+ const blocks: Block[] = [];
+ const stack: Block[] = [];
+
+ for (const child of children) {
+ if (child.type === "text" || child.type === "mdxTextExpression") {
+ const lines = child.value.split(/\r?\n/);
+ for (const line of lines) {
+ const startMatch = line.match(/^:::(\w[\w-]*)\s*(.*)$/);
+ const endMatch = line.match(/^:::/);
+
+ if (startMatch) {
+ const block: Block = {
+ delimiter: ":::",
+ startTag: startMatch[1],
+ attributes: parseAttributes(startMatch[2] || ""),
+ children: [],
+ };
+ stack.push(block);
+ } else if (endMatch) {
+ const finished = stack.pop();
+ if (!finished) continue;
+ if (stack.length > 0) {
+ stack[stack.length - 1].children.push(finished);
+ } else {
+ blocks.push(finished);
+ }
+ } else {
+ if (stack.length > 0) {
+ stack[stack.length - 1].children.push({
+ type: "text",
+ value: line,
+ });
+ }
+ }
+ }
+ } else if (child.type === "element") {
+ const availableBlock = [CodeBlockSelector];
+ const selector = availableBlock.find((selector) =>
+ selector.filter(child),
+ );
+
+ if (selector) {
+ if (stack.length > 0) {
+ stack[stack.length - 1].children.push(selector.render(child));
+ } else {
+ blocks.push(selector.render(child));
+ }
+ }
+ }
+ }
+
+ return blocks;
+ };
+
+ return parseChildren(node.children || []);
+};
+
+// Usage dans rehypeComponents
+export default function rehypeComponents(): Plugin<[], Root> {
+ return (tree: Root) => {
+ visit(tree, "element", (node, index, parent) => {
+ const parsedBlocks = parseSingleNode(node);
+
+ parent.children[index] = {
+ type: "element",
+ tagName: "BlockRenderer",
+ properties: { ast: JSON.stringify(parsedBlocks) },
+ children: [], // ou des enfants si nécessaire
+ };
+ });
+ };
+}
diff --git a/src/lib/plugins/parser/block-renderer.astro b/src/lib/plugins/parser/block-renderer.astro
index ac1d1dc..3259e8a 100644
--- a/src/lib/plugins/parser/block-renderer.astro
+++ b/src/lib/plugins/parser/block-renderer.astro
@@ -21,9 +21,7 @@ if (!ast) return null;
const node: ASTNode | ASTNode[] = JSON.parse(ast);
-const renderNodeData = (
- block: ASTNode | { type: "text"; value: string } | undefined,
-): any => {
+const renderNodeData = (block: any): any => {
if (!block) return null;
if (Array.isArray(block)) {
@@ -34,14 +32,12 @@ const renderNodeData = (
if ("type" in block && block.type === "text") return block.value;
- const Component = mdx[block.startTag || ""];
+ const Component = mdx[block.startTag ?? ""];
- // Si aucun composant trouvé, on rend les enfants directement (raw / code / span)
const childrenRendered =
block.children?.flatMap(renderNodeData).filter(Boolean) || [];
if (!Component) {
- // Rendu direct des enfants
return childrenRendered;
}
diff --git a/src/lib/plugins/parser/plugin.ts b/src/lib/plugins/parser/plugin.ts
index d983d03..3f42efd 100644
--- a/src/lib/plugins/parser/plugin.ts
+++ b/src/lib/plugins/parser/plugin.ts
@@ -1,7 +1,6 @@
import type { Element, Root } from "unist";
import { visit } from "unist-util-visit";
-// Exemple de mapping startTag -> composant Astro
const mdx: Record = {
"card-group": "CardGroup",
card: "Card",
@@ -14,27 +13,24 @@ interface Block {
children: Array;
}
-// Parse les attributs de la ligne :::tag key="value"
const parseAttributes = (str: string): Record => {
- const regex = /(\w+)\s*=\s*(?:"([^"]*)"|(\S+))/g;
+ const regex = /(\w+)\s*=\s*(?:"([^"]*)"|([^,\s]+))/g;
const attrs: Record = {};
let match: RegExpExecArray | null;
while ((match = regex.exec(str)) !== null) {
const key = match[1];
let value: any;
-
if (match[2] !== undefined) {
- // Valeur entre guillemets
const raw = match[2];
try {
- value = JSON.parse(raw); // Essayer de parser JSON
+ value = JSON.parse(raw);
} catch {
- value = raw; // fallback string
+ value = raw;
}
} else if (match[3] !== undefined) {
- // Valeur non-quoted (true, false, number, etc.)
const raw = match[3];
+
if (raw === "true") value = true;
else if (raw === "false") value = false;
else if (!isNaN(Number(raw))) value = Number(raw);
@@ -47,9 +43,7 @@ const parseAttributes = (str: string): Record => {
return attrs;
};
-// Fonction principale : parse un node et retourne un Block[]
const parseSingleNode = (node: Element): Block[] => {
- // On combine le node text + mdxTextExpression éventuel en un seul texte
let combinedText = "";
for (const child of node.children || []) {
@@ -60,7 +54,6 @@ const parseSingleNode = (node: Element): Block[] => {
}
}
- // Fonction récursive interne pour parser un texte complet
const parseBlockText = (text: string): Block[] => {
const blocks: Block[] = [];
const lines = text.split(/\r?\n/);
@@ -79,16 +72,14 @@ const parseSingleNode = (node: Element): Block[] => {
};
if (stack.length > 0) {
- // Ajoute comme enfant du dernier parent
stack[stack.length - 1].children.push(block);
} else {
- // Bloc racine
blocks.push(block);
}
- stack.push(block); // push pour gérer la fermeture
+ stack.push(block);
} else if (endMatch) {
- stack.pop(); // on ferme le bloc courant
+ stack.pop();
} else {
if (stack.length > 0) {
stack[stack.length - 1].children.push({ type: "text", value: line });
@@ -96,7 +87,6 @@ const parseSingleNode = (node: Element): Block[] => {
}
}
- // Pousser tout bloc restant dans stack (blocs non fermés)
while (stack.length > 0) {
const remaining = stack.pop()!;
if (stack.length > 0) {
@@ -112,24 +102,26 @@ const parseSingleNode = (node: Element): Block[] => {
return parseBlockText(combinedText);
};
-// Plugin rehype
export default function rehypeComponents() {
return (tree: Root) => {
visit(tree, "element", (node: Element, index, parent) => {
if (!parent) return;
const parsedBlocks = parseSingleNode(node);
+ const hasBlock = parsedBlocks.some((block) => block.startTag in mdx);
- console.log(JSON.stringify(parsedBlocks, null, 2));
+ if (hasBlock) {
+ console.log(JSON.stringify(parsedBlocks, null, 2));
- parent.children[index] = {
- type: "element",
- tagName: "BlockRenderer",
- properties: {
- ast: JSON.stringify(parsedBlocks),
- },
- children: [],
- } as Element;
+ parent.children[index] = {
+ type: "element",
+ tagName: "BlockRenderer",
+ properties: {
+ ast: JSON.stringify(parsedBlocks),
+ },
+ children: [],
+ } as Element;
+ }
});
};
}
From d6958fa958568820a0b8e296e784c55d32028af3 Mon Sep 17 00:00:00 2001
From: Baptiste Parmantier
Date: Fri, 21 Nov 2025 15:18:45 +0100
Subject: [PATCH 3/3] docs: create card documentation
---
.../foundamentals/components/card.mdx | 123 +++++++++++++++++-
.../content/card-group/card-group.astro | 2 +-
.../components/content/card-group/card.astro | 1 -
src/lib/plugins/parser/plugin.ts | 4 +-
4 files changed, 119 insertions(+), 11 deletions(-)
diff --git a/content/docs/documentation/foundamentals/components/card.mdx b/content/docs/documentation/foundamentals/components/card.mdx
index f8147c5..5fe8a14 100644
--- a/content/docs/documentation/foundamentals/components/card.mdx
+++ b/content/docs/documentation/foundamentals/components/card.mdx
@@ -5,13 +5,124 @@ permalink: card
icon: lucide:bell
---
-# Card
+# Cards
-:::card-group{cols=4, test="test"}
-:::card{label="Test", icon="lucide:bell"}
-test
+This component allows you to group text elements while providing visual impact.
+
+```
+:::card-group
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+:::
+```
+
+:::card-group {cols=2}
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::
+
+---
+
+## Configuration
+
+### Card per line
+
+You can customize the number of elements per line by setting the `cols` prop.
+
+> [!note]
+> Default card per line was fixed to 2
+
+```
+:::card-group {cols=4}
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+:::
+```
+
+:::card-group {cols=4}
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::
+
+If the elements cannot be aligned horizontally, they will be moved to the next line.
+
+```
+:::card-group {cols=2}
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+:::
+```
+
+:::card-group {cols=2}
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+:::
+:::
+
+### Card item
+
+Vous pouvez personaliser chacune de vos cartes en utilisant les props suivantes :
+
+- `label`: Le texte à afficher en haut de la carte. (required)
+- `icon`: L'icône à afficher en haut de la carte.
+
+> [!note]
+> The cards use the [`iconify`](https://icon-sets.iconify.design/) library to display icons.
+
+```
+:::card-group
+ :::card {label="Test", icon="lucide:bell"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+ :::card {label="Test", icon="lucide:user"}
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+ :::
+:::
+```
+
+:::card-group {cols=2}
+:::card {label="Test", icon="lucide:bell"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
:::
-:::card{label="Test", icon="lucide:bell"}
-d
+:::card {label="Test", icon="lucide:user"}
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
:::
:::
diff --git a/src/lib/components/content/card-group/card-group.astro b/src/lib/components/content/card-group/card-group.astro
index 83253a0..8cff351 100644
--- a/src/lib/components/content/card-group/card-group.astro
+++ b/src/lib/components/content/card-group/card-group.astro
@@ -20,6 +20,6 @@ const gridCols = {
};
---
-
+
diff --git a/src/lib/components/content/card-group/card.astro b/src/lib/components/content/card-group/card.astro
index de7fdaf..2f453d2 100644
--- a/src/lib/components/content/card-group/card.astro
+++ b/src/lib/components/content/card-group/card.astro
@@ -7,7 +7,6 @@ type props = {
};
const props = Astro.props;
-console.log("Card", props);
---
block.startTag in mdx);
- if (hasBlock) {
- console.log(JSON.stringify(parsedBlocks, null, 2));
-
+ if (parent.type === "root" && hasBlock) {
parent.children[index] = {
type: "element",
tagName: "BlockRenderer",