zfb
GitHub repository

Type to search...

to open search from anywhere

Recipe: Admonitions

Created Jun 24, 2026Takeshi Takatsudo

Register note, tip, info, warning, danger, caution, and details directives using the generic directives feature, then supply minimal component stubs and CSS hooks.

What this page covers

How to register an admonition vocabulary (note, tip, info, warning, danger, caution) and a collapsible details directive using the generic directives opt-in feature. Includes minimal component stubs and CSS patterns. zudo-doc is the fully-worked reference implementation.

Background

zfb is the engine — it provides the Directives registry as a Core primitive and the directives opt-in feature to populate it from config. The engine ships zero named directives by default. The admonition vocabulary (:::note, :::tip, etc.) is a userland policy decision — this recipe shows one way to implement it.

:::details (a collapsible section) is structurally similar to admonitions but semantically different, so it is registered separately and given its own component.

1. Register the directives

zfb.config.ts
import { defineConfig } from "zfb/config";

export default defineConfig({
  markdown: {
    features: {
      directives: {
        // Admonition group — all containers with titleFromLabel on (the default)
        note:    "Note",
        tip:     "Tip",
        info:    "Info",
        warning: "Warning",
        danger:  "Danger",
        caution: "Caution",

        // Collapsible section — separate from the admonitions
        details: {
          component: "Details",
          kind: "container",
          titleFromLabel: true,
        },
      },
    },
  },
});

After this, content authors can write:

:::note[Optional title]

Note body text.

:::

:::tip

Tip body text (no title).

:::

:::details[Click to expand]

Hidden content that the reader reveals on demand.

:::

The pipeline emits <Note title="Optional title">, <Tip>, <Details title="Click to expand">, etc. You are responsible for making these identifiers available in scope — see MDX Components.

2. Component stubs

These are minimal stubs to get started. Extend them with your design system's tokens, icons, and ARIA roles.

components/admonitions.tsx
type AdmonitionProps = {
  title?: string;
  children: React.ReactNode;
};

function admonition(kind: string) {
  return function Admonition({ title, children }: AdmonitionProps) {
    return (
      <div class={`admonition admonition-${kind}`} data-kind={kind}>
        {title && <p class="admonition-title">{title}</p>}
        <div class="admonition-body">{children}</div>
      </div>
    );
  };
}

export const Note    = admonition("note");
export const Tip     = admonition("tip");
export const Info    = admonition("info");
export const Warning = admonition("warning");
export const Danger  = admonition("danger");
export const Caution = admonition("caution");
components/details.tsx
type DetailsProps = {
  title?: string;
  children: React.ReactNode;
};

export function Details({ title, children }: DetailsProps) {
  return (
    <details class="directive-details">
      {title && <summary>{title}</summary>}
      <div class="directive-details-body">{children}</div>
    </details>
  );
}

3. Register with MDX

Add the components to your MDX components map so they are in scope at every page render.

mdx-components.tsx
import { defaultComponents } from "zfb";
import { Note, Tip, Info, Warning, Danger, Caution } from "./components/admonitions";
import { Details } from "./components/details";

export default {
  ...defaultComponents,
  Note,
  Tip,
  Info,
  Warning,
  Danger,
  Caution,
  Details,
};

4. CSS hook pattern

The data-kind attribute on each admonition wrapper gives you a single CSS hook for all variants without a separate class per kind.

.admonition {
  border-left: 4px solid var(--admonition-color, #888);
  padding: 0.75rem 1rem;
  margin: 1rem 0;
  background: color-mix(in srgb, var(--admonition-color, #888) 8%, transparent);
}

.admonition-title {
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.admonition[data-kind="note"]    { --admonition-color: #4a90d9; }
.admonition[data-kind="tip"]     { --admonition-color: #27ae60; }
.admonition[data-kind="info"]    { --admonition-color: #5b8dee; }
.admonition[data-kind="warning"] { --admonition-color: #e67e22; }
.admonition[data-kind="danger"]  { --admonition-color: #e74c3c; }
.admonition[data-kind="caution"] { --admonition-color: #f39c12; }

/* Details (collapsible) — separate from admonitions */
.directive-details {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 0.5rem 1rem;
  margin: 1rem 0;
}

.directive-details summary {
  cursor: pointer;
  font-weight: 600;
  padding: 0.25rem 0;
}

.directive-details-body {
  padding-top: 0.5rem;
}

Reference implementation

zudo-doc ships a fully-worked admonition system — typed ARIA roles, icon SVGs, dark-mode tokens, and the Details component with keyboard and animation support. Browse its source for a production-ready baseline.

See also

Revision History

Takeshi TakatsudoCreated: 2026-06-25T05:17:25+09:00Updated: 2026-06-25T05:17:25+09:00

AI Assistant

Ask a question about the documentation.