Nunjucks 101

Harrie

Info


Created
8 months, 22 days ago
Creator
Harrie
Favorites
222

Profile


Why You Might Want Write Your Layouts In Nunjucks


Harrie

Nunjucks is a templating language, which means it's built around making it easier to write repeating code, and personally when I write my own layouts I write them using nunjucks! I understand though that it might not be entirely clear why I do this, nor how you could also benefit from it, so I thought I'd put together a brief introduction to make its utility clearer.

First: Nunjucks is not supported by Toyhouse directly, but I have a live editor you can use set up on my website.

Second: Nunjucks is kind of to HTML as SASS is to CSS, it allows you to use variables, if statements, loops, macros (mixins in sass, functions in js), and so on and so forth.


Anyway, to give you a clear example of why you might use it, which one of these do you think is easier to read and nicer to edit?

No nunjucks:

<div style="scrollbar-width:thin;" class="overflow-auto py-2 h-100">
   <ul class="fa-ul ml-3 pr-3 mb-0 d-block d-sm-flex d-md-block flex-wrap">
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1
fa-check-square text-success"></i></span>  Gift Art</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-question-square text-warning"></i></span>  Gift Writing</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-times-square text-danger"></i></span>  NSFW / Fetish</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-question-square text-warning"></i></span>  Mild Gore</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1
fa-check-square text-success"></i></span>  Different outfits / hairstyles</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-question-square text-warning"></i></span>  Different species</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1
fa-check-square text-success"></i></span>  Interacting with your characters</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-question-square text-warning"></i></span>  Shipped with your characters</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-question-square text-warning"></i></span>  Shipped with my characters</li>
      <li class="py-1 mr-3"><span style="position:static;" class="fa-li mr-1"><i class="fa-light mt-1 fa-check-square text-success"></i></span>  Shipped with Tempest specifically</li>
   </ul>
</div>

With nunjucks:

<div class="overflow-auto py-2 h-100" style="scrollbar-width:thin;">
  <ul class="fa-ul ml-3 pr-3 mb-0 d-block d-sm-flex d-md-block flex-wrap">
    {{li("yes","Gift Art")}}
    {{li("ask","Gift Writing")}}
    {{li("no","NSFW / Fetish")}}
    {{li("ask","Mild Gore")}}
    {{li("yes","Different outfits / hairstyles")}}
    {{li("ask","Different species")}}
    {{li("yes","Interacting with your characters")}}
    {{li("ask","Shipped with your characters")}}
    {{li("ask","Shipped with my characters")}}
    {{li("yes","Shipped with Tempest specifically")}}
  </ul>
</div>
  • Gift Art
  • Gift Writing
  • NSFW / Fetish
  • Mild Gore
  • Different outfits / hairstyles
  • Different species
  • Interacting with your characters
  • Shipped with your characters
  • Shipped with my characters
  • Shipped with Tempest specifically

To me the answer is very obviously Nunjucks! To give you another example, here's some tabs:

No nunjucks:

<div class="container">
   <ul role="tablist" class="nav nav-tabs">
      <li class="nav-item">
         <a aria-selected="true" aria-controls="biography" role="tab" href="#biography" data-toggle="tab" id="biography-tab" class="nav-link active">Bio</a>
      </li>
      <li class="nav-item">
         <a aria-controls="stats" role="tab" href="#stats" data-toggle="tab" id="stats-tab" class="nav-link">Stats</a>
      </li>
      <li class="nav-item">
         <a aria-controls="design" role="tab" href="#design" data-toggle="tab" id="design-tab" class="nav-link">Design</a>
      </li>
   </ul>
   <div class="tab-content">
      <div aria-labelledby="biography-tab" role="tabpanel" id="biography" class="tab-pane fade show active">
         <div class="p-3 bg-faded">This is a tab! Yippee!</div>
      </div>
      <div aria-labelledby="stats-tab" role="tabpanel" id="stats" class="tab-pane fade">
         <div class="row no-gutters py-5 bg-faded">
            <div class="col text-right pr-3">
               <i class="fas fa-heart"></i> HP
            </div>
            <div class="col-11 d-flex align-items-center">
               <div style="height:10px" class="progress col p-0 mr-3">
                  <div aria-valuemax="56" aria-valuemin="0" aria-valuenow="100" style="width: 100%" role="progressbar" class="progress-bar bg-success"></div>
               </div>
               <div class="col-1 p-0">56 / 56</div>
            </div>
            <div class="col-12"></div>
            <div class="col text-right pr-3">
               <i class="fas fa-book-spells"></i> MP
            </div>
            <div class="col-11 d-flex align-items-center">
               <div style="height:10px" class="progress col p-0 mr-3">
                  <div aria-valuemax="108" aria-valuemin="0" aria-valuenow="100" style="width: 100%" role="progressbar" class="progress-bar bg-primary"></div>
               </div>
               <div class="col-1 p-0">108 / 108</div>
            </div>
            <div class="col-12"></div>
            <div class="col text-right pr-3">
               <i class="fas fa-sword"></i> STR
            </div>
            <div class="col-11 d-flex align-items-center">
               <div style="height:10px" class="progress col p-0 mr-3">
                  <div aria-valuemax="20" aria-valuemin="0" aria-valuenow="75" style="width: 75%" role="progressbar" class="progress-bar bg-danger"></div>
               </div>
               <div class="col-1 p-0">15 / 20</div>
            </div>
            <div class="col-12"></div>
            <div class="col text-right pr-3">
               <i class="fas fa-shield"></i> DEF
            </div>
            <div class="col-11 d-flex align-items-center">
               <div style="height:10px" class="progress col p-0 mr-3">
                  <div aria-valuemax="20" aria-valuemin="0" aria-valuenow="90" style="width: 90%" role="progressbar" class="progress-bar bg-info"></div>
               </div>
               <div class="col-1 p-0">18 / 20</div>
            </div>
            <div class="col-12"></div>
            <div class="col text-right pr-3">
               <i class="fas fa-rabbit"></i> DEX
            </div>
            <div class="col-11 d-flex align-items-center">
               <div style="height:10px" class="progress col p-0 mr-3">
                  <div aria-valuemax="20" aria-valuemin="0" aria-valuenow="15" style="width: 15%" role="progressbar" class="progress-bar bg-warning"></div>
               </div>
               <div class="col-1 p-0">3 / 20</div>
            </div>
            <div class="col-12"></div>
         </div>
      </div>
      <div aria-labelledby="design-tab" role="tabpanel" id="design" class="tab-pane fade">
         <div class="row no-gutters bg-faded">
            <div class="col-12 col-md-8 py-1 pl-1 pr-1 pr-md-0">
               <div class="card border-0 rounded-0 p-2 h-100">
                  <img src="https://f2.toyhou.se/file/f2-toyhou-se/images/49253829_3xs8BnCKtw2mbbY.png">
               </div>
            </div>
            <div class="col-12 col-md-4 p-1">
               <div class="card border-0 rounded-0 p-3 h-100">
                  <h2 class="text-muted font-weight-light">Design Notes</h2>
                  <hr class="w-100">
                  <ul>
                     <li>Insert notes here</li>
                     <li>Insert notes here</li>
                     <li>Insert notes here</li>
                  </ul>
               </div>
            </div>
         </div>
      </div>
   </div>
</div>

With nunjucks:

<div class="container">
  <ul class="nav nav-tabs" role="tablist">
  {{tabButton('biography','Bio',true)}}
  {{tabButton('stats','Stats')}}
  {{tabButton('design','Design')}}
  </ul>
  
  <div class="tab-content">
  {{tabContent('biography', '<div class="p-3 bg-faded">This is a tab! Yippee!</div>', true)}}
  
  {{tabContent('stats',
    "<div class='row no-gutters py-5 bg-faded'>" +
      stat("HP", "heart", "56", "56", "success")  +
      stat("MP", "book-spells", "108", "108", "primary") +
      stat("STR", "sword", "15", "20", "danger") +
      stat("DEF", "shield", "18", "20", "info") +
      stat("DEX", "rabbit", "3", "20", "warning") +
    "</div>"
    )}}
    
  {{tabContent('design',
    '<div class="row no-gutters bg-faded">' +
      column(8, 2, '<img src="https://f2.toyhou.se/file/f2-toyhou-se/images/49253829_3xs8BnCKtw2mbbY.png">', true) +
      column(4, 3, '<h2 class="text-muted font-weight-light">Design Notes</h2>
        <hr class="w-100">
        <ul>
          <li>Insert notes here</li>
          <li>Insert notes here</li>
          <li>Insert notes here</li>
        </ul>') +
    '</div>'
        )}}
  </div>
</div>

This is a tab! Yippee!
HP
56 / 56
MP
108 / 108
STR
15 / 20
DEF
18 / 20
DEX
3 / 20
49253829_3xs8BnCKtw2mbbY.png

Design Notes


  • Insert notes here
  • Insert notes here
  • Insert notes here

A lot more complicated and definitely a lot more going on, but the nunjucks is still considerably shorter and to me more readable than the HTML...

So how is this done? These are macros, prewritten pieces of HTML with variable values that you feed into the macro through the syntax {{macroName(parameter, parameter, parameter, ...)}}

A macro is first defined like so:

{% macro tabButton(ID, label, selected) %}
  <li class="nav-item">
    <a {{'aria-selected=true' if selected}} aria-controls="{{ID}}" role="tab" href="#{{ID}}" data-toggle="tab" id="{{ID}}-tab" class="nav-link {{"active" if selected}}"
    >{{label}}</a>
  </li>
{% endmacro %}
Example usage:
{{tabButton('biography', 'Bio', true)}}
	
Final output:
  <li class="nav-item">
     <a aria-selected="true" aria-controls="biography" role="tab" href="#biography" data-toggle="tab" id="biography-tab" class="nav-link active">Bio</a>
  </li>

You can also set variables outside of macros with {% set variable = value %}, e.g., {% set name = "Rosalie Thorne" %}, and then call that anywhere you like with the double bracket syntax, e.g. Hi my name is {{name}} ยป Hi my name is Rosalie Thorne.

If you include HTML inside of a variable, you'll need to mark it as safe for it to work, i.e. lets say we have this variable: {% set content = "<span class="text-primary">html in a variable</span>" %}

{{content}} โ†’ <span class="text-danger font-weight-bold">html in a variable</span>

{{content | safe}} โ†’ html in a variable


Macros aren't the only thing you can do with nunjucks though, you can also use loops for example. The tabs from before could also be written like this, writing all the tabs into one variable and then iterating through that variable in a loop:

{% set tabs = [
  { label: "Bio",
    ID: "biography",
    content: "This is a tab! Yippee!!"
  },
  { label: "Stats",
    ID: "stats",
    content: "<div class='row no-gutters py-5 bg-faded'>" +
      stat("HP", "heart", "56", "56", "success")  +
      stat("MP", "book-spells", "108", "108", "primary") +
      stat("STR", "sword", "15", "20", "danger") +
      stat("DEF", "shield", "18", "20", "info") +
      stat("DEX", "rabbit", "3", "20", "warning") +
    "</div>"
  },
  { label: "Design",
    ID: "design",
    content: '<div class="row no-gutters bg-faded">' +
      column(8, 2, '<img src="https://f2.toyhou.se/file/f2-toyhou-se/images/49253829_3xs8BnCKtw2mbbY.png">', true) +
      column(4, 3, '<h2 class="text-muted font-weight-light">Design Notes</h2>
        <hr class="w-100">
        <ul>
          <li>Insert notes here</li>
          <li>Insert notes here</li>
          <li>Insert notes here</li>
        </ul>') +
    '</div>'
  }
] %}


<ul class="nav nav-tabs" role="tablist">
{% for tab in tabs %}
  <li class="nav-item">
    <a class="nav-link  {{"active" if loop.first}}" id="{{tab.ID}}-tab" data-toggle="tab" href="#{{tab.ID}}" role="tab" aria-controls="{{tab.label}}" {{'aria-selected="true"' if loop.first}}>{{tab.label}}</a>
  </li>
{% endfor %}
</ul>
<div class="tab-content">
{% for tab in tabs %}
  <div class="tab-pane fade {{"show active" if loop.first}}" id="{{tab.ID}}" role="tabpanel" aria-labelledby="{{tab.ID}}-tab">{{tab.content}}</div>
{% endfor %}
</div>
    

I think this is a little easier to edit personally as the buttons and the tabs get made all at once instead of having to do both with individual macros, but you might prefer macros, who knows! Or maybe you'd rather just write the tabs out yourself, but use variables for other things.

Either way, the ability to use variables, macros, and loops allows you to create some very complicated layouts, that aren't too difficult to edit because all you have to do is change some values, and those values can be repeated in multiple places without copy-pasting them.


In any case that's my pitch for nunjucks ^_^ Like I said, you can play around with it on my website!