Creating a "Featured Categories" homepage banner

I’d like to create a banner section that sits below the header on the homepage and features some glammed-up category boxes. Eg:

I understand I can use the “below-site-header” outlet and the instructions from How to add custom content that only appears on your homepage to output my markup.

Ideally, I would like to have a custom setting enum field to select which categories are featured here. Is it possibly to have a custom setting field which allows you to search for categories?

I would also like to have a custom background image for these cards, in addition to the category background image. Is it possible to add another file input to the Images tab of the category settings modal? eg:

Overall, does this seem feasible, and are there any potential pitfalls or tricks I should be aware of?

3 Likes

Consider using the TLP plugin as a start. There is already a Featured Images feature that points to Topics. You might be able to reuse some of the code.

3 Likes

Could he use this?

4 Likes

Given Categories are less dynamic a more static approach might make sense :+1:

I guess you need to decide if you want it data driven or not.

3 Likes

Ideally, I would like for it to be like a fancy version of the ‘Category Box with Topics’ that also pulls in the category logo & background image, along with the latest topics in that category.

However, if this isn’t straightforward using the standard theme/plugin extension mechanisms, the juice may well not be worth the squeeze, in which case I will either do it statically, or use the category boxes homepage layout and style them as needed.

I think two key tests are:

  1. Is there a standard way of surfacing a Select Category config item?
  2. Is there a standard way of adding metadata fields to the category settings modal?

Alternatively, if I can just use category IDs in the config, but still be able to pull the data programatically, then that would probably be ok.

If, however, it’s not really feasible to get at the category data in that way, then I’ll use one of the other approaches.

You can do anything you want in a plugin (though it’s obviously best to employ strategies that minimise the risk of your plugin breaking after a core update). Sketch out your ideas, copy from existing plugins and discourse core and then start with a simple version and iterate.

3 Likes

I looked over this previous post by Kris and directly adapted it to something you may be able to use as a base. Much of what is covered here has been laid out by him in his original topic.

All of the code will be added to the </head> (head_tag.html) section of your theme.

This first section will check to see if the page you are on is the “home page”.

We will then get the sites categories using the Category.list() method on the category class.

We will then compare those to the categories you will want featured. These will be located in the definedFeaturedCategories array defined in the code below. The ones you allow will be set to the component to be passed along to a template for rendering.

<script type="text/discourse-plugin" version="0.8">
  const Category = require("discourse/models/category").default;
  // we will use the Category model to retrieve site categories

  api.registerConnectorClass('above-main-container', 'featured-categories', {
    // above-main-container is the plugin outlet,
    // featured-topics is your custom component name

    setupComponent(args, component) {

      api.onPageChange((url, title) => {
        console.log(url,title)
        if ((url === "/") || (url === "/latest") ) {
        // on page change, check if url matches
        // if your homepage isn't /latest change this to /categories
        
          $('html').addClass('custom-featured-categories');
          // add a class to the HTML tag for easy CSS targetting

          let definedFeaturedCategories = ["uncategorized","blog","two"]
          // array of categories you want to feature
          // be sure to put the category titles in lowercase

          let featuredCategories = [];

          categories = Category.list();

          for (let cat of categories) {
            if (definedFeaturedCategories.includes(cat.name.toLowerCase())) {
              // only pushes categories you want to feature into the array to be rendered
              featuredCategories.push(cat)
            }
          }

          component.set('featuredCategories', featuredCategories)

        } else {

        // If the page doesn't match the urls above, do this:
          $('html').removeClass('custom-featured-categories');
          // Remove our custom class
          component.set('categories',[])
          // set the categories to an empty array to disable rendering
        }
      });
    }
  });
</script>

This next section injects the template for the component created above into the plugin outlet above-main-container. It will call the custom categories-wrapper created in the third step and define categories to be featuredCategories created above.

<script type="text/x-handlebars" data-template-name="/connectors/above-main-container/featured-categories">
      <div class="custom-featured-categories-wrapper">
        {{categories-wrapper categories=featuredCategories}}
          <!-- use the categories-wrapper template created below -->
          <!-- define categories to be featuredCategories created in the script above -->
      </div>
</script>

The third section creates the custom categories-wrapper handlebars template to render the featured categories. It is directly adapted from the Discourse categories-only.hbs used on Discourse’s categories page.

<script type="text/x-handlebars" data-template-name="components/categories-wrapper">
  <!-- This creates a component template called 'categories wrapper' -->
  <!-- All of this template code was adapted from Discourse's own categories page template
   https://github.com/discourse/discourse/blob/acd1693dac1bff6ff50250d942134bc48a27ff14/app/assets/javascripts/discourse/templates/components/categories-only.hbs -->

  <div class="top-categories-wrapper">
    {{#each categories as |c|}}
        <div class="top-category-column-one">
          {{category-title-link category=c}}
          {{#if c.escription}}
            <div class="category-description">
              {{dir-span c.description}}
            </div>
          {{/if}}
          {{#if c.isGrandParent}}
            <table class="category-list subcategories-with-subcategories">
              <tbody>
                {{#each c.subcategories as |subcategory|}}
                  <tr
                    data-category-id={{subcategory.id}}
                    data-notification-level={{subcategory.notificationLevelString
                    }}
                    class="{{if
                        subcategory.description_excerpt
                        "has-description"
                        "no-description"
                      }}
  
                      {{if subcategory.uploaded_logo.url "has-logo" "no-logo"}}"
                  >
                    <td
                      class="category"
                      style={{border-color subcategory.color}}
                    >
                      {{category-title-link tagName="h4" category=subcategory}}
                      {{#if subcategory.description_excerpt}}
                        <div
                          class="category-description subcategory-description"
                        >
                          {{{dir-span subcategory.description_excerpt}}}
                        </div>
                      {{/if}}
                      {{#if subcategory.subcategories}}
                        <div class="subcategories">
                          {{#each subcategory.subcategories as |subsubcategory|
                          }}
                            {{#unless subsubcategory.isMuted}}
                              <span class="subcategory">
                                {{category-title-before category=subsubcategory
                                }}
                                {{category-link subsubcategory hideParent="true"
                                }}
                              </span>
                            {{/unless}}
                          {{/each}}
                        </div>
                      {{else if subcategory.description_excerpt}}
                        <div
                          class="category-description subcategory-description"
                        >
                          {{{dir-span subcategory.description_excerpt}}}
                        </div>
                      {{/if}}
                    </td>
                  </tr>
                {{/each}}
              </tbody>
            </table>
          {{else if c.subcategories}}
            <div class="subcategories">
              {{#each c.subcategories as |subcategory|}}
                {{#unless subcategory.isMuted}}
                  <span class="subcategory">
                    {{category-title-before category=subcategory}}
                    {{category-link subcategory hideParent="true"}}
                    {{category-unread category=subcategory}}
                  </span>
                {{/unless}}
              {{/each}}
            </div>
          {{/if}}
        </div>
        <div class="top-category-column-two">
          <span class="topics-header">
            Topics
          </span>
          <span class="topics-count">
            {{c.topic_count}}
          </span>
          {{category-unread category=c tagName="div" class="unread-new"}}
        </div>
    {{/each}}
  </div>
</script>

This should help you begin to do what you requested in your OP.

For styling the color of each category box according to its custom defined colors, in the third section, you can hard-code styles using # + c.color to access the categories color code.

Other than that, styling can be done in the common.scss file.

7 Likes

Wow! Thanks for this detailed and extremely helpful reply, @jordan.vidrine! I’ll give it a try and let you know how I get on :slight_smile:

3 Likes

That worked great, @jordan.vidrine:

The only weird thing is that the topics only load after visiting /categories, otherwise it’s like there aren’t any topics at all:

Any idea what’s going on there?

1 Like

Hmm. Sorry I am not sure. I am not able to reproduce that issue.

2 Likes