A new Riff-radio.org site with a static approach.

creating-a-theme.rst 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. .. title: Creating a Theme
  2. .. slug: creating-a-theme
  3. .. date: 2015-05-28 18:46:48 UTC
  4. .. tags:
  5. .. category:
  6. .. link:
  7. .. description:
  8. .. type: text
  9. Nikola is a static site and blog generator. So is Jekyll. While I like what we have done with Nikola,
  10. I do admit that Jekyll (and others!) have many more, and nicer themes than Nikola does.
  11. This document is an attempt at making it easier for 3rd parties (that means *you* people! ;-) to
  12. create themes. Since I **suck** at designing websites, I asked for opinions on themes to port,
  13. and got some feedback. Since this is **Not So Hard™**, I will try to make time to port a few
  14. and see what happens.
  15. If you are looking for a reference, check out :doc:`Theming reference <theming>` and `Template variables <https://getnikola.com/template-variables.html>`_.
  16. Today’s theme is `Lanyon <https://github.com/poole/lanyon>`__ which is written by `@mdo <https://twitter.com/mdo>`__
  17. and released under a MIT license, which is liberal enough.
  18. So, let’s get started.
  19. Checking It Out
  20. ---------------
  21. The first step in porting a theme is making the original theme work. Lanyon is awesome in that its
  22. `GitHub project <https://github.com/poole/lanyon>`__ is a full site!
  23. So::
  24. # Get jekyll
  25. sudo apt-get install jekyll
  26. # Get Lanyon
  27. git clone git@github.com:poole/lanyon.git
  28. # Build it
  29. cd lanyon && jekyll build
  30. # Look at it
  31. jekyll serve & google-chrome http://localhost:4000
  32. If you **do not want to install Jekyll**, you can also see it in action at https://lanyon.getpoole.com/
  33. Some things jump to my mind:
  34. 1. This is one fine looking theme
  35. 2. Very clear and readable
  36. 3. Nice hidden navigation-thingy
  37. Also, from looking at `the project’s README <https://github.com/poole/lanyon/blob/master/README.md>`__
  38. it supports some nice configuration options:
  39. 1. Color schemes
  40. 2. Reverse layout
  41. 3. Sidebar overlay instead of push
  42. 4. Open the sidebar by default, or on a per-page basis by using its metadata
  43. Let’s try to make all those nice things survive the porting.
  44. Starting From Somewhere
  45. -----------------------
  46. Nikola has a nice, clean, base theme from which you can start when writing your own theme.
  47. Why start from that instead of from a clean slate? Because theme inheritance is going to save you a ton of work,
  48. that’s why. If you start from scratch you won’t be able to build **anything** until you have a bunch of
  49. templates written. Starting from base, you just need to hack on the things you **need** to change.
  50. First, we create a site with some content in it. We’ll use the ``nikola init`` wizard (with the ``--demo`` option) for that::
  51. $ nikola init --demo lanyon-port
  52. Creating Nikola Site
  53. ====================
  54. This is Nikola v7.8.0. We will now ask you a few easy questions about your new site.
  55. If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter.
  56. --- Questions about the site ---
  57. Site title [My Nikola Site]:
  58. Site author [Nikola Tesla]:
  59. Site author's e-mail [n.tesla@example.com]:
  60. Site description [This is a demo site for Nikola.]:
  61. Site URL [https://example.com/]:
  62. --- Questions about languages and locales ---
  63. We will now ask you to provide the list of languages you want to use.
  64. Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default.
  65. Type '?' (a question mark, sans quotes) to list available languages.
  66. Language(s) to use [en]:
  67. Please choose the correct time zone for your blog. Nikola uses the tz database.
  68. You can find your time zone here:
  69. https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  70. Time zone [UTC]:
  71. Current time in UTC: 16:02:07
  72. Use this time zone? [Y/n]
  73. --- Questions about comments ---
  74. You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank.
  75. Comment system:
  76. That's it, Nikola is now configured. Make sure to edit conf.py to your liking.
  77. If you are looking for themes and addons, check out https://themes.getnikola.com/ and https://plugins.getnikola.com/.
  78. Have fun!
  79. [2015-05-28T16:02:08Z] INFO: init: A new site with example data has been created at lanyon-port.
  80. [2015-05-28T16:02:08Z] INFO: init: See README.txt in that folder for more information.
  81. Then, we create an empty theme inheriting from base. This theme will use Mako templates. If you prefer Jinja2,
  82. then you should use ``base-jinja`` as a parent and ``jinja`` as engine instead::
  83. $ cd lanyon-port/
  84. $ nikola theme -n lanyon --parent base --engine mako
  85. Edit ``conf.py`` and set ``THEME = 'lanyon'``. Also set ``USE_BUNDLES = False`` (just do it for now, we’ll get to bundles later).
  86. Also, if you intend to publish your theme on the Index, or want to use it with older versions (v7.8.5 or older), use the ``--legacy-meta`` option for ``nikola theme -n``.
  87. You can now build that site using ``nikola build`` and it will look like this:
  88. .. figure:: https://getnikola.com/images/lanyon-0.thumbnail.png
  89. :target: https://getnikola.com/images/lanyon-0.png
  90. This is just the base theme.
  91. Basic CSS
  92. ---------
  93. The next step is to know exactly how Lanyon’s pages work. To do this, we read its HTML.
  94. First let’s look at the head element:
  95. .. code:: html
  96. <!DOCTYPE html>
  97. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
  98. <head>
  99. <link href="https://gmpg.org/xfn/11" rel="profile">
  100. <meta http-equiv="content-type" content="text/html; charset=utf-8">
  101. <!-- Enable responsiveness on mobile devices-->
  102. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
  103. <title>
  104. Lanyon &middot; A Jekyll theme
  105. </title>
  106. <!-- CSS -->
  107. <link rel="stylesheet" href="/public/css/poole.css">
  108. <link rel="stylesheet" href="/public/css/syntax.css">
  109. <link rel="stylesheet" href="/public/css/lanyon.css">
  110. <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700|PT+Sans:400">
  111. <!-- Icons -->
  112. <link rel="apple-touch-icon-precomposed" sizes="144x144" href="/public/apple-touch-icon-144-precomposed.thumbnail.png">
  113. <link rel="shortcut icon" href="/public/favicon.ico">
  114. <!-- RSS -->
  115. <link rel="alternate" type="application/rss+xml" title="RSS" href="/atom.xml">
  116. <!-- Google Analytics -->
  117. [...]
  118. </head>
  119. The interesting part there is that it loads a few CSS files. If you check the source of your Nikola site, you will
  120. see something fairly similar:
  121. .. code:: html
  122. <!DOCTYPE html>
  123. <html prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article# " vocab="http://ogp.me/ns" lang="en">
  124. <head>
  125. <meta charset="utf-8">
  126. <meta name="description" content="This is a demo site for Nikola.">
  127. <meta name="viewport" content="width=device-width">
  128. <title>My Nikola Site | My Nikola Site</title>
  129. <link href="assets/css/rst_base.css" rel="stylesheet" type="text/css">
  130. <link href="assets/css/code.css" rel="stylesheet" type="text/css">
  131. <link href="assets/css/theme.css" rel="stylesheet" type="text/css">
  132. <link rel="alternate" type="application/rss+xml" title="RSS" href="rss.xml">
  133. <link rel="canonical" href="https://example.com/index.html">
  134. <!--[if lt IE 9]><script src="assets/js/html5.js"></script><![endif]--><link rel="prefetch" href="posts/welcome-to-nikola.html" type="text/html">
  135. </head>
  136. Luckily, since this is all under a very liberal license, we can just copy these CSS files into
  137. Nikola, adapting the paths a little so that they follow our conventions::
  138. $ mkdir -p themes/lanyon/assets/css
  139. $ cp ../lanyon/public/css/poole.css themes/lanyon/assets/css/
  140. $ cp ../lanyon/public/css/lanyon.css themes/lanyon/assets/css/
  141. Notice I am *not* copying ``syntax.css``? That’s because Nikola handles that styles for syntax highlighting
  142. in a particular way, using a setting called ``CODE_COLOR_SCHEME`` where you can configure
  143. what color scheme the syntax highlighter uses. You can use your own ``assets/css/code.css`` if you
  144. don’t like the provided ones.
  145. Nikola **requires** ``assets/css/rst_base.css`` and ``assets/css/code.css`` to function properly.
  146. We will also add themes for Jupyter (``assets/css/ipython.min.css``
  147. and ``assets/css/nikola_ipython.css``) into the template; note that they are
  148. activated only if you configured your ``POSTS``/``PAGES`` with ipynb support.
  149. There’s also ``assets/css/nikola_rst.css``, which adds Bootstrap 3-style reST notes etc.
  150. But how do I tell **our** lanyon theme to use those CSS files instead of whatever it’s using now?
  151. By giving our theme its own base_helper.tmpl.
  152. That file is a **template** used to generate parts of the pages. It’s large and
  153. complicated but we don’t need to change a lot of it. First, make a copy in your
  154. theme (note this command requires setting your ``THEME`` in ``conf.py`` to
  155. ``lanyon``)::
  156. $ nikola theme -c base_helper.tmpl
  157. The part we want to change is this:
  158. .. code:: html+mako
  159. <%def name="html_stylesheets()">
  160. %if use_bundles:
  161. %if use_cdn:
  162. <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
  163. %else:
  164. <link href="/assets/css/all-nocdn.css" rel="stylesheet" type="text/css">
  165. %endif
  166. %else:
  167. <link href="/assets/css/rst_base.css" rel="stylesheet" type="text/css">
  168. <link href="/assets/css/nikola_rst.css" rel="stylesheet" type="text/css">
  169. <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
  170. <link href="/assets/css/theme.css" rel="stylesheet" type="text/css">
  171. %if has_custom_css:
  172. <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
  173. %endif
  174. %endif
  175. % if needs_ipython_css:
  176. <link href="/assets/css/ipython.min.css" rel="stylesheet" type="text/css">
  177. <link href="/assets/css/nikola_ipython.css" rel="stylesheet" type="text/css">
  178. % endif
  179. </%def>
  180. And we will change it so it uses the lanyon styles instead of theme.css (again, ignore the bundles for now!):
  181. .. code:: html+mako
  182. <%def name="html_stylesheets()">
  183. %if use_bundles:
  184. <link href="/assets/css/all.css" rel="stylesheet" type="text/css">
  185. %else:
  186. <link href="/assets/css/rst_base.css" rel="stylesheet" type="text/css">
  187. <link href="/assets/css/nikola_rst.css" rel="stylesheet" type="text/css">
  188. <link href="/assets/css/poole.css" rel="stylesheet" type="text/css">
  189. <link href="/assets/css/lanyon.css" rel="stylesheet" type="text/css">
  190. <link href="/assets/css/code.css" rel="stylesheet" type="text/css">
  191. %if has_custom_css:
  192. <link href="/assets/css/custom.css" rel="stylesheet" type="text/css">
  193. %endif
  194. %endif
  195. % if needs_ipython_css:
  196. <link href="/assets/css/ipython.min.css" rel="stylesheet" type="text/css">
  197. <link href="/assets/css/nikola_ipython.css" rel="stylesheet" type="text/css">
  198. % endif
  199. <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Serif:400,400italic,700|PT+Sans:400">
  200. </%def>
  201. .. figure:: https://getnikola.com/images/lanyon-1.thumbnail.png
  202. :target: https://getnikola.com/images/lanyon-1.png
  203. You may say this looks like crap. Don’t worry, we are just starting :-)
  204. Page Layout
  205. -----------
  206. This is trickier but should be no problem for people with a basic understanding of HTML and a desire to make a theme!
  207. Lanyon’s content is split in two parts: a sidebar and the rest. The sidebar looks like this (shortened for comprehension):
  208. .. code:: html
  209. <body>
  210. <!-- Target for toggling the sidebar `.sidebar-checkbox` is for regular
  211. styles, `#sidebar-checkbox` for behavior. -->
  212. <input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">
  213. <!-- Toggleable sidebar -->
  214. <div class="sidebar" id="sidebar">
  215. <div class="sidebar-item">
  216. <p>A reserved <a href="https://jekyllrb.com" target="_blank">Jekyll</a> theme that places the utmost gravity on content with a hidden drawer. Made by <a href="https://twitter.com/mdo" target="_blank">@mdo</a>.</p>
  217. </div>
  218. <nav class="sidebar-nav">
  219. <a class="sidebar-nav-item active" href="/">Home</a>
  220. <a class="sidebar-nav-item" href="/about/">About</a>
  221. [...]
  222. </nav>
  223. </div>
  224. So, a plain body, with an input element that controls the sidebar, a div which is the sidebar itself.
  225. Inside that, div.sidebar-item for items, and a nav with "navigational links". This is followed by the "masthead" and
  226. the content itself, which we will look at in a bit.
  227. If we look for the equivalent code in Nikola’s side, we see this:
  228. .. code:: html
  229. <body>
  230. <a href="#content" class="sr-only sr-only-focusable">Skip to main content</a>
  231. <div id="container">
  232. <header id="header" role="banner">
  233. <h1 id="brand"><a href="https://example.com/" title="My Nikola Site" rel="home"> <span id="blog-title">My Nikola Site</span> </a></h1>
  234. <nav id="menu" role="navigation"><ul>
  235. <li><a href="../archive.html">Archive</a></li>
  236. <li><a href="../categories/index.html">Tags</a></li>
  237. <li><a href="../rss.xml">RSS feed</a></li>
  238. So Nikola has the "masthead" above the nav element, and uses list elements in nav instead of bare links.
  239. Not all that different is it?
  240. Let’s make it lanyon-like! We will need 2 more templates: `base.tmpl <https://github.com/getnikola/nikola/blob/master/nikola/data/themes/base/templates/base.tmpl>`__ and `base_header.tmpl <https://github.com/getnikola/nikola/blob/master/nikola/data/themes/base/templates/base_header.tmpl>`__. Get them and put them in your ``themes/lanyon/templates`` folder.
  241. Let’s look at ``base.tmpl`` first. It’s short and nice, it looks like a webpage without
  242. all the interesting stuff:
  243. .. code:: html+mako
  244. ## -*- coding: utf-8 -*-
  245. <%namespace name="base" file="base_helper.tmpl" import="*"/>
  246. <%namespace name="header" file="base_header.tmpl" import="*"/>
  247. <%namespace name="footer" file="base_footer.tmpl" import="*"/>
  248. ${set_locale(lang)}
  249. ${base.html_headstart()}
  250. <%block name="extra_head">
  251. ### Leave this block alone.
  252. </%block>
  253. ${template_hooks['extra_head']()}
  254. </head>
  255. <body>
  256. <a href="#content" class="sr-only sr-only-focusable">${messages("Skip to main content")}</a>
  257. <div id="container">
  258. ${header.html_header()}
  259. <main id="content" role="main">
  260. <%block name="content"></%block>
  261. </main>
  262. ${footer.html_footer()}
  263. </div>
  264. ${body_end}
  265. ${template_hooks['body_end']()}
  266. ${base.late_load_js()}
  267. </body>
  268. </html>
  269. That link which says "Skip to main content" is very important for accessibility, so we will leave it in
  270. place. But below, you can see how it creates the "container" div we see in the Nikola page, and the content is
  271. created by ``html_header()`` which is defined in ``base_header.tmpl`` The actual ``nav`` element is done
  272. by the ``html_navigation_links`` function out of the ``NAVIGATION_LINKS`` and ``NAVIGATION_ALT_LINKS`` options. (Let's put the alt links after regular ones; Bootstrap puts it on the right side, for example.)
  273. So, first, lets change that base template to be more lanyon-like:
  274. .. code:: html+mako
  275. ## -*- coding: utf-8 -*-
  276. <%namespace name="base" file="base_helper.tmpl" import="*"/>
  277. <%namespace name="header" file="base_header.tmpl" import="*"/>
  278. <%namespace name="footer" file="base_footer.tmpl" import="*"/>
  279. ${set_locale(lang)}
  280. ${base.html_headstart()}
  281. <%block name="extra_head">
  282. ### Leave this block alone.
  283. </%block>
  284. ${template_hooks['extra_head']()}
  285. </head>
  286. <body>
  287. <a href="#content" class="sr-only sr-only-focusable">${messages("Skip to main content")}</a>
  288. <!-- Target for toggling the sidebar `.sidebar-checkbox` is for regular
  289. styles, `#sidebar-checkbox` for behavior. -->
  290. <input type="checkbox" class="sidebar-checkbox" id="sidebar-checkbox">
  291. <!-- Toggleable sidebar -->
  292. <div class="sidebar" id="sidebar">
  293. <div class="sidebar-item">
  294. <p>A reserved <a href="https://getnikola.com" target="_blank" rel="noopener">Nikola</a> theme that places the utmost gravity on content with a hidden drawer. Made by <a href="https://twitter.com/mdo" target="_blank" rel="noopener">@mdo</a> for Jekyll,
  295. ported to Nikola by <a href="https://twitter.com/ralsina" target="_blank">@ralsina</a>.</p>
  296. </div>
  297. ${header.html_navigation_links()}
  298. </div>
  299. <main id="content" role="main">
  300. <%block name="content"></%block>
  301. </main>
  302. ${footer.html_footer()}
  303. ${body_end}
  304. ${template_hooks['body_end']()}
  305. ${base.late_load_js()}
  306. </body>
  307. </html>
  308. .. figure:: https://getnikola.com/images/lanyon-2.thumbnail.png
  309. :target: https://getnikola.com/images/lanyon-2.png
  310. And that’s after I exposed the sidebar by clicking on an invisible widget!
  311. One problem, which causes that yellow color in the sidebar is a CSS conflict.
  312. We are loading ``rst_base.css`` which specifies
  313. the background color of ``div.sidebar`` which is more specific than
  314. ``lanyon.css``, which specifies for ``.sidebar`` alone.
  315. There are many ways to fix this, I chose to change lanyon.css to also use div.sidebar:
  316. .. code:: css
  317. div.sidebar,.sidebar {
  318. position: fixed;
  319. top: 0;
  320. bottom: 0;
  321. left: -14rem;
  322. width: 14rem;
  323. [...]
  324. This is annoying but it will happen when you just grab CSS from different places. The "Inspect Element"
  325. feature of your web browser is your best friend for these situations.
  326. Another problem is that the contents of the nav element are wrong. They are not bare links. We will fix that in
  327. ``base_header.html``, like this:
  328. .. code:: html+mako
  329. <%def name="html_navigation_links()">
  330. <nav id="menu" role="navigation" class="sidebar-nav">
  331. %for url, text in navigation_links[lang]:
  332. <a class="sidebar-nav-item" href="${url}">${text}</a>
  333. %endfor
  334. ${template_hooks['menu']()}
  335. %for url, text in navigation_alt_links[lang]:
  336. <a class="sidebar-nav-item" href="${url}">${text}</a>
  337. %endfor
  338. ${template_hooks['menu_alt']()}
  339. </nav>
  340. </%def>
  341. **Note: this means this theme will not support submenus in navigation. If you want that, I’ll happily take a patch.**
  342. .. figure:: https://getnikola.com/images/lanyon-3.thumbnail.png
  343. :target: https://getnikola.com/images/lanyon-3.png
  344. Starting to see a resemblance?
  345. Now let’s look at the content. In Lanyon, this is how the "main" content looks:
  346. .. code:: html
  347. <!-- Wrap is the content to shift when toggling the sidebar. We wrap the
  348. content to avoid any CSS collisions with our real content. -->
  349. <div class="wrap">
  350. <div class="masthead">
  351. <div class="container">
  352. <h3 class="masthead-title">
  353. <a href="/" title="Home">Lanyon</a>
  354. <small>A Jekyll theme</small>
  355. </h3>
  356. </div>
  357. </div>
  358. <div class="container content">
  359. <div class="post">
  360. <h1 class="post-title">Introducing Lanyon</h1>
  361. <span class="post-date">02 Jan 2014</span>
  362. <p>Lanyon is an unassuming <a href="https://jekyllrb.com">Jekyll</a> theme [...]
  363. </div>
  364. </div>
  365. </div>
  366. <label for="sidebar-checkbox" class="sidebar-toggle"></label>
  367. </body>
  368. </html>
  369. Everything inside the "container content" div is… the content. The rest is a masthead with the site title
  370. and at the bottom a label for the sidebar toggle. Easy to do in ``base.tmpl``
  371. (only showing the relevant part):
  372. .. code:: html+mako
  373. <!-- Wrap is the content to shift when toggling the sidebar. We wrap the
  374. content to avoid any CSS collisions with our real content. -->
  375. <div class="wrap">
  376. <div class="masthead">
  377. <div class="container">
  378. <h3 class="masthead-title">
  379. <a href="/" title="Home">Lanyon</a>
  380. <small>A Jekyll theme</small>
  381. </h3>
  382. </div>
  383. </div>
  384. <div class="container content" id="content">
  385. <%block name="content"></%block>
  386. </div>
  387. </div>
  388. <label for="sidebar-checkbox" class="sidebar-toggle"></label>
  389. ${footer.html_footer()}
  390. ${body_end}
  391. ${template_hooks['body_end']()}
  392. ${base.late_load_js()}
  393. </body>
  394. </html>
  395. .. figure:: https://getnikola.com/images/lanyon-4.thumbnail.png
  396. :target: https://getnikola.com/images/lanyon-4.png
  397. Getting there!
  398. The sidebar looks bad because of yet more CSS conflicts with ``rst_base.css``. By
  399. adding some extra styling in ``lanyon.css``, it will look better.
  400. .. code:: css
  401. /* Style and "hide" the sidebar */
  402. div.sidebar, .sidebar {
  403. position: fixed;
  404. top: 0;
  405. bottom: 0;
  406. left: -14rem;
  407. width: 14rem;
  408. visibility: hidden;
  409. overflow-y: auto;
  410. padding: 0;
  411. margin: 0;
  412. border: none;
  413. font-family: "PT Sans", Helvetica, Arial, sans-serif;
  414. font-size: .875rem; /* 15px */
  415. color: rgba(255,255,255,.6);
  416. background-color: #202020;
  417. -webkit-transition: all .3s ease-in-out;
  418. transition: all .3s ease-in-out;
  419. }
  420. Also, the accessibility link on top is visible when it should not. That’s
  421. because we removed ``theme.css`` from the base theme, and with it, we lost a
  422. couple of classes. We can add them in ``lanyon.css``, along with others used by other
  423. pieces of the site:
  424. .. code:: css
  425. .sr-only {
  426. position: absolute;
  427. width: 1px;
  428. height: 1px;
  429. padding: 0;
  430. margin: -1px;
  431. overflow: hidden;
  432. clip: rect(0, 0, 0, 0);
  433. border: 0;
  434. }
  435. .sr-only-focusable:active,
  436. .sr-only-focusable:focus {
  437. position: static;
  438. width: auto;
  439. height: auto;
  440. margin: 0;
  441. overflow: visible;
  442. clip: auto;
  443. }
  444. .breadcrumb {
  445. padding: 8px 15px;
  446. margin-bottom: 20px;
  447. list-style: none;
  448. }
  449. .breadcrumb > li {
  450. display: inline-block;
  451. margin-right: 0;
  452. margin-left: 0;
  453. }
  454. .breadcrumb > li:after {
  455. content: ' / ';
  456. color: #888;
  457. }
  458. .breadcrumb > li:last-of-type:after {
  459. content: '';
  460. margin-left: 0;
  461. }
  462. .thumbnails > li {
  463. display: inline-block;
  464. margin-right: 10px;
  465. }
  466. .thumbnails > li:last-of-type {
  467. margin-right: 0;
  468. }
  469. .. figure:: https://getnikola.com/images/lanyon-5.thumbnail.png
  470. :target: https://getnikola.com/images/lanyon-5.png
  471. Little by little, things look better.
  472. One clear problem is that the title "Lanyon · A Jekyll theme" is set in the
  473. theme itself. We don’t do that sort of thing in Nikola, we have settings for
  474. that. So, let’s use them. There is a ``html_site_title`` function in
  475. ``base_helper.tmpl`` which is just the thing. So we change base.tmpl to use it:
  476. .. code:: html+mako
  477. <div class="wrap">
  478. <div class="masthead">
  479. <div class="container">
  480. ${header.html_site_title()}
  481. </div>
  482. </div>
  483. That’s a ``<h1>`` instead of a ``<h3>`` like Lanyon does, but hey, it’s the
  484. right thing to do. If you want to go with an ``<h3>``, just
  485. change ``html_site_title`` itself.
  486. And now we more or less have the correct page layout and styles. Except for a
  487. rather large thing…
  488. Typography
  489. ----------
  490. You can see in the previous screenshot that text still looks quite different in our port: Serif versus Sans-Serif
  491. content, and the titles have different colors!
  492. Let’s start with the titles. Here’s how they look in Lanyon:
  493. .. code:: html
  494. <h3 class="masthead-title">
  495. <a href="/" title="Home">Lanyon</a>
  496. <small>A Jekyll theme</small>
  497. </h3>
  498. Versus our port:
  499. .. code:: html
  500. <h1 id="brand"><a href="https://example.com/" title="My Nikola Site" rel="home">
  501. So, it looks like we will have to fix ``html_site_title`` after all:
  502. .. code:: html+mako
  503. <%def name="html_site_title()">
  504. <h3 id="brand" class="masthead-title">
  505. <a href="${_link("root", None, lang)}" title="${blog_title}" rel="home">${blog_title}</a>
  506. </h3>
  507. </%def>
  508. As for the actual content, that’s not in any of the templates we have seen so far. The page you see is an
  509. "index.tmpl" page, which means it’s a list of blog posts shown one below the
  510. other. Obviously it’s not doing
  511. things in the way the Lanyon CSS expects it to. Here’s the original, which you
  512. can find in Nikola’s source
  513. code:
  514. .. code:: html+mako
  515. ## -*- coding: utf-8 -*-
  516. <%namespace name="helper" file="index_helper.tmpl"/>
  517. <%namespace name="comments" file="comments_helper.tmpl"/>
  518. <%inherit file="base.tmpl"/>
  519. <%block name="extra_head">
  520. ${parent.extra_head()}
  521. % if posts and (permalink == '/' or permalink == '/' + index_file):
  522. <link rel="prefetch" href="${posts[0].permalink()}" type="text/html">
  523. % endif
  524. </%block>
  525. <%block name="content">
  526. <%block name="content_header"></%block>
  527. <div class="postindex">
  528. % for post in posts:
  529. <article class="h-entry post-${post.meta('type')}">
  530. <header>
  531. <h1 class="p-name entry-title"><a href="${post.permalink()}" class="u-url">${post.title()|h}</a></h1>
  532. <div class="metadata">
  533. <p class="byline author vcard"><span class="byline-name fn">${post.author()}</span></p>
  534. <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="published dt-published" datetime="${post.date.isoformat()}" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></a></p>
  535. % if not post.meta('nocomments') and site_has_comments:
  536. <p class="commentline">${comments.comment_link(post.permalink(), post._base_path)}
  537. % endif
  538. </div>
  539. </header>
  540. %if index_teasers:
  541. <div class="p-summary entry-summary">
  542. ${post.text(teaser_only=True)}
  543. %else:
  544. <div class="e-content entry-content">
  545. ${post.text(teaser_only=False)}
  546. %endif
  547. </div>
  548. </article>
  549. % endfor
  550. </div>
  551. ${helper.html_pager()}
  552. ${comments.comment_link_script()}
  553. ${helper.mathjax_script(posts)}
  554. </%block>
  555. And this is how it looks after I played with it for a while, making it generate code that looks closer to
  556. the Lanyon original:
  557. .. code:: html+mako
  558. <%block name="content">
  559. <%block name="content_header"></%block>
  560. <div class="posts">
  561. % for post in posts:
  562. <article class="post h-entry post-${post.meta('type')}">
  563. <header>
  564. <h1 class="post-title p-name"><a href="${post.permalink()}" class="u-url">${post.title()|h}</a></h1>
  565. <div class="metadata">
  566. <p class="byline author vcard"><span class="byline-name fn">${post.author()}</span></p>
  567. <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="post-date published dt-published" datetime="${post.date.isoformat()}" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></a></p>
  568. % if not post.meta('nocomments') and site_has_comments:
  569. <p class="commentline">${comments.comment_link(post.permalink(), post._base_path)}
  570. % endif
  571. </div>
  572. </header>
  573. %if index_teasers:
  574. <div class="p-summary entry-summary">
  575. ${post.text(teaser_only=True)}
  576. %else:
  577. <div class="e-content entry-content">
  578. ${post.text(teaser_only=False)}
  579. %endif
  580. </div>
  581. </article>
  582. % endfor
  583. </div>
  584. ${helper.html_pager()}
  585. ${comments.comment_link_script()}
  586. ${helper.mathjax_script(posts)}
  587. </%block>
  588. With these changes, it looks… similar?
  589. .. figure:: https://getnikola.com/images/lanyon-6.thumbnail.png
  590. :target: https://getnikola.com/images/lanyon-6.png
  591. It does!
  592. Similar changes (basically adding class names to elements) needed to be done in ``post_header.tmpl``:
  593. .. code:: html+mako
  594. <%def name="html_post_header()">
  595. <header>
  596. ${html_title()}
  597. <div class="metadata">
  598. <p class="byline author vcard"><span class="byline-name fn">${post.author()}</span></p>
  599. <p class="dateline"><a href="${post.permalink()}" rel="bookmark"><time class="post-date published dt-published" datetime="${post.date.isoformat()}" itemprop="datePublished" title="${post.formatted_date(date_format)}">${post.formatted_date(date_format)}</time></a></p>
  600. % if not post.meta('nocomments') and site_has_comments:
  601. <p class="commentline">${comments.comment_link(post.permalink(), post._base_path)}
  602. % endif
  603. %if post.description():
  604. <meta name="description" itemprop="description" content="${post.description()}">
  605. %endif
  606. </div>
  607. ${html_translations(post)}
  608. </header>
  609. </%def>
  610. Customization
  611. -------------
  612. The original Lanyon theme supports some personalization options. It suggests you do them by tweaking the templates, and
  613. you *can* also do that in the Nikola port. But we prefer to use options for that, so that you can get a later, better
  614. version of the theme and it will still "just work".
  615. Let’s see the color schemes first. They apply easily, just tweak your ``body`` element like this:
  616. .. code:: html
  617. <body class="theme-base-08">
  618. ...
  619. </body>
  620. We can tweak ``base.tmpl`` to do just that:
  621. .. code:: html+mako
  622. % if lanyon_subtheme:
  623. <body class="${lanyon_subtheme}">
  624. %else:
  625. <body>
  626. %endif
  627. And then we can put the options in conf.py’s ``GLOBAL_CONTEXT``:
  628. .. code:: python
  629. GLOBAL_CONTEXT = {
  630. "lanyon_subtheme": "theme-base-08"
  631. }
  632. .. figure:: https://getnikola.com/images/lanyon-7.thumbnail.png
  633. :target: https://getnikola.com/images/lanyon-7.png
  634. Look at it, all themed up.
  635. Doing the same for layout-reverse, sidebar-overlay and the rest is left as an exercise for the reader.
  636. Bundles
  637. -------
  638. If the ``USE_BUNDLES`` option set to True,
  639. Nikola can put several CSS or JS files together in a larger file, which can
  640. makes site load faster for some deployments. To do this, your theme needs
  641. a ``bundles`` file. The file format is a modified
  642. `config <https://docs.python.org/3/library/configparser.html>`_ file with no
  643. defined section; the basic syntax is::
  644. outputfile1.js=
  645. thing1.js,
  646. thing2.js,
  647. ...
  648. outputfile2.css=
  649. thing1.css,
  650. thing2.css,
  651. ...
  652. For the Lanyon theme, it should look like this::
  653. assets/css/all.css=
  654. rst_base.css,
  655. nikola_rst.css,
  656. code.css,
  657. poole.css,
  658. lanyon.css,
  659. custom.css,
  660. **Note:** trailing commas are optional
  661. **Note:** Some themes also support the ``USE_CDN`` option meaning that in some cases it will load one bundle with all CSS and in other will load some CSS files
  662. from a CDN and others from a bundle. This is complicated and probably not worth the effort.
  663. The End
  664. -------
  665. And that’s it, that’s a whole theme. Eventually, once people start using it, they will notice small broken details, which will need handling one at a time.
  666. This theme should be available in https://themes.getnikola.com/v7/lanyon/ and you can see it in action at https://themes.getnikola.com/v7/lanyon/demo/ .
  667. What if you want to extend other parts of the theme? Check out the :doc:`Theming reference <theming>`. You can also contribute your improvements to the `nikola-themes <https://github.com/getnikola/nikola>` repository on GitHub.