-- allium: 1 -- bDS Liquid Template Context Specification -- Scope: core (Wave 4 — rendering parity) -- Distilled from: ../bDS/src/main/engine/GenerationRouteRendererFactory.ts, -- PageRenderer.ts, BlogGenerationEngine.ts -- -- This document specifies the exact data structure passed to Liquid templates -- during rendering. It is the contract between the generation engine and -- template authors. -- ============================================================================ -- GLOBAL TEMPLATE VARIABLES -- ============================================================================ surface TemplateRenderingSurface { facing _: RenderPipeline provides: RenderPostPageRequested(post, language) RenderListPageRequested(posts, pagination, archive_context) RenderNotFoundPageRequested() } surface RenderContextSurface { context context: RenderContext exposes: context.language context.language_prefix context.page_title context.pico_stylesheet_href context.blog_languages context.alternate_links context.menu_items context.post context.day_blocks context.archive_context context.is_list_page context.prev_page_href context.next_page_href context.not_found_message } surface PaginationContextSurface { context pagination: PaginationContext exposes: pagination.is_first_page pagination.is_last_page pagination.has_prev_page pagination.has_next_page pagination.prev_page_href pagination.next_page_href pagination.current_page pagination.total_pages pagination.total_items pagination.items_per_page } value RenderContext { -- Top-level variables available in all templates language: String -- Current language code language_prefix: String? -- "/de" or "" depending on language html_theme_attribute: String -- Theme class for element page_title: String -- Page title for tag pico_stylesheet_href: String -- Path to Pico CSS theme blog_languages: List<BlogLanguage> alternate_links: List<AlternateLink> menu_items: List<MenuItem> calendar_initial_year: Integer calendar_initial_month: Integer post: PostContext? -- Present on single post pages post_categories: List<Category> post_tags: List<Tag> tag_color_by_name: Map<String, String> backlinks: List<Backlink> day_blocks: List<DayBlock> archive_context: ArchiveContext? show_archive_range_heading: Boolean min_date: Timestamp? max_date: Timestamp? is_list_page: Boolean is_first_page: Boolean is_last_page: Boolean has_prev_page: Boolean has_next_page: Boolean prev_page_href: String? next_page_href: String? not_found_message: String? not_found_back_label: String? -- Lookup maps for macro expansion canonical_post_path_by_slug: Map<String, String> canonical_media_path_by_source_path: Map<String, String> post_data_json_by_id: Map<String, PostDataJson> } value BlogLanguage { is_current: Boolean code: String flag: String href: String href_prefix: String } value AlternateLink { href: String hreflang: String } value MenuItem { href: String title: String has_children: Boolean children: List<MenuItem>? } -- ============================================================================ -- POST CONTEXT (Single Post Pages) -- ============================================================================ value PostContext { id: String title: String content: String -- Rendered HTML (after markdown + macros) slug: String excerpt: String? author: String? language: String? show_title: Boolean -- Always true for post pages published_at: Timestamp created_at: Timestamp updated_at: Timestamp tags: List<String> categories: List<String> template_slug: String? do_not_translate: Boolean linked_media: List<MediaContext> outgoing_links: List<LinkContext> incoming_links: List<LinkContext> } value PostDataJson { -- Serialized post data for Liquid template access id: String title: String slug: String excerpt: String? author: String? language: String? published_at: Timestamp created_at: Timestamp updated_at: Timestamp tags: List<String> categories: List<String> } value MediaContext { id: String filename: String original_name: String mime_type: String title: String? alt: String? caption: String? author: String? width: Integer? height: Integer? file_path: String } value LinkContext { href: String title: String display_slug: String } -- ============================================================================ -- ARCHIVE CONTEXT (List Pages) -- ============================================================================ value ArchiveContext { kind: category | tag | date name: String? month: Integer? year: Integer? day: Integer? } value DayBlock { show_date_marker: Boolean date_label: String posts: List<PostContext> show_separator: Boolean } value Category { name: String slug: String post_count: Integer } value Tag { name: String slug: String color: String? post_count: Integer } value Backlink { path: String display_slug: String title: String } -- ============================================================================ -- PAGINATION CONTEXT -- ============================================================================ value PaginationContext { is_list_page: Boolean is_first_page: Boolean is_last_page: Boolean has_prev_page: Boolean has_next_page: Boolean prev_page_href: String? next_page_href: String? current_page: Integer total_pages: Integer total_items: Integer items_per_page: Integer } -- ============================================================================ -- LIQUID FILTER SPECIFICATION -- Note: Allium does not have a 'filter' keyword. Filters are documented here -- for reference but are implemented in the template engine, not in Allium specs. -- ============================================================================ -- Built-in filters: -- default: {{ value | default: "fallback" }} -- escape: {{ text | escape }} -- url_encode: {{ text | url_encode }} -- append: {{ text | append: suffix }} -- -- Custom filters: -- i18n: {{ "key" | i18n: language }} - translation lookup -- markdown: {{ content | markdown: ... }} - markdown rendering with macro expansion -- ============================================================================ -- BUILT-IN MACROS -- Note: Allium does not have a 'macro' keyword. Macros are documented here -- for reference but are implemented by the rendering subsystem. -- ============================================================================ -- Built-in macros: -- gallery: [[gallery images=post.linked_media columns=3]] -- youtube: [[youtube id=dQw4w9WgXcQ]] -- vimeo: [[vimeo id=123456789]] -- photo_archive: [[photo_archive media=project.media]] -- tag_cloud: [[tag_cloud tags=Tags]] -- ============================================================================ -- TEMPLATE LOOKUP RULES -- ============================================================================ invariant TemplateLookupPriority { -- Templates are resolved in this order: -- 1. Post-specific template (post.template_slug) -- 2. Tag-specific template (tag.post_template_slug) -- 3. Category-specific template (category.post_template_slug) -- 4. Default "post" template -- -- List pages use "list" template -- 404 pages use "not_found" template -- Partials are referenced via {% render 'partial' %} } invariant PartialResolution { -- Partials are looked up in templates/ directory -- Usage: {% render 'partials/header', context_var: value %} -- Partial files: templates/partials/{name}.liquid } -- ============================================================================ -- RENDERING RULES -- ============================================================================ rule RenderPostPage { when: RenderPostPageRequested(post, language) let template = resolve_template(post) let context = BuildPostContext(post, language) ensures: RenderedHtml = liquid_render(template.content, context) } rule RenderListPage { when: RenderListPageRequested(posts, pagination, archive_context) let template = first (t in Templates where t.kind = "list" and t.enabled) let context = BuildListContext(posts, pagination, archive_context) ensures: RenderedHtml = liquid_render(template.content, context) } rule RenderNotFoundPage { when: RenderNotFoundPageRequested() let template = first (t in Templates where t.kind = "not_found" and t.enabled) let context = BuildNotFoundContext ensures: RenderedHtml = liquid_render(template.content, context) }