Shortcodes
Shortcodes are tags inserted in page or article content and resolved server-side at render time. All of them are parsed inside render_content_html(), in core/render/tf-shortcodes.php.
Parsing order
[gallery id="X"]→renderGallery()[toc]→ anchors on<h2>/<h3>+ table of contents[callout type="…"]…[/callout][quote author="…"]…[/quote][button …][recent_articles …][recent_projects …][articles_by_tag …][contact_form]
Each parser is conditional — if its shortcode string isn't found in the HTML, the regex never runs.
Self-closing shortcodes
[toc]
Auto-generated table of contents from every <h2> and <h3> in the content.
[button url="…" label="…" style="…"]
| Attribute | Values | Required | ||
|---|---|---|---|---|
url | Any URL | Yes | ||
label | Button text | Yes | ||
style | primary \\ | secondary \\ | outline | No (default primary) |
[recent_articles limit="3" tag="slug" category="slug"]
Grid of recent articles, optionally filtered by tag or category.
Reads from sl_load_index('article') directly — never from $GLOBALS['data']. On single-item pages $GLOBALS['data']['article'] only contains the current item; using it here would return empty results.
[recent_projects limit="3"]
Same idea, for projects.
[articles_by_tag tag="slug" limit="5"]
Alias of [recent_articles] with a mandatory tag filter.
[contact_form]
Full contact form: CSRF, honeypot, rate limiting, optional hCaptcha. Injects contact.css once per page.
Wrapping shortcodes
[callout type="info"]…[/callout]
| Type | Color |
|---|---|
info | Blue |
warning | Orange |
tip | Green |
danger | Red |
Core emits single-dash class names: sc-callout-info, sc-callout-icon, sc-callout-body, sc-callout-title. A theme stylesheet written with double-dash BEM-style names (sc-callout--info) will silently never match — check your theme's shortcodes.css uses single dashes if callouts render unstyled.
[quote author="Name"]…[/quote]
Styled blockquote with attribution.
Adding a custom shortcode
Core has no filter hook for third-party shortcodes — a theme adds one by extending render_content_html()'s own pipeline via a functions.php filter (see Hooks & Filters); a plugin instead wraps the whole page in an output buffer (see Front-End Integration) since it can't touch core files.
For a theme-owned shortcode:
- Add the parser inside your own render pipeline, following the same conditional-
strpos()pattern as the built-ins. - Use
sl_load_index($type)for any data lookup — never$GLOBALS['data']. - Prefix your CSS with
sc-in the theme'sshortcodes.css, single-dash for any sub-parts. - Add the entry to the shortcode picker in both
admin/assets/js/editor.js(WYSIWYG) andadmin/assets/js/editor-markdown.js(Markdown mode) if you want it in the picker UI.
if (strpos($html, '[myshortcode') !== false) {
$html = preg_replace_callback(
'/\[myshortcode([^\]]*)\]/i',
function ($m) {
$attrs = _shortcode_parse_attrs($m[1]);
return '<div class="sc-myshortcode">…</div>';
},
$html
);
}
