Reusing Contents

User Guide → Reusing Contents

Reusing Contents

MarkBind is highly-optimized for content reuse. It offers several mechanisms to provide readers with many variations of the content while minimizing duplication at source file level. As a result, instead of creating a one-size-fits-all site, MarkBind can create a site in which readers can chart their own path of reading.

User Guide → Reusing Contents → Variables

Variables

Nunjucks variables are ideal for reusing small bits of code in multiple places; you can define a variable to represent the code bit in question and reuse it anywhere in the site by referring to the variable instead of duplicating the code bit.

MarkBind does not aim to alter the already robust variable features of Nunjucks, but provides several extensions to it.

Global Variables

Global variables are to be defined in the _markbind/variables.md file. Each variable must have an name and the value can be any MarkBind-compliant code fragment. The name should not contain - and .. For example, search-option and search.options are not allowed.

The variables declared here are available from anywhere in the code base.

Example Here's how you can define two variables year and options:

<variable name="year">2018</variable>

<variable name="options">
* yes
* no
* maybe
</variable>

To include a variable value in your code, give the variable id enclosed in Nunjucks' double curly braces syntax.

Example The year was {{ year }}. The year was 2018.

Global variables (_markbind/variables.md) will take precedence over any variables set via Nunjucks' tags (e.g. {% set %}).

Built-in Global Variables

MarkBind also provides a number of built-in variables.

Variable Notes Example Output
baseUrl Represents the root directory of the site on the server, as configured in your site configuration file.
Used for specifying intra-site links.
If baseUrl is specified as userGuide/:

<img src="{{baseUrl}}/images/logo.png" />
<img src="userGuide/images/logo.png" />
timestamp The time stamp that indicates when the page was generated.

The default values of "timeZone" and "locale" are "UTC" and "en-GB" respectively.
The following example showcases the use of the "Asia/Singapore" time zone.

Page generated at: {{timestamp}}
Page generated at: Fri, 19 Apr 2024, 10:11:32 GMT+8
MarkBind The MarkBind version in use, linked to the MarkBind website. Page generated by: {{MarkBind}} Page generated by: MarkBind 5.4.0

Importing variables from other external file formats

You can also source variables from external files using MarkBind's {% ext varName = "filepathToFile" %} Nunjucks extension. This is useful if you have external datasets you want to display in your site!

To do so, assign a root variable name (varName) to the file path from the . You may then access the file's variables using dot varName.xx or array varName[i] syntax, depending on the file's contents.

Example

CODE:

Displaying a student scoreboard stored as JSON
{% ext studentScoreboard = "userGuide/syntax/extra/scoreboard.json" %}

Student Number | Score | Rank
:----- | :-------: | ----
{% for student in studentScoreboard.students -%}
{{ student.number }} | {{ student.score }} | {{ student.rank }}
{% endfor %}

<small>Last updated at {{ studentScoreboard.lastUpdated }}</small>

JSON file used in example


OUTPUT:

Student Number Score Rank
A1234567X 87 / 100 1
A1234123U 60 / 100 3
A9876543L 76 / 100 2

Last updated at 21 November, 2020


Example

CODE:

Displaying a student scoreboard stored as CSV
{% ext studentScoreboard = "userGuide/syntax/extra/scoreboard.csv" %}

Student Number | Score | Rank
:----- | :-------: | ----
{% for student in studentScoreboard -%}
{{ student.number }} | {{ student.score }} | {{ student.rank }}
{% endfor %}

CSV file used in example


If you do not want to have a header row, you can specify it by appending a noHeader option at the end of the variable declaration. In this example, it should be {% ext studentScoreboard = "userGuide/syntax/extra/scoreboard.csv", noHeader %} . Elements have to be accessed using the [] operator (i.e. using student[0] to access student number instead of student.number).

OUTPUT:

Student Number Score Rank
A1234567X 87 / 100 1
A1234123U 60 / 100 3
A9876543L 76 / 100 2

Only .json and .csv files are supported for now.

Place global variables in `_markbind/variables.md`
<variable name="year">2018</variable>
The year was {{ year }}

User Guide → Reusing Contents → Includes

Includes

MarkBind has a powerful <include> mechanism which allows you to create documents by combining other content fragments.

You can use <include> tag to include another markdown or HTML document into the current document.

Example Including text from a tip2.md in another file.

Tip 1. ...
<include src="tips/tip2.md" />
Tip 3. ...

You can <include> a fragment of a file by specifying the #fragment-id at the end of the src attribute value, provided the fragment is wrapped in a <div>/<span> tag with the matching id.

Choose <div> over <span> when wrapping block-level elements, to prevent invalid HTML markup which causes hydration issues.

Example Including a fragment from a file:

Some text
<include src="docs/tips.md#tip-1" />
Some other text

docs/tips.md:

...
<div id="tip-1" />
  Tip 1. ...
  ...
</div>
Tip 2. ...

When setting the id of a fragment, be careful not to clash with heading anchor IDs auto-generated by MarkBind. For example, if you have a heading ## Some Useful Tips, MarkBind will auto-generate an ID some-useful-tips for that heading.

Global Effects of the Script and Styles from the Imported Externals

Importing external resources that contains script or styles can inadvertently take global effects on your MarkBind website. Due to hoisting during processing, imported scripts and stylesheets affect the entire page. This could potentially alter its appearance and behavior beyond the intended scope.

For example, if a CSS file imported via such means styles headings to be red, this change will be reflected page-wide.

To safeguard against unintended consequences, consider directly incorporating the code or customizing styles to target specific elements or classes not used universally. This approach grants more precise control over your website's presentation and reduces the risk of unexpected changes.

Add {{ baseUrl }} to make your URLs absolute links if they may be reused in different contexts

Make an internal relative link an absolute link by adding {{ baseUrl }} in front of the path. This allows the link to always point to the same target. Keep this in mind when putting content with links that is reused (eg: via <include>). This is because when your content is re-used, a relative link may no longer point to where you want it to.

Example of using absolute links in <include>



The <include> mechanism can be used inside any MarkBind source file (even inside the frontmatter section) but it will not work inside some special files such as the _markbind/variables.md.

Attributes:

  • src: specify the source file path.
  • inline (optional): make the included result an inline element. (wrapped in <span> tag). e.g.,
    The title is <include src="../docs/summary.md#title" inline /> while ...
    
  • optional (optional): include the file/fragment only if it exists i.e., there will be no error message if the file/fragment does not exist. e.g.,
    <include src="UserStories.md" optional />
    
  • trim (optional): remove leading and trailing whitespace and newlines from the document before including.
    <include src="UserStories.md#epic" trim />
    
  • omitFrontmatter (optional): omit the frontmatter of the file/fragment from being included (if any).
    <include src="UserStories.md#epic" omitFrontmatter />
    
<include> Inside an Included File

Although the src attribute of an <include> is given relative to the current directory, it is converted to an absolute value before the is included from another file.

Example Suppose you have a MarkBind project with the following file structure.

C:/mySite/ ├── bookFiles/ │ ├── book.md │ ├── chapter1.md │ └── chapter2.md └── reviewFiles/ └── review.md

The book.md:

# My Book
<include src="chapter1.md" />
<include src="chapter2.md" />

The review.md:

# My Review
<include src="../bookFiles/book.md" />
...

The content of the chapter1.md and chapter2.md will be included in the review.md (via <include src="../bookFiles/book.md" />) although chapter1.md and chapter2.md are not in reviewFiles directory. i.e., <include src="chapter1.md" /> will be interpreted as <include src="c:/mySite/bookFiles/chapter1.md" />

In other words, <include> interprets the reused code relative to the original location of the file, not the location in which it is reused.


Specifying Variables in an <include>

It is possible to include variables in an <include>.

Example Specifying title and author variables in an <include> tag:

<include src="article.md">
  <variable name="title">My Title</variable>
  <variable name="author">John Doe</variable>
</include>

In article.md:

# {{ title }}<br>
Author: {{ author }}

These variables work the same way as variables in _markbind/variables.md, except that they only apply to the included file. They allow the included file to be reused as a template, for different source files using different variable values.

You can also specify include variables within the <include> tag itself by adding a var- prefix.

Example Specifying title and author variables inline:

<include src="article.md" var-title="My Title" var-author="John Doe" />

If the same variable is defined in a chain of <include>s (e.g. a.md includes b.md includes c.md...), variables defined in the top-most <include> will take precedence. Global variables (_markbind/variables.md) will take precedence over any <include> variables. This is to allow the outer context to adapt the reused content without changing its actual content.

Preventing cyclical errors when using multiple includes with same variables



Excluding Files from Rendering as Pages

MarkBind supports the exclusion of files from page generation. For example, you can exclude files containing that are only meant to be used in <include>.

Note: This example below is assuming that you have included the following glob pattern in the site.json file:

{
  "pages": [
    {
      "glob": "*.md",
      "layout": "normal",
      "searchable": "yes"
    }
  ],
}

Else, if each page is included individually, there is no need to exclude the fragments as they will not be included in the page generation.

Example Suppose you have a fragment file content-fragment.md and you want to include it in some pages of the site course without rendering content-fragment.md as a page.

C:/course/ ├── content-fragment.md ├── index.md ├── reading.md └── site.json

In reading.md (note how it reuses content from the content-fragment.md):

# Week 1 Reading:
<include src="content-fragment.md" />

In site.json we then exclude the fragment from the page generation with pagesExclude:

...
"pagesExclude": [
  "**/*-fragment.md"
],
...

You may use any custom name you wish for your fragments but be sure to update the pagesExclude list with the appropriate glob pattern.


Using Boilerplate Files

If you find yourself duplicating a fragment in multiple places of your code base, you can use a boilerplate file to avoid such duplication. Note that you cannot use a normal <include> in this case because the code included using a normal <include> stays relative to the original location while boilerplate code needs to be interpreted relative to the location it is being used.

Example Suppose you have a MarkBind project with the following file structure.

C:/mySite/ ├── chapter1/ │ ├── chapter.md │ ├── text.md │ └── exercises.md ├── chapter2/ │ ├── chapter.md │ ├── text.md │ └── exercises.md └── book.md

The book.md:

# My Book
<include src="chapter1/chapter.md" />
<include src="chapter2/chapter.md" />

The chapter1/chapter.md:

## Text
<include src="text.md" />
## Exercises
<include src="exercises.md" />

The chapter2/chapter.md:

## Text
<include src="text.md" />
## Exercises
<include src="exercises.md" />

As you can see, both chapter.md files are exactly the same. If we were to use only one of the chapter.md files and <include> it twice in the book.md, we'll end up with the same chapter content duplicated twice, which is not what we want. In other words, chapter.md contains boilerplate code that needs to be interpreted relative to where it is applied, once relative to chapter1 directory and once relative to chapter2 directory.

To use a code fragment as a boilerplate file,

  1. Put the code in a file inside the _markbind/boilerplates directory.
  2. <include> the file as if a copy of it exists in any directory you want it to applied, but add the boilerplate attribute to the <include> tag.

Example Here's how you can use a boilerplate file to avoid duplicating the chapter.md:

C:/mySite/ ├── _markbind/boilerplates/ │ └── chapter.md ├── chapter1/ │ ├── text.md │ └── exercises.md ├── chapter2/ │ ├── text.md │ └── exercises.md └── book.md

The book.md:

# My Book
<include src="chapter1/chapter.md" boilerplate />
<include src="chapter2/chapter.md" boilerplate />

The _markbind/boilerplates/chapter.md:

## Text
<include src="text.md" />
## Exercises
<include src="exercises.md" />

Consider the line <include src="chapter1/chapter.md" boilerplate />. Note how you can use src="chapter1/chapter.md" even though there is no such file. MarkBind will use the chapter.md file from /_markbind/boilerplates/ but interpret it as if the file exists in the chapter1 directory (i.e., interpret the chapter.md code relative to the chapter1 directory).

Similarly, <include src="chapter2/chapter.md" boilerplate /> interprets the chapter.md relative to the chapter2 directory.

If you have many boilerplate files, you can organize them into directories inside the _markbind directory. When using such boilerplate files, you need to replace boilerplate attribute with boilerplate="<path to file relative to _markbind/boilerplates>".

Example Suppose the chapter.md is places in a book directory:

C:/mySite/ └── _markbind/boilerplates/ └── book/ └── chapter.md

It needs to be used as follows:

<include src="chapter1/chapter.md" boilerplate="book/chapter.md" />
<include src="chapter2/chapter.md" boilerplate="book/chapter.md" />
<include src="foo.md#bar" boilerplate inline trim>
  <variable name="x">5</variable>
</include>

Reusing Contents Across Sites

MarkBind supports reusing across sites. It allows you to include the pages you want from a sub-site in another main-site without having to change anything in the source files of the sub-site as long as the sub-site source files are inside the directory of the main-site.

Example Suppose you have a site textbook and you want to include some pages from it in another site course. Given below is how you can locate the sub-site textbook inside the root directory of the main-site course so that files from textbook can be reused in the course site.

C:/course/ ├── textbook/ │ ├── index.md │ ├── overview.md │ └── site.json ├── index.md ├── reading.md └── site.json

In reading.md (note how it reuses content from the sub-site textbook):

# Week 1 Reading:
<include src="textbook/overview.md" />

If you are using Git for version control, you can set up the sub-site repository as a Git sub-module of the main site repository.


Creating Content Variations

MarkBind can create sites that give more control to the reader. Given below are some mechanisms authors can use to create variations of content that gives more control to the reader in charting their own path through the content.

Allowing users to remove some contents

When the readers can remove an item from a page, they can create their own version of the page by removing items they don't want to see. This is especially useful when printing a page.

To make an element closeable, use v-closeable.

<div v-closeable>

Optional video:

@[youtube](v40b3ExbM0c)

</div>

This is how the content will appear. Note how you can hover over the content to access the ❌ button that can collapse the content.

Optional video:

Giving alternative contents

You can use a Tabs component to give alternative versions of content, for example, giving a code snippet in different programming languages.

Giving access to additional contents

You can use following components to give readers an option to access additional content at their discretion.

Organizing contents in alternative ways

You can take advantage of MarkBind's feature for content reuse to organize content in alternative ways to cater for different readers, without having to duplicate content. For example, you can have different pages that organizes the same information alphabetically, chronologically, by difficulty, group information by topic, etc.

Optimizing the Print View

To hide minimized panels in the print view, add the following code to a CSS file used in your site.

@media print {
    .card-container > .morph {
        display: none;
    }
}

MarkBind also comes with some built-in optimizations for printing by default:

  • Markbind enhances the readability of your code blocks for printing by
    • Applying soft wrapping, ensuring code doesn't get cut off
    • Adding line numbers to maintain context when wrapping occurs
    • Automatically changing dark code theme to light
    • Removing codeBlockCopyButtons and codeBlockWrapButtons
    There are some issues with printing in Firefox. Please use other browsers such as Chrome if these issues persist.

Hiding some info in the generated content

To permanently hide a fragment from the reader:

<span class="d-none">
  content to hide ...
</span>

<panel header="..." add-class="d-none">
  content to hide ...
<panel>

To hide a fragment in one specific page, 'mark' the elements using a class:

<span class="extra">
  content to hide ...
</span>

Then, in a page-specific CSS file,

.extra {
  display: none; /* 'block' or 'inline-block' if you want it to show */
}

Deploying a page multiple times with different titles

By overriding the title declared in the frontmatter of the page using site.json, it is possible to allow MarkBind to serve the same page with different titles.

This may especially be useful for users who are serving a page from a submodule.

Creating slight variations of content

Tags are a good way to create multiple variations of a page within the same source file, such as to filter content for creating multiple different versions of the same page. See User Guide: Tweaking the Page Structure → Tags section for more information.