{# Content Components Reusable macro-based content display components for the Bengal default theme. Components: - article_card(article, show_excerpt=True, show_image=False): Rich article preview card - child_page_tiles(posts=None, subsections=None): Subsections and child pages as compact tiles - tag_list(tags, small=False, linkable=True): Styled tag badges - popular_tags(limit=10): Tag cloud widget with popular tags - random_posts(count=3): Random post discovery widget Usage: {% from 'partials/content-components.html' import article_card, tag_list %} {{ article_card(post, show_image=True) }} {{ tag_list(page.tags, small=True) }} #} {# Article Card Component Displays a rich card preview of an article with metadata, tags, and optional image. Args: article: Page object to display (required) show_excerpt: Boolean (default: True) - show description/excerpt show_image: Boolean (default: False) - show featured image Example: {{ article_card(post, show_image=True, show_excerpt=True) }} #} {% macro article_card(article, show_excerpt=True, show_image=False) %}
{% if show_image and article.metadata.get('image') %} {# Responsive image with srcset - dogfooding image_srcset() #} {{ article.metadata.get('image') | image_alt }} {% endif %}
{# Tag badges - dogfooding has_tag() #}
{% if article | has_tag('featured') %} ⭐ Featured {% endif %} {% if article | has_tag('tutorial') %} 📚 Tutorial {% endif %} {% if article | has_tag('new') %} ✨ New {% endif %}

{{ article.title }}

{% if article.date %} {% endif %} {% if article.metadata.get('author') and site.theme_config.config.get('show_author', true) %} {{ article.metadata.get('author') }} {% endif %} {% if article.content and site.theme_config.config.get('show_reading_time', true) %} {{ article.content | reading_time }} min read {% endif %}
{% if show_excerpt and site.theme_config.config.get('show_excerpts_default', true) %}

{% if article.metadata.get('description') %} {{ article.metadata.get('description') }} {% elif article.content %} {{ article.content | strip_html | excerpt(150) }} {% endif %}

{% endif %} {% if article.tags %}
{{ tag_list(article.tags, small=True) }}
{% endif %}
{% endmacro %} {# Child Page Tiles Component Displays subsections and child pages as compact row-based items with icons. Args: posts: List of child pages to display (optional, defaults to None) subsections: List of child sections to display (optional, defaults to None) Example: {{ child_page_tiles(posts=page.regular_pages, subsections=page.sections) }} #} {% macro child_page_tiles(posts=None, subsections=None) %} {% if posts or subsections %} {# Merge subsections and pages into unified list #} {% set all_items = [] %} {# Add subsections with metadata #} {% if subsections %} {% for subsection in subsections %} {% set _ = all_items.append({ 'type': 'section', 'obj': subsection, 'title': subsection.title, 'url': subsection.url, 'description': subsection.metadata.get('description', '') if subsection.metadata and subsection.metadata.get('description', '') else '', 'weight': subsection.metadata.get('weight', 999999) if subsection.metadata else 999999, 'page_count': subsection.pages | length if subsection.pages else 0 }) %} {% endfor %} {% endif %} {# Add pages with metadata #} {% if posts %} {% for post in posts %} {# Skip the current section index page if it appears in child posts #} {% if not ((page is defined and page) and (post.relative_url == page.relative_url)) %} {% set _ = all_items.append({ 'type': 'page', 'obj': post, 'title': post.title, 'url': post.url, 'description': post.metadata.get('description', '') if post.metadata and post.metadata.get('description', '') else (post.content | strip_html | excerpt(120) if post.content else ''), 'weight': post.metadata.get('weight', 999999) if post.metadata else 999999, 'date': post.date if post.date else none }) %} {% endif %} {% endfor %} {% endif %} {# Sort by weight (ascending), then title #} {% set sorted_items = all_items | sort(attribute='weight,title') %}
{% for item in sorted_items %}
{% if item.type == 'section' %} {% else %} {% endif %}
{{ item.title }} {% if item.description %} {{ item.description }} {% endif %} {% if item.type == 'section' and item.page_count > 0 %} {{ item.page_count }} page{{ 's' if item.page_count != 1 }} {% elif item.type == 'page' and item.date %} {{ item.date | time_ago }} {% endif %}
{% endfor %}
{% endif %} {% endmacro %} {# Tag List Component Displays tags as styled badges, optionally linkable to tag archive pages. Args: tags: List of tag strings (required) small: Boolean (default: False) - use smaller size linkable: Boolean (default: True) - make tags clickable Example: {{ tag_list(page.tags) }} {{ tag_list(article.tags, small=True, linkable=False) }} #} {# Reusable iterator that skips the current page/item. Usage: {% from 'partials/content-components.html' import for_each_item_excluding_current with context %} {% call (post) for_each_item_excluding_current(posts) %} ... render using `post` ... {% endcall %} #} {% macro for_each_item_excluding_current(items) %} {%- for item in items -%} {%- if not (page and item.relative_url == page.relative_url) -%} {{ caller(item) }} {%- endif -%} {%- endfor -%} {% endmacro %} {# Render a section with a title only if there are any items. Args: - items: list to check (required) - title: section title string (required) - section_class: optional additional CSS classes on the section Usage: {% from 'partials/content-components.html' import render_section_if_any with context %} {% call (items) render_section_if_any(my_list, 'Functions', 'api-section') %}
{% endcall %} #} {% macro render_section_if_any(items, title, section_class='') %} {% if items and (items | length) > 0 %}

{{ title }}

{{ caller(items) }}
{% endif %} {% endmacro %} {% macro tag_list(tags, small=False, linkable=True) %} {% set max_display = site.theme_config.config.get('max_tags_display', none) %} {% set display_tags = tags[:max_display] if max_display else tags %}
{% for tag in display_tags %} {% if linkable %} {# Use tag_url() function which handles baseurl and i18n prefixes correctly #} {{ tag }} {% else %} {{ tag }} {% endif %} {% endfor %} {% if max_display and tags|length > max_display %} +{{ tags|length - max_display }} more {% endif %}
{% endmacro %} {# Popular Tags Component Displays a tag cloud widget showing the most frequently used tags across the site. Args: limit: Number of tags to show (default: 10) Example: {{ popular_tags(limit=20) }} #} {% macro popular_tags_widget(limit=None) %} {% set tag_limit = limit if limit is not none else site.theme_config.config.get('popular_tags_count', 10) %} {% set top_tags = popular_tags(limit=tag_limit) %} {% if top_tags %} {% endif %} {% endmacro %} {# Random Posts Component Displays a widget with randomly selected posts from the site. Args: count: Number of random posts to show (default: 3) Example: {{ random_posts(count=5) }} #} {% macro random_posts(count=3) %} {% set random_posts = site.regular_pages | sample(count) %} {% if random_posts %} {% endif %} {% endmacro %}