Components
Sourcey renders directives in standard markdown. The ::: syntax is processed at build time; no JavaScript ships for static content. Every component on this page is rendered using the syntax it documents.
To see all of these components used in a real project, check the Cheese Store demo and its source on GitHub.
Callouts
Four types for different contexts. Add text after the type to override the default title.
Use note for supplementary information that supports the surrounding content.
Use warning for things that will break, cost money, or lose data if ignored.
Use tip for shortcuts, best practices, or non-obvious techniques.
Use info for context that helps the reader understand why something works the way it does.
Add text after the type to set a custom title. This callout uses :::note Custom Title.
Syntax:
:::note
Content here.
:::
:::warning Custom Title
Content with a custom title.
:::Available types: note, warning, tip, info.
Steps
Numbered processes with a visual connector line. Write a standard ordered list inside :::steps.
Write the directive
Open with :::steps on its own line.
Add numbered items
Use a standard markdown ordered list. The first line is the step title; subsequent indented lines are the content.
Close it
End with ::: on its own line.
Syntax:
:::steps
1. Step title
Step content goes here. Indent by 3 spaces.
2. Another step
More content.
:::Tabs
Tabbed content panels. Each tab is a ::tab child with a title attribute.
Content for the first tab. This tab is active by default.
Content for the second tab. Click to reveal.
Tabs work for any content: text, code blocks, lists, images.
Syntax:
:::tabs
::tab{title="Tab Name"}
Content here.
::
::tab{title="Another Tab"}
More content.
::
:::Code Group
Tabbed code blocks with syntax highlighting. Wrap fenced code blocks in :::code-group. Each block needs a title attribute.
const res = await fetch('https://api.example.com/items');
const data = await res.json();import requests
res = requests.get('https://api.example.com/items')
data = res.json()curl -s 'https://api.example.com/items' | jqSyntax:
:::code-group
```javascript title="Node.js"
const res = await fetch('https://api.example.com/items');
```
```python title="Python"
import requests
res = requests.get('https://api.example.com/items')
```
:::Cards
Grid of linked or static cards with icons. Wrap ::card children in :::card-group. The cols attribute controls the grid (2, 3, or 4 columns on desktop; always 1 on mobile).
With a link
Cards with an href attribute are clickable and navigate to that page.
With an icon
Cards without href are static. Use them to highlight features or concepts.
Minimal
Only title is required. Icon and href are optional.
Two-column variant:
Left card
Two-column layouts work well for binary choices or before/after comparisons.
Right card
The grid is responsive. On mobile, cards always stack vertically.
Syntax:
:::card-group{cols="3"}
::card{title="Card Title" icon="book" href="/path"}
Card content.
::
::card{title="Another Card" icon="lock"}
More content.
::
:::Attributes:
card-group:cols(number of columns, default 2)card:title(required),icon(optional),href(optional)
Accordion
Expandable sections using the HTML <details> element. Consecutive accordions are automatically grouped.
Is this rendered with details/summary?
Yes. Each accordion is a native <details> element with a styled <summary>. No JavaScript required for the expand/collapse behavior.
Do consecutive accordions group automatically?
They do. Place multiple :::accordion blocks one after another and sourcey wraps them in a visual group with shared borders.
Can I have a single accordion?
Absolutely. A single :::accordion renders standalone without group styling.
:::accordion{title="Question or section title"}
Answer or content here.
:::Place multiple accordions consecutively for automatic grouping.
Video
Embed YouTube, Vimeo, or raw video files. YouTube URLs are rewritten to youtube-nocookie.com for privacy.
Syntax:
::video[https://www.youtube.com/watch?v=VIDEO_ID]{title="Optional title"}
::video[https://vimeo.com/123456789]{title="Vimeo video"}
::video[https://example.com/video.mp4]{title="Self-hosted video"}YouTube and Vimeo URLs render as iframes. Raw .mp4 and .webm URLs render as native <video> elements.
Iframe
Embed any external page. Only https:// and http:// URLs are allowed.
Syntax:
::iframe[https://example.com]{title="Example embed" height="400"}Attributes:
title(optional) — accessible label for the iframeheight(optional, default400) — height in pixels
JSX syntax
Sourcey also accepts JSX-style component syntax. These are preprocessed to directives internally, so the output is identical.
<Steps>
<Step title="First">Content</Step>
<Step title="Second">Content</Step>
</Steps>
<Tabs>
<Tab title="First">Content</Tab>
<Tab title="Second">Content</Tab>
</Tabs>
<CodeGroup>
(fenced code blocks with title attributes)
</CodeGroup>
<CardGroup cols={2}>
<Card title="Title" icon="lock" href="/path">Content</Card>
</CardGroup>
<AccordionGroup>
<Accordion title="Question">Answer</Accordion>
</AccordionGroup>
<Note>Informational callout</Note>
<Warning title="Careful">Warning with custom title</Warning>
<Tip>Helpful suggestion</Tip>
<Info>Contextual information</Info>
<Video src="https://www.youtube.com/watch?v=ID" title="Video title" />
<Iframe src="https://example.com" title="Embed" height="500" />Directive syntax (:::) is the recommended format. JSX syntax exists for compatibility and convenience; both produce the same HTML output.
Nesting directives inside other directives is not supported. Each directive block must be a top-level element in your markdown.
