Forum Discussion

KaelaC's avatar
KaelaC
Lithium Alumni (Retired)
13 years ago

How To Create a Category Based Navigation Like Lithosphere

A number of communities have implemented navigation similar to what we have on the Lithosphere with tabs across the top that help the user quickly find the major areas of the community.  This is a simple example of how that navigation is created.  In this example, each tab is a category at the top level of the community.  The tabs themselves are hard coded links to each category - Learn, Share, and Help.

 

First you need to create a custom component in Studio.  You will need to have a small amout of freemarker logic for this implementation so a custom content area will not work. 

 

Once you have the component, you must write the basic structure of your tabs. 

 

<ul class="custom-nav-wrapper">
 		<li class="nav-learn">
  			<a href="link to learn"><span>Learn</span></a>
 		</li>
 		<li class="nav-share">
  			<a href="link to share"><span>Share</span></a>
 		</li>
 		<li class="nav-help">
  			<a href="link to help"><span>Help</span></a>
 		</li>
</ul>

This is fairly simple html but you will need to add css to your skin to style it as you like.

 

Next, you need to account for the active state of each tab.  Whenever the user is in a board or thread within that category, the tab should remain highlighted.  This requires a little css to be added to the page.  For this example we will simply bold the text if the tab is active.  You can, of course, add any css here that you would like.  The Lithosphere switches out images to highlight the active area.

 

This code should be placed above the html at the top of your component

 

<#assign ancestors = coreNode.ancestors?reverse />
<#assign parent= "" >
<#if ancestors?size gt 1 >
<#assign parent=ancestors[1].id>
</#if>

<#if parent == "Learn" || coreNode.id == "Learn"> 
<style>
.nav-learn { text-weight:bold; }
</style>
<#elseif parent == "Share" || coreNode.id == "Share"> 
<style>
.nav-share { text-weight:bold; }
</style>
<#elseif parent == "Help" || coreNode.id == "Help">
<style>
.nav-help{ text-weight:bold; }
</style>
</#if>

 

The first part gets a sequence of all the nodes from the current page back up to the top of the community, then reverses it so that community is the first item, ancestors[0].  Since what we want to know is the top category, we set a variable (parent) to the second item in the sequence, ancestors[1].  The second part of our code runs through an if statement of all the top level categories and adds a little bit of css for the correct tab based on which category matches the one in our variable.

 

And that's just about it... of course you can get a lot fancier by having dynamically built navigation.  For that solution you will need to use REST calls to dyamically get all the top level categories and use that info to build your classes and styles.  However, if you don't plan to add categories regularly or if you want to choose only some of your categories to be displayed in the navigation, this will work just fine.

  • AdamN's avatar
    AdamN
    Khoros Oracle

    There was interest in the dynamic solution that Kaela alluded to her in post, so I took a quick stab at tweaking her code. Below I'll show two different approaches to accomplish basically the same thing. The approaches differ primarily in the styling methodology.

     

    Approach #1

     

    The first approach is basically a direct transformation of Kaela's code. I've made a few minor tweaks and added some logic to dynamically construct the tabs as well as to construct the appropriate styling rule. Please note that this example grabs the top-level categories of the community. If you wanted to grab nested categories also, or if you wanted to include other nodes such as boards or blogs, you will need to tweak the REST API call a bit, but the approach is basically the same.

     

    <#assign ancestors = coreNode.ancestors?reverse />
    <#assign selectedTab = "" >
    <#if ancestors?size gt 1 >
    	<#assign selectedTab=ancestors[1].id>
    <#else>
    	<#assign selectedTab=coreNode.id>
    </#if>
    
    <#if selectedTab != "">
    <style>
    	.nav-${selectedTab} { background: yellow; }
    </style>
    </#if>
    
    <#assign categories = rest("categories").categories />
    <ul class="custom-nav-wrapper">
    		<#list categories.category as category>
     		<li class="nav-${category.id}">
      			<a href="${category.@view_href}"><span>${category.title}</span></a>
     		</li>
     		</#list>
    </ul>

    As before we use the ancestors to determine which tab should be shown as selected. I use this to create a style rule based on the id of the selected node. Then we make a REST API call to get the top-level categories and iterate through these to dynamically create the tabs.

     

    Approach #2

     

    This approach is very similar. The main difference comes in the styling. Instead of dynamically creating a style rule, I'm going to dynamically apply a class to the selected tab instead. This would allow me to move the style rule out of the component if I wanted to.

     

    <#assign ancestors = coreNode.ancestors?reverse />
    <#assign selectedTab = "" >
    <#if ancestors?size gt 1 >
    	<#assign selectedTab=ancestors[1].id>
    <#else>
    	<#assign selectedTab=coreNode.id>
    </#if>
    
    <style>
    	.custom-nav2-wrapper .selected { background: yellow; }
    </style>
    
    <#assign categories = rest("categories").categories />
    <ul class="custom-nav2-wrapper">
    		<#list categories.category as category>
     		<li class="nav2-${category.id} <#if selectedTab == category.id>selected</#if>">
      			<a href="${category.@view_href}"><span>${category.title}</span></a>
     		</li>
     		</#list>
    </ul>

     You'll notice that my style rule no longer includes any FreeMarker variables; it's just a simple rule now. To compensate for this, I've added logic inside the HTML for the tab to add a class called "selected" if the tab should display as selected. 

     

     

     

    Which approach you use is really up to you depending on your design/styling goals. Keep in mind that as I mentioned previously, you may need to tweak a bit if you wanted to use different nodes for your tabs.