📜 ⬆️ ⬇️

Advanced Jekyll


Jekyll is a static site generator. This means that some information is given to the input, and the output is a set of HTML pages. Everything is great when the site is simple or even one-page. But what about more complex sites? Will Jekyll cope? Will it be convenient?


This publication is an attempt to summarize the knowledge gained from the creation of several websites. Therefore, I leave links to both working examples and their full source code on GitHub. The level of material goes from simple to complex.


On Habré there have already been several publications on Jekyll: A practical guide to Jekyll , a blog on Jekyll and Github , Jekyll 2 is coming on Github! . However, they all describe mostly the basic features of Jekyll. In addition, Jekyll 3 is already being used on GitHub Pages, which has brought new buns. So, let's begin!


Static html


Github Pages uses Jekyll as a website generator. Can GitHub Pages be used on their own, without Jekyll? Yes! You can use GitHub Pages as a simple HTML hosting (for free!). Especially if the site has already been generated somewhere.


- Winter Novel


For example, the Winter Novel Web demo was generated in a C application. This is just a bunch of simple HTML pages located on GitHub Pages. Sources here .


Custom domains


As was seen above, it is possible to use your domain name instead of * .imtqy.com . And to do this is quite simple: you need to create a CNAME file with a single line — the domain:


winternovel.dexp.in 

After the commit, the set domain can also be seen in the repository settings:


GitHub Pages:


You can use domains of any level, not just the third. For example, dexp.in is also hosted on GitHub.


Cloudflare


Another great free service is CloudFlare . Indeed, in any case, you need a DNS manager for the domain. And CloudFlare provides not only DNS management tools, but also a CDN service. This means that CloudFlare can cache pages and display them even if GitHub Pages is unavailable.


dexp.in  CloidFlare


The main domain directly by IP is bound to GitHub. Subdomains are implemented via cname-alias on dexp.imtqy.com .


Markdown


Jekyll supports both HTML and Markdown. HTML gives more features, but is not very convenient for writing by humans. Markdown is a very cool text writer.


An example of a piece of text in HTML :


 <p>Creating a website in Manga Maker Comipo is almost like creating it in Photoshop. That means the program is only responsible for creating the image of the future design. Everything else must be done in other programs and requires skills and knowledge of HTML and CSS.</p> <p>Let's see an example shown below:</p> <p class="centered"><img src="{{ page.linkadd }}pic/tutorials/site/OneMangaDay-site-comipo.png" alt="Website in Comipo" class="imgshad"></p> <p>You can see "Layer List" panel. It already has a stack of layers. You just need to implement this stack in HTML! The most convenient way is exporting each layer as a separate image. Export can be accessed from the menu "File - Export Image File". Also you can press F2. Export options:</p> <p class="centered"><img src="{{ page.linkadd }}pic/tutorials/site/OneMangaDay-branch-export.png" alt="Export parameters for website miking in Comipo"></p> 

At a minimum, you need to add both <p> and </p> ... And an example of a piece of text on Markdown :


 Visual novels creating is not such a difficult thing, as it might seem. And RenPy engine will help us: [http://renpy.org](http://renpy.org){:target="_blank"}. On the one hand, the engine is simple and understandable even for beginners. On the other hand, the engine is quite powerful and allows you to create really cool games. You need to download engine and install it. Nothing complicated in this process is not present, the default settings are good. Here is the RenPy main window: ![RenPy main window]({{ page.picdir }}RenPy-main-en.png){:.imgshad} There is a list of projects on left. And active project options on the right (the active project is highlighted with blue in projects list ). To create your game you need to click "Add New Project" under the list of projects. Further, the engine will ask a few simple questions. Remember the name of the game should be in English (do not use international/unicode symbols). 

Markdown code looks much better. Just try, you will not regret.


SASS / SCSS


Sass is a language on top of CSS. A lot of good functionality has been added: variables, code blocks, nested rules, include, etc. Sample SCSS code with impurities and variables:


 @mixin border-radius($radius,$border,$color) { -webkit-border-radius: $radius; -moz-border-radius: $radius; -ms-border-radius: $radius; border-radius: $radius; border:$border solid $color } .box { @include border-radius(10px,1px,red); } 

The code will be translated to:


 .box { -webkit-border-radius: 10px; -moz-border-radius: 10px; -ms-border-radius: 10px; border-radius: 10px; border: 1px solid red; } 

Useful links: Sass Basics , Sass Tutorial


Markup


Ok, pure HTML is already good. But maybe in Jekyll there is something like include ? Then you can build your pages in the style:


 include header.html {   } include footer.html 

Yes, in Jekyll there is include . But it is better to use this directive for other purposes. For the appearance it is better to use templates: Layout .


Let the source code of the page look like this:


 --- layout: default --- {   } 

And the _layouts / default.html file:


 <html>... {{ content }} ...</html> 

The page generates some code and saves it to the content variable. Jekyll sees from the header that the next file being processed will be _layouts / default.html . Next, you just need to display the contents of this variable anywhere in the code.


Of course, in reality, the code is more complex. For example, the main page One Manga Day and layout for it .


Variables


We have already touched on the topic of variables. Consider in more detail the title of the main page of One Manga Day :


 --- layout: default curlang: en title: Home page addcss: badges --- 

The variable layout tells Jekyll which next file will be processed. The remaining variables are used in the layout code and are created for convenience:


 <!DOCTYPE html> <html> <head> <title>{{ site.name }} | {{ page.title }}</title> 

The variable page.title is set in the page header, site.name is in _config.yml . The result of executing this line in my case is: One Manga Day | Home page One Manga Day | Home page


Collections


Jekyll’s previous capabilities were not very advanced, even more basic. But collections allow you to do truly powerful things. For example, galleries. And as a particular example - a page with manga .


One Manga Day: Manga page


A small clipping of _data / galleries.yml :


 - id: screenshots description: One Manga Day screenshots imagefolder: pic/scr images: - name: OMD-s0001.png thumb: thumb-1.jpg text: Main menu - name: OMD-s0002.png thumb: thumb-2.jpg text: Instructor 

This code creates a site.data.galleries collection. The first minus sign indicates the creation of a collection item. images is a subcollection, each element that has the name , thumb and text fields.


And an example of how to work with this collection is _includes / mangascript.html :


 <script type="text/javascript"> var imageGallery = [ {% assign gallery = site.data.galleries | where:"id",page.gId | first %} {% for image in gallery.images %} "{{ page.linkadd }}{{ gallery.imagefolder }}/{{ image.name }}", {% endfor %} ]; ... </script> 

Initially, the site.data.galleries collection is filtered by the id field (the value must be equal to page.gId ). The result of the where filter can have multiple values, so only the first is taken (this is permissible, since the collection id is assumed to be unique in this case). The result is saved in the variable gallery .


Then just go through the entire gallery in the for loop.


The result of the performance will be something like this:


 var imageGallery = [ "pic/manga/OneMangaDay_000_001.png", "pic/manga/OneMangaDay_000_002.png", ... "pic/manga/OneMangaDay_999.png" ]; 

Useful links: Jekyll (Liquid) reference , "Advanced Liquid: Where" .


Galleries built into the page


Another good opportunity for galleries is to embed their collections right into the page code. For example, the game page :


 --- layout: page title: "One Manga Day" gallery: - image_url: omd/OMD-s0001.jpg - image_url: omd/OMD-s0002.jpg - image_url: omd/OMD-s0003.jpg ... buttons: - caption: "Website" url: "http://onemangaday.dexp.in/" class: "warning" - caption: "Steam" url: "http://store.steampowered.com/app/365070/" class: "info" ... --- Manga are... {% include gallery %} ... {% include buttons %} 

Two collections are built into this page: gallery and buttons . Variables page.gallery and page.buttons are used in _includes / gallery and _includes / buttons respectively.


Gallery example


This method is well suited if you want to display some information specific only to this page. If collections are used throughout the site (contacts / menus, etc.), then it is better to use the previous method and store the collections in the _data directory.


Filter sets


We have already covered the topic of filters. Now you can take a more advanced task. I want to see all my "Linux" posts, grouped by year, in only 3 years.


 {% assign cp = site.tags.linux | sort | reverse %} {% assign byYear = cp | group_by_exp:"post", "post.date | date: '%Y'" %} {% for yearItem in byYear limit:3 %} <h4>{{ yearItem.name }}</h4> <ul> {% for post in yearItem.items %} <li><a href="{{ post.url }}">{{ post.title }}</a></li> {% endfor %} </ul> {% endfor %} 

First you need to take all the posts on the tag "Linux": site.tags.linux . The next line groups by date. You can select any field or format to group. Well, the last condition is satisfied through the limit of the for loop. Conclusion:


Posts grouped by year


Actually it is used in my photo album ( source ).


Variable record


Let it be necessary not to immediately output the {{ content }} variable, but to modify it somehow, and only then output it.


Normal assignment:


 {% assign someString = "value" %} 

It does not work, because we need not raw (raw) lines, but preprocessed with Jekyll.


The solution is the directive capture . And for example, the hack for traversing the bug in compress.html will be considered. Let the original code look like this:


 {% highlight AnyLanguage linenos %} Some code {% endhighlight %} 

Change it to use capture :


 {% capture _code %}{% highlight AnyLanguage linenos %} Some code {% endhighlight %}{% endcapture %}{% include fixlinenos.html %} {{ _code }} 

Now all the highlighted code is in the _code variable. Then it is processed in _include / fixlinenos.html :


 {% if _code contains '<pre class="lineno">' %} {% assign _code = _code | replace: "<pre><code", "<code" %} {% assign _code = _code | replace: "</code></pre>", "</code>" %} {% endif %} 

The code checks for the occurrence of the <pre class="lineno"> substring. If such is found, then we have in stock a bold HTML-code, the wrong pieces of which are simply replaced with the correct ones.


Useful link: Jekyll (Liquid) string filters


Code compression


You can shrink the entire SASS code with just one line in _config.yml :


 sass: style: :compressed 

If you need to squeeze the SASS code on the fly, you can use the scssify filter (the compression style from the config will also be applied to the filter):


 {{ some_scss | scssify }} 

Jekyll does not provide standard methods for compressing HTML. You can use a third-party solution - compress.html . You need to add just one line to your top-level layout :


 --- layout: compress --- 

Compression will occur after all the code generation. Ultimately, the HTML code of the page will look something like this:


HTML


Its syntax highlighting


Let it be necessary to highlight the syntax of some exotic programming language, which Jekyll does not know. This can be done programmatically.


I did customhighlight.html to highlight RenPy code (based on Python). The idea is simple and is also based on the replace filter:


 {% assign _customtag = "image side hide play show scene" | split: " " %} {% for _element in _customtag %} {% capture _from %}<span class="n">{{ _element }}{% endcapture %} {% capture _to %}<span class="k">{{ _element }}{% endcapture %} {% assign _code = _code | replace: _from, _to %} {% endfor %} 

Here an array of tags is formed, then search-replace for each element in the line of code. The only new thing is splitting a string into an array. An example of highlighted code:


RenPy code


Use example here . Also, the backlight is built into the code of the original article in English .


Tag cloud


Tag cloud is even more challenging. First, for each tag manually a page must be created. Secondly, you need to make an array of valid tags with their human-readable names. Thirdly, the script should calculate the number of articles for each tag.



The main calculations take place in the _includes / tagcloud.html file :


 <ul id="cloud"> <li style="font-size: 150%"><a href="index.html">All</a></li> {% for tag in site.tags %} {% assign curTag = tag | first | slugize %} {% assign langtag = landat.tags | where:"slug",curTag | first %} <li style="font-size: {{ tag | last | size | times: 100 | divided_by: site.tags.size | plus: 20 }}%"> <a href="{{ curTag }}.html">{{ langtag.name }}</a> </li> {% endfor %} </ul> 

The site.tags variable stores all used tags from all articles. The variable langtag is the current tag in the human format, described in _data / lang.yml .


At each iteration there will be a tag variable taken from the site.tags collection. The tag variable contains a list of all articles with this tag. So you can just take the size, multiply, divide, etc. The smallest tag was too small for me, so I added another 20%.


Useful link: Jekyll (Liquid) array filters


Multilingual sites


Jekyll itself does not support internationalization. So you have to implement it yourself. The easiest method is to simply isolate materials by category (for example, materials in Russian ).


You can also use a subdomain for each language. But in this case for 2 languages ​​there will be exactly 2 sites. And 10 sites for 10 languages, which is not always convenient.


One Manga Day in Polish


On the One Manga Day website, each language has its own folder. The idea is simple. For example, there is an English page cat/page.html . If this page has a Russian version, then the URL will be ru/cat/page.html . If there are few pages, you can create them all manually. But if there are many pages, it is necessary to check the availability of the page in one or another language. However, in Jekyll there are no file functions at all for the security of GitHub servers.


To check the existence of files, you can use my _includes / CHECKEXISTS.html . There all sites and posts sites just go over:


 {% assign curUrlExists = false %} {% assign curFUrl = curUrl | remove: ".html" %} {% for curFile in site.pages %} {% assign cFile = curFile.url | remove: ".html" %} {% if cFile == curFUrl %} {% assign curUrlExists = true %} {% endif %} {% endfor %} {% for curFile in site.posts %} {% assign cFile = curFile.url | remove: ".html" %} {% if cFile == curFUrl %} {% assign curUrlExists = true %} {% endif %} {% endfor %} 

If the file exists in the language, you can add a link to it on your page.


Commenting system


The first solution that comes to mind is Disqus or a similar AJAX-based comment system. A small JS code is simply embedded into the page and everything works well.


One Manga Day Disqus comments


But personally, I liked Staticman much more - a comment system based on pull-requests to the site repository. So the comments are also a jekyll collection!


Code to display comments on the page :


 {% capture post_slug %}{{ page.url | slugify }}{% endcapture %} {% if site.data.comments[post_slug] %} {% assign comments = site.data.comments[post_slug] | sort %} {% for comment in comments %} {% assign email = comment[1].email %} {% assign name = comment[1].name %} {% assign url = comment[1].url %} {% assign date = comment[1].date %} {% assign message = comment[1].message %} {% include _post-comment.html index=forloop.index email=email name=name url=url date=date message=message %} {% endfor %} {% endif %} 

The form for posting a comment is also quite simple:


Comment Posting Form


Moderation of comments is implemented through confirmation of the pull request. You can also simply ask Staticman to put comments directly, bypassing the confirmation of the request. After accepting the request, the entire site will be regenerated and a comment will appear on the site.


Conclusion


Jekyll is a very cool thing for small sites and blogs. Just try it, you will love it!


Jekyll logo


useful links




')

Source: https://habr.com/ru/post/336266/


All Articles