Skip to content

Frontend Templates

Overview

Frontend templates handle all user-facing pages in the Gold Standard Module. These templates follow responsive design principles and integrate with XOOPS themes while maintaining module-specific styling.

Template Flow

flowchart TB
    subgraph "Request Flow"
        A[User Request] --> B[Controller]
        B --> C[Service Layer]
        C --> D[View Model]
        D --> E[Template Engine]
        E --> F[Rendered HTML]
    end

    subgraph "Template Composition"
        G[Layout Template] --> H[Page Template]
        H --> I[Partial 1]
        H --> J[Partial 2]
        H --> K[Partial N]
    end

Main Templates

Index Template (goldstandard_index.tpl)

The homepage template displays featured and recent articles:

{* goldstandard_index.tpl *}

<div class="goldstandard-index">
    {* Hero Section with Featured Article *}
    {if $featured_article}
    <section class="hero-section">
        <article class="featured-article">
            {if $featured_article.featured_image}
            <div class="featured-image">
                <img src="{$featured_article.featured_image}"
                     alt="{$featured_article.title|escape}"
                     class="img-fluid">
            </div>
            {/if}
            <div class="featured-content">
                <span class="category-badge">
                    {$featured_article.categories[0].name|escape}
                </span>
                <h1>
                    <a href="{$featured_article.url}">
                        {$featured_article.title|escape}
                    </a>
                </h1>
                <p class="excerpt">{$featured_article.excerpt}</p>
                <div class="meta">
                    <span class="author">
                        <img src="{$featured_article.author_avatar}"
                             alt="" class="avatar-sm">
                        {$featured_article.author_name|escape}
                    </span>
                    <span class="date">
                        {$featured_article.published_at|date_format:'%B %d, %Y'}
                    </span>
                </div>
            </div>
        </article>
    </section>
    {/if}

    {* Category Navigation *}
    <nav class="category-nav" aria-label="Categories">
        <ul class="category-list">
            <li class="{if !$current_category}active{/if}">
                <a href="{$module_url}">All</a>
            </li>
            {foreach $categories as $category}
            <li class="{if $current_category == $category.id}active{/if}">
                <a href="{$category.url}">{$category.name|escape}</a>
            </li>
            {/foreach}
        </ul>
    </nav>

    {* Article Grid *}
    <section class="articles-section">
        <h2 class="section-title">{$section_title|default:'Latest Articles'}</h2>

        <div class="article-grid">
            {foreach $articles as $article}
                {include file='db:goldstandard_partials_article_card.tpl'
                    article=$article}
            {foreachelse}
                <div class="no-articles">
                    <p>{$lang.no_articles}</p>
                </div>
            {/foreach}
        </div>

        {* Pagination *}
        {if $total_pages > 1}
            {include file='db:goldstandard_partials_pagination.tpl'
                current_page=$current_page
                total_pages=$total_pages
                base_url=$module_url}
        {/if}
    </section>
</div>

Article Template (goldstandard_article.tpl)

Single article display with full content:

{* goldstandard_article.tpl *}

<article class="goldstandard-article" itemscope itemtype="https://schema.org/Article">
    {* Breadcrumb Navigation *}
    <nav class="breadcrumb-nav" aria-label="Breadcrumb">
        {include file='db:goldstandard_partials_breadcrumb.tpl'
            items=$breadcrumb}
    </nav>

    {* Article Header *}
    <header class="article-header">
        {if $article.categories}
        <div class="categories">
            {foreach $article.categories as $category}
            <a href="{$category.url}" class="category-link">
                {$category.name|escape}
            </a>
            {/foreach}
        </div>
        {/if}

        <h1 itemprop="headline">{$article.title|escape}</h1>

        <div class="article-meta">
            <div class="author" itemprop="author" itemscope itemtype="https://schema.org/Person">
                <img src="{$article.author_avatar}"
                     alt="{$article.author_name|escape}"
                     class="author-avatar">
                <span itemprop="name">{$article.author_name|escape}</span>
            </div>
            <time datetime="{$article.published_at|date_format:'%Y-%m-%d'}"
                  itemprop="datePublished">
                {$article.published_at|date_format:'%B %d, %Y'}
            </time>
            <span class="reading-time">
                {$article.reading_time} min read
            </span>
            {if $article.updated_at != $article.published_at}
            <span class="updated">
                Updated: {$article.updated_at|date_format:'%B %d, %Y'}
            </span>
            {/if}
        </div>
    </header>

    {* Featured Image *}
    {if $article.featured_image}
    <figure class="featured-image">
        <img src="{$article.featured_image}"
             alt="{$article.featured_image_alt|escape|default:$article.title}"
             itemprop="image"
             class="img-fluid">
        {if $article.featured_image_caption}
        <figcaption>{$article.featured_image_caption|escape}</figcaption>
        {/if}
    </figure>
    {/if}

    {* Article Content *}
    <div class="article-content prose" itemprop="articleBody">
        {$article.content}
    </div>

    {* Tags *}
    {if $article.tags}
    <footer class="article-footer">
        <div class="tags">
            <span class="tags-label">Tags:</span>
            {foreach $article.tags as $tag}
            <a href="{$tag.url}" class="tag" rel="tag">
                #{$tag.name|escape}
            </a>
            {/foreach}
        </div>

        {* Share Buttons *}
        {include file='db:goldstandard_partials_share.tpl'
            title=$article.title
            url=$article.url}
    </footer>
    {/if}

    {* Author Bio *}
    {if $show_author_bio}
    <aside class="author-bio">
        <img src="{$article.author_avatar}" alt="" class="author-avatar-lg">
        <div class="author-info">
            <h3>{$article.author_name|escape}</h3>
            <p>{$article.author_bio}</p>
            <a href="{$article.author_url}">View all posts</a>
        </div>
    </aside>
    {/if}

    {* Related Articles *}
    {if $related_articles}
    <section class="related-articles">
        <h2>Related Articles</h2>
        <div class="article-grid article-grid-sm">
            {foreach $related_articles as $related}
            {include file='db:goldstandard_partials_article_card.tpl'
                article=$related
                compact=true}
            {/foreach}
        </div>
    </section>
    {/if}

    {* Comments Section *}
    {if $comments_enabled}
    <section class="comments-section" id="comments">
        <h2>Comments ({$article.comment_count})</h2>
        {include file='db:goldstandard_partials_comments.tpl'
            comments=$comments
            article_id=$article.id}
    </section>
    {/if}
</article>

Category Template (goldstandard_category.tpl)

Category archive page:

{* goldstandard_category.tpl *}

<div class="goldstandard-category">
    {* Breadcrumb *}
    <nav class="breadcrumb-nav" aria-label="Breadcrumb">
        {include file='db:goldstandard_partials_breadcrumb.tpl'
            items=$breadcrumb}
    </nav>

    {* Category Header *}
    <header class="category-header">
        {if $category.image}
        <div class="category-image">
            <img src="{$category.image}" alt="{$category.name|escape}">
        </div>
        {/if}
        <h1>{$category.name|escape}</h1>
        {if $category.description}
        <p class="category-description">{$category.description}</p>
        {/if}
        <span class="article-count">
            {$category.article_count} {if $category.article_count == 1}article{else}articles{/if}
        </span>
    </header>

    {* Subcategories *}
    {if $category.children}
    <nav class="subcategories" aria-label="Subcategories">
        <h2>Subcategories</h2>
        <ul class="subcategory-list">
            {foreach $category.children as $child}
            <li>
                <a href="{$child.url}">
                    {$child.name|escape}
                    <span class="count">({$child.article_count})</span>
                </a>
            </li>
            {/foreach}
        </ul>
    </nav>
    {/if}

    {* Sort Options *}
    <div class="sort-options">
        <label for="sort">Sort by:</label>
        <select id="sort" onchange="location.href=this.value">
            <option value="{$category.url}?sort=newest"
                {if $sort == 'newest'}selected{/if}>Newest</option>
            <option value="{$category.url}?sort=popular"
                {if $sort == 'popular'}selected{/if}>Most Popular</option>
            <option value="{$category.url}?sort=title"
                {if $sort == 'title'}selected{/if}>Title A-Z</option>
        </select>
    </div>

    {* Article List *}
    <div class="article-list">
        {foreach $articles as $article}
        {include file='db:goldstandard_partials_article_card.tpl'
            article=$article
            show_category=false}
        {foreachelse}
        <div class="no-articles">
            <p>No articles in this category yet.</p>
        </div>
        {/foreach}
    </div>

    {* Pagination *}
    {if $total_pages > 1}
    {include file='db:goldstandard_partials_pagination.tpl'
        current_page=$current_page
        total_pages=$total_pages
        base_url=$category.url}
    {/if}
</div>

Search Template (goldstandard_search.tpl)

Search results page:

{* goldstandard_search.tpl *}

<div class="goldstandard-search">
    <header class="search-header">
        <h1>Search Results</h1>

        {* Search Form *}
        <form action="{$module_url}/search.php" method="get" class="search-form">
            <div class="search-input-group">
                <input type="search"
                       name="q"
                       value="{$query|escape}"
                       placeholder="Search articles..."
                       aria-label="Search">
                <button type="submit">
                    <span class="sr-only">Search</span>
                    <svg><!-- search icon --></svg>
                </button>
            </div>

            {* Filters *}
            <div class="search-filters">
                <select name="category" aria-label="Category filter">
                    <option value="">All Categories</option>
                    {foreach $categories as $cat}
                    <option value="{$cat.id}"
                        {if $filter_category == $cat.id}selected{/if}>
                        {$cat.name|escape}
                    </option>
                    {/foreach}
                </select>

                <select name="date" aria-label="Date filter">
                    <option value="">Any Time</option>
                    <option value="day" {if $filter_date == 'day'}selected{/if}>
                        Past 24 Hours
                    </option>
                    <option value="week" {if $filter_date == 'week'}selected{/if}>
                        Past Week
                    </option>
                    <option value="month" {if $filter_date == 'month'}selected{/if}>
                        Past Month
                    </option>
                    <option value="year" {if $filter_date == 'year'}selected{/if}>
                        Past Year
                    </option>
                </select>
            </div>
        </form>
    </header>

    {* Results Summary *}
    {if $query}
    <div class="results-summary">
        <p>
            {if $total_results > 0}
                Found <strong>{$total_results}</strong> results for
                "<strong>{$query|escape}</strong>"
            {else}
                No results found for "<strong>{$query|escape}</strong>"
            {/if}
        </p>
    </div>
    {/if}

    {* Search Results *}
    {if $results}
    <div class="search-results">
        {foreach $results as $result}
        <article class="search-result">
            <h2>
                <a href="{$result.url}">{$result.title|escape}</a>
            </h2>
            <p class="result-excerpt">
                {$result.excerpt_highlighted}
            </p>
            <div class="result-meta">
                <span class="category">{$result.category_name|escape}</span>
                <span class="date">{$result.published_at|date_format:'%B %d, %Y'}</span>
            </div>
        </article>
        {/foreach}
    </div>

    {* Pagination *}
    {if $total_pages > 1}
    {include file='db:goldstandard_partials_pagination.tpl'
        current_page=$current_page
        total_pages=$total_pages
        base_url="{$module_url}/search.php?q={$query|escape:'url'}"}
    {/if}

    {else if $query}
    {* No Results *}
    <div class="no-results">
        <h2>No articles found</h2>
        <p>Try different keywords or browse our categories.</p>
        <div class="suggestions">
            <h3>Popular Categories</h3>
            <ul>
                {foreach $popular_categories as $cat}
                <li><a href="{$cat.url}">{$cat.name|escape}</a></li>
                {/foreach}
            </ul>
        </div>
    </div>
    {/if}
</div>

Partial Templates

Article Card (partials/article_card.tpl)

Reusable article card component:

{* goldstandard_partials_article_card.tpl *}

<article class="article-card{if $compact} article-card--compact{/if}">
    {if $article.featured_image && !$compact}
    <a href="{$article.url}" class="article-card__image">
        <img src="{$article.featured_image}"
             alt="{$article.title|escape}"
             loading="lazy">
    </a>
    {/if}

    <div class="article-card__content">
        {if $show_category|default:true && $article.categories}
        <a href="{$article.categories[0].url}" class="article-card__category">
            {$article.categories[0].name|escape}
        </a>
        {/if}

        <h3 class="article-card__title">
            <a href="{$article.url}">{$article.title|escape}</a>
        </h3>

        {if !$compact}
        <p class="article-card__excerpt">
            {$article.excerpt|truncate:120:'...'}
        </p>
        {/if}

        <div class="article-card__meta">
            <span class="article-card__author">
                {$article.author_name|escape}
            </span>
            <span class="article-card__date">
                {$article.published_at|date_format:'%b %d'}
            </span>
            {if $article.reading_time}
            <span class="article-card__reading-time">
                {$article.reading_time} min
            </span>
            {/if}
        </div>
    </div>
</article>

Pagination (partials/pagination.tpl)

{* goldstandard_partials_pagination.tpl *}

<nav class="pagination" aria-label="Pagination">
    <ul class="pagination__list">
        {* Previous *}
        <li class="pagination__item pagination__item--prev">
            {if $current_page > 1}
            <a href="{$base_url}{if strpos($base_url, '?') !== false}&{else}?{/if}page={$current_page - 1}"
               aria-label="Previous page">
                &laquo; Previous
            </a>
            {else}
            <span aria-disabled="true">&laquo; Previous</span>
            {/if}
        </li>

        {* Page Numbers *}
        {assign var="start" value=max(1, $current_page - 2)}
        {assign var="end" value=min($total_pages, $current_page + 2)}

        {if $start > 1}
        <li class="pagination__item">
            <a href="{$base_url}?page=1">1</a>
        </li>
        {if $start > 2}
        <li class="pagination__item pagination__ellipsis">...</li>
        {/if}
        {/if}

        {for $page=$start to $end}
        <li class="pagination__item{if $page == $current_page} pagination__item--active{/if}">
            {if $page == $current_page}
            <span aria-current="page">{$page}</span>
            {else}
            <a href="{$base_url}{if strpos($base_url, '?') !== false}&{else}?{/if}page={$page}">{$page}</a>
            {/if}
        </li>
        {/for}

        {if $end < $total_pages}
        {if $end < $total_pages - 1}
        <li class="pagination__item pagination__ellipsis">...</li>
        {/if}
        <li class="pagination__item">
            <a href="{$base_url}?page={$total_pages}">{$total_pages}</a>
        </li>
        {/if}

        {* Next *}
        <li class="pagination__item pagination__item--next">
            {if $current_page < $total_pages}
            <a href="{$base_url}{if strpos($base_url, '?') !== false}&{else}?{/if}page={$current_page + 1}"
               aria-label="Next page">
                Next &raquo;
            </a>
            {else}
            <span aria-disabled="true">Next &raquo;</span>
            {/if}
        </li>
    </ul>
</nav>
{* goldstandard_partials_breadcrumb.tpl *}

<ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">
    {foreach $items as $index => $item}
    <li class="breadcrumb__item{if $item@last} breadcrumb__item--active{/if}"
        itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
        {if !$item@last}
        <a href="{$item.url}" itemprop="item">
            <span itemprop="name">{$item.title|escape}</span>
        </a>
        {else}
        <span itemprop="name">{$item.title|escape}</span>
        {/if}
        <meta itemprop="position" content="{$index + 1}">
    </li>
    {/foreach}
</ol>

CSS Classes Reference

Class Description
.goldstandard-* Module-scoped wrapper classes
.article-card Article preview card
.article-card--compact Compact card variant
.article-grid CSS Grid container for cards
.pagination Pagination wrapper
.breadcrumb Breadcrumb navigation
.prose Typography for article content