diff --git a/astro.config.mjs b/astro.config.mjs index 19fbcb5832..3b75936528 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -12,7 +12,7 @@ import rehypeSlug from 'rehype-slug'; import rehypeAutolinkHeadings from 'rehype-autolink-headings'; import redirects from './src/config/redirects/index.js'; import movedPages from './src/config/redirects/moved-pages.json' with { type: 'json' }; -import { accessibleTablesIntegration } from './src/plugins/rehype-accessible-tables.mjs'; +import rehypeAccessibleTables from './src/plugins/rehype-accessible-tables.mjs'; import remarkRewriteLocalizedLinks from './src/plugins/remark-rewrite-localized-links.mjs'; import remarkCodeTabs from './src/plugins/remark-code-tabs.mjs'; import rehypeRewriteLocalizedLinks from './src/plugins/rehype-rewrite-localized-links.mjs'; @@ -45,6 +45,7 @@ export default defineConfig({ behavior: 'wrap', }, ], + rehypeAccessibleTables, ], }), }, @@ -68,7 +69,6 @@ export default defineConfig({ borderWidth: 'var(--border-width-1)', }, }), - accessibleTablesIntegration(), mdx(), icon(), react(), diff --git a/docs/content.md b/docs/content.md index 78ef752884..156cb8e9ec 100644 --- a/docs/content.md +++ b/docs/content.md @@ -300,3 +300,51 @@ Rules: - **`resources`, `support`, `blog`** are never versioned; links are language-only. - External URLs, relative links (`./x`), pure anchors (`#x`) and already-localized links (`/en/...`) are left untouched. + +## Plugins + +### 1. rehypeAccessibleTables() + +#### Why was this plugin created? + +The documentation team chose **Markdown tables as the preferred and simplest way to write tables in the documentation**. + +Markdown tables are easier to read and maintain in source files, especially for contributors. For example: + +```md +| Method | Description | +| ------ | ------------- | +| GET | Retrieve data | +| POST | Create data | +``` + +Because Markdown tables are the preferred format, we want contributors to be able to write tables using simple Markdown without having to worry about additional HTML markup for accessibility or responsive behavior. + +However, Markdown source does not provide everything we need for the final website. The generated table may need additional attributes and styling behavior, such as: + +```html +
-
- |
-
- HTTP method for which the middleware function applies.
-
-
- Path (route) for which the middleware function applies.
-
-
-
- The middleware function.
-
-
-
- Callback argument to the middleware function, called "next" by convention.
-
-
-
- HTTP [response](/api/response) argument to the middleware function, called "res" by convention.
-
-
-HTTP [request](/api/request) argument to the middleware function, called "req" by convention.
- |
inside
- * - Adds scope="row" to | inside | |
+ * Rehype plugin that:
+ * - Wraps each
|---|
in
+ * - Adds scope="row" to | in | |
+ *
+ * Supports both regular HAST elements and MDX JSX elements.
+ *
+ * NOTE:
+ * Process the table in separate passes so each step can safely modify the tree.
+ * Pass 1 and Pass 2 use the default traversal behavior, so `CONTINUE` does not
+ * need to be returned explicitly. Pass 3 uses `SKIP` after wrapping a table
+ * because its children have already been processed.
*/
-export function rehypeAccessibleTables() {
+export default function rehypeAccessibleTables() {
return (tree) => {
- // Add scope="col" to th elements inside thead
- visit(tree, 'element', (node) => {
- if (node.tagName !== 'thead') return;
- visit(node, 'element', (th) => {
- if (th.tagName === 'th' && !th.properties.scope) {
- th.properties.scope = 'col';
+ const isSupportedNode = (node) =>
+ node?.type === 'element' ||
+ node?.type === 'mdxJsxFlowElement' ||
+ node?.type === 'mdxJsxTextElement';
+
+ const getTagName = (node) => {
+ if (!isSupportedNode(node)) {
+ return null;
+ }
+
+ return node.type === 'element' ? node.tagName : node.name;
+ };
+
+ const isTable = (node) => getTagName(node) === 'table';
+ const isThead = (node) => getTagName(node) === 'thead';
+ const isTbody = (node) => getTagName(node) === 'tbody';
+ const isTr = (node) => getTagName(node) === 'tr';
+ const isTh = (node) => getTagName(node) === 'th';
+
+ const hasScope = (node) => {
+ // HAST and MDX store attributes differently.
+ if (node.type === 'element') {
+ return node.properties?.scope != null;
+ }
+
+ return node.attributes?.some((attribute) => attribute?.name === 'scope');
+ };
+
+ const setScope = (node, scope) => {
+ // add the attribute differently depending on the node type.
+ if (node.type === 'element') {
+ node.properties ??= {};
+ node.properties.scope = scope;
+ return;
+ }
+
+ node.attributes ??= [];
+
+ if (!node.attributes.some((attribute) => attribute?.name === 'scope')) {
+ node.attributes.push({
+ type: 'mdxJsxAttribute',
+ name: 'scope',
+ value: scope,
+ });
+ }
+ };
+
+ const isTableScroller = (node) =>
+ node?.type === 'element' &&
+ node.tagName === 'div' &&
+ Array.isArray(node.properties?.className) &&
+ node.properties.className.includes('table-scroller');
+
+ /*
+ * Remember which
|---|---|
| . + * + * In a normal HTML table: + * + * / | |
| + * + * Therefore the parent of | is the |