Linkita

A clean and elegant blog theme for Zola. Linkita is based on Kita and Hugo-Paper and is multilingual and SEO friendly.

The source code is available on Codeberg and mirrored on GitHub. A live preview can be viewed in English, Bulgarian, and Esperanto. Screenshots are provided for both Light mode and Dark mode. For discussion, you can join the Matrix chat room.

Features

Kita features

Linkita features

Installing

  1. Add this theme as a submodule:
git submodule add https://codeberg.org/salif/linkita.git themes/linkita

Alternatively, clone the repository: git clone https://codeberg.org/salif/linkita.git themes/linkita.

  1. Set linkita as your theme in your config.toml file.
theme = "linkita"
  1. Optionally, you can switch from the linkita branch to the latest release:
cd themes/linkita
npm run switch-to-latest

Updating

git submodule update --merge --remote themes/linkita
# cd themes/linkita
# npm run switch-to-latest

Usage

TOML frontmatter

+++
title = ""
description = ""
# date = 
# updated = 
[taxonomies]
categories = []
tags = []
authors = []
[extra]
# comment = true
# math = true
# mermaid = true
# page_info = []
[extra.cover]
# image = ""
# alt = ""
+++

YAML frontmatter

---
title: ""
description: ""
date: 
# updated: 
taxonomies:
  categories:
  tags:
  authors:
extra:
  comment: false
  math: false
  mermaid: false
  cover:
    image: ""
    alt: ""
---

Open Graph frontmatter options

[extra.open_graph]
# MIME type of the cover image. e.g. `image/jpeg`, `image/gif`, `image/png`
cover_type = ""
# Width of the cover image in pixels
cover_width =
# Height of the cover image in pixels
cover_height =
# When the article is out of date after. e.g. `2024-02-29`
expiration_time =
# Describes the tier status for an article. e.g. `free`, `locked`, or `metered`
content_tier = ""
# Defines the location to target for the article. e.g. `["county:COUNTY"]` or `["city:CITY,COUNTY"]`
locations = []
# A high-level section name. e.g. `Technology`
section = ""
# Tag words associated with this article
tags = [""]
# Indicates whether the article is an opinion piece or not. e.g. `true` or `false`
opinion =
# The URL for the audio
audio = ""
# MIME type of the audio. e.g. `audio/vnd.facebook.bridge`, `audio/mpeg`
audio_type = ""
# The URL for the video
video = ""
# MIME type of the video. e.g. `application/x-shockwave-flash`, `video/mp4`
video_type = ""
# Width of the video in pixels
video_width =
# Height of the video in pixels
video_height =
# Set only if different from canonical page URL
url = ""

Sitemap frontmatter options

[extra.sitemap]
# Set only if different from `page.updated`
updated =
# Valid values are `always`, `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `never`
changefreq =
# Valid values range from 0.0 to 1.0. The default priority of a page is 0.5
priority =

Home page profile

Create content/_index.md file in your blog and set extra.profile to your username:

+++
sort_by = "date"
paginate_by = 5
[extra]
profile = "your_username"
+++

Do it for each language in your blog, for example for French, the file name is content/_index.fr.md.

Profiles for authors

You should add extra.profiles.author_username table in your config.toml file for each author. Replace author_username with author's username.

Authors

Option 1: Using page.authors

You don't need to set page.authors in the frontmatter if you are the only author.

Otherwise, set page.authors:

+++
authors = ["author_username", "author2_username"]
+++

Option 2: Using Taxonomies

If you choose this option you should set taxonomies in each post.

Examples:

If the blog is your personal blog:

+++
[taxonomies]
authors = ["your_username"]
+++

If the blog has a team of multiple authors:

+++
[taxonomies]
authors = ["author_username"]
# or:
# authors = ["author_username", "author2_username"]
+++

Archive page

+++
title = "Archive"
template = "archive.html"
[extra]
section = "_index.md"
+++

Inject support

You can easily use inject to add new features to your side without modifying the theme itself.

To use inject, you need to add some HTML files to the templates/injects directory.

The available inject points are: head, header_nav, body_start, body_end, page_start, page_end, footer.

Keyboard shortcuts

ActionShortcut
HomeAlt+!
SearchAlt+/
Toggle menuAlt++
Toggle dark modeAlt+$
Go to prev pageAlt+,
Go to next pageAlt+.
Table of contentsAlt+=
Skip to footerAlt+_
Skip to mainAlt+-

Configuring

Copy and paste the examples into your config.toml file and comment out the options you don't use instead of setting empty values.

keytype
default_languagestring
authorstring
titlestring
descriptionstring
generate_feedsboolean
feed_filenamesarray of strings
build_search_indexboolean
extratable

Taxonomies with translated names are tags, categories, and authors.

# The default language
default_language = "en"

# The default author for pages
author = "your_username"

# The site title
title = ""

# The site description
description = ""

# Automatically generated feed 
generate_feeds = true

# The filenames to use for the feeds
feed_filenames = ["atom.xml"] # or ["rss.xml"]

# Enable search
build_search_index = true
[[taxonomies]]
name = "categories"
feed = true
paginate_by = 5

[[taxonomies]]
name = "tags"
feed = true
paginate_by = 5

[[taxonomies]]
name = "authors"
feed = true
paginate_by = 5

Add more languages ​​by replacing fr from the example with the language code.

[languages.fr]
title = "Site title in French"
description = "Site description in French"
generate_feeds = true
feed_filenames = ["atom.xml"] # or ["rss.xml"]

[[languages.fr.taxonomies]]
name = "authors"
feed = true
paginate_by = 5
keytype
extra.mathboolean
extra.mermaidboolean
extra.commentboolean
extra.title_separatorstring
extra.header_menu_namestring
extra.header_buttonsarray of strings
extra.page_infoarray of strings
extra.disable_default_faviconboolean
extra.disable_javascriptboolean

Tables: extra.style, extra.menus, extra.profiles, extra.footer, extra.languages, extra.goatcounter, extra.giscus.

The table below lists valid extra.page_info values. Default value is ["date", "date_updated_on_page", "reading_time", "authors"].

on bothonly on pageonly on paginator
datedate_on_pagedate_on_paginator
date_updateddate_updated_on_pagedate_updated_on_paginator
reading_timereading_time_on_pagereading_time_on_paginator
word_countword_count_on_pageword_count_on_paginator
authorsauthors_on_pageauthors_on_paginator
tagstags_on_pagetags_on_paginator

Default extra.header_buttons value is ["site_title", "theme_button", "search_button", "translations_button"]. You can replace site_title with home_button if you want.

[extra]
# Enable KaTeX math formula support globally
math = false

# Enable Mermaid support globally 
mermaid = false

# Enable comments globally
comment = false

# Title separator
title_separator = " | "

# The top menu. See `extra.menus`
header_menu_name = "menu_name"

# header_buttons = []
# page_info = []
# disable_default_favicon = true
# disable_javascript = false

Style config

keytypedefault value
extra.style.bg_colorstring"#f4f4f5"
extra.style.bg_dark_colorstring"#18181b"
extra.style.header_blurbooleanfalse
extra.style.header_colorstring"#e4e4e7"
extra.style.header_dark_colorstring"#27272a"
[extra.style]
# The custom background color
bg_color = "#f4f4f5"

# The custom background color in dark mode
bg_dark_color = "#18181b"

# Enable header blur
header_blur = false

# The custom header color, only available when `header_blur` is false
header_color = "#e4e4e7"

# The custom header color in dark mode, only available when `header_blur` is false
header_dark_color = "#27272a"
keytype
extra.menus[menu_name].menu[].urlstring
extra.menus[menu_name].menu[].namestring
extra.menus[menu_name].menu[].namestable
extra.menus[menu_name].menu[].names[lang]string
extra.menus[menu_name].menu[].names_i18nstring

$BASE_URL in .url will be automatically replaced with the language specific base url. You can use names_i18n instead of names[lang], see the static/i18n.json file, set names_i18n to a common_ key.

[[extra.menus.menu_name]]
url = "$BASE_URL/projects/"
# name = "Projects"
[extra.menus.menu_name.names]
en = "Projects"
# fr = "Projects in French"

[[extra.menus.menu_name]]
url = "$BASE_URL/archive/"
# name = "Archive"
[extra.menus.menu_name.names]
en = "Archive"
# fr = "Archive in French"

[[extra.menus.menu_name]]
url = "$BASE_URL/tags/"
# name = "Tags"
[extra.menus.menu_name.names]
en = "Tags"
# fr = "Tags in French"

[[extra.menus.menu_name]]
url = "$BASE_URL/about/"
# name = "About"
[extra.menus.menu_name.names]
en = "About"
# fr = "About in French"

Profiles

keytype
extra.profiles[username].avatar_urlstring
extra.profiles[username].avatar_altstring
extra.profiles[username].avatar_invertboolean
extra.profiles[username].namestring
extra.profiles[username].biostring
extra.profiles[username].emailstring
extra.profiles[username].urlstring
extra.profiles[username].languagestable
extra.profiles[username].socialarray of tables
extra.profiles[username].open_graphtable
[extra.profiles.your_username]
# The URL of avatar
avatar_url = "icons/github.svg"

# A description of what is in the avatar
avatar_alt = ""

# Invert avatar color in dark mode
avatar_invert = false

# Profile name for all languages
name = ""

# Profile bio for all languages. Supports Markdown.
bio = ""

# Profile email
# email = ""

# Profile website
# url = ""

Profile translations

keytype
extra.profiles[username].languages[lang].namestring
extra.profiles[username].languages[lang].biostring
extra.profiles[username].languages[lang].urlstring
extra.profiles[username].languages[lang].avatar_altstring
[extra.profiles.your_username.languages.fr]
# Profile name in French
name = ""

# Profile bio in French
bio = ""

Social icons

keytype
extra.profiles[username].social[].namestring
extra.profiles[username].social[].urlstring

The name should be the file name of static/icons/*.svg or the icon name of simpleicons.org. The url supports $BASE_URL.

[[extra.profiles.your_username.social]]
name = "github"
url = "https://github.com/username"

[[extra.profiles.your_username.social]]
name = "bluesky"
url = "https://bsky.app/profile/username"

[[extra.profiles.your_username.social]]
name = "rss"
url = "$BASE_URL/atom.xml"

Open Graph

keytype
extra.profiles[username].open_graph.imagestring
extra.profiles[username].open_graph.image_altstring
extra.profiles[username].open_graph.first_namestring
extra.profiles[username].open_graph.last_namestring
extra.profiles[username].open_graph.usernamestring
extra.profiles[username].open_graph.genderstring
extra.profiles[username].open_graph.fb_app_idstring
extra.profiles[username].open_graph.fb_adminsarray of strings
extra.profiles[username].open_graph.fediverse_creatortable
extra.profiles[username].open_graph.fediverse_creator.handlestring
extra.profiles[username].open_graph.fediverse_creator.domainstring
extra.profiles[username].open_graph.fediverse_creator.urlstring
extra.profiles[username].open_graph.languages[lang]table

See the Open Graph protocol.

[extra.profiles.your_username.open_graph]
# The URL of social image
image = ""

# A description of what is in the social image
image_alt = ""

first_name = "Your first name"
last_name = "Your last name"
username = "Your username"
gender = "female" # or "male"

# Set if you have a Fediverse account. Example for @user@mastodon.social:
[extra.profiles.your_username.open_graph.fediverse_creator]
# Your Fediverse handle
# handle = "user"
# Your Fediverse instance
# domain = "mastodon.social"
# Your Fediverse account URL
# url = ""

# [extra.profiles.your_username.open_graph.languages.fr.image_alt]
# A description in French of what is in the social image
# image_alt = ""

fb_app_id and fb_admins are only allowed in the config.author's profile. In addition, image and image_alt of the profile will be used as a fallback open graph image for all pages.

[extra.profiles.your_username.open_graph]
fb_app_id = "Your fb app ID"
fb_admins = ["YOUR_USER_ID"]
# image = ""
# image_alt = ""
keytype
extra.footer.sincenumber
extra.footer.copyrightstring
extra.footer.license_urlstring
extra.footer.privacy_policy_urlstring
extra.footer.terms_of_service_urlstring
extra.footer.search_page_urlstring

Currently privacy_policy_url, terms_of_service_url, and search_page_url are not shown.

$BASE_URL is supported in the _url options.

Option copyright supports Markdown and:

[extra.footer]
# Replace with the correct year
since = 2024
# Replace with the url of the license you want
license_url = "https://creativecommons.org/licenses/by-sa/4.0/deed"
# Replace `Your Name` with your name and `CC BY-SA 4.0` with the name of the license you want
copyright = "© $YEAR Your Name | [CC BY-SA 4.0]($LICENSE_URL)"
# privacy_policy_url = "$BASE_URL/privacy-policy/"
# terms_of_service_url = "$BASE_URL/terms-of-service/"
# search_page_url = "$BASE_URL/search/"

Locale and Date format

keytypedefault value
extra.languages[lang].localestring
extra.languages[lang].date_formatstring%F
extra.languages[lang].date_format_archivestring%m-%d
extra.languages[lang].header_menu_namestring
extra.languages[lang].header_buttonsarray of strings
extra.languages[lang].art_x_langstring

For date format, see chrono docs.

[extra.languages.en]
locale = "en_US"
date_format = "%x"
date_format_archive = "%m-%d"

[extra.languages.fr]
locale = "fr_FR"
date_format = "%x"
date_format_archive = "%m-%d"

Web analytics

keytype
extra.goatcounter.endpointstring
extra.goatcounter.srcstring

Set only if you use GoatCounter.

[extra.goatcounter]
endpoint = "https://MYCODE.goatcounter.com/count"
src = "//gc.zgo.at/count.js"

Comments

keytypedefault value
extra.giscus.repostring
extra.giscus.repo_idstring
extra.giscus.categorystring
extra.giscus.category_idstring
extra.giscus.mappingstringpathname
extra.giscus.strictnumber1
extra.giscus.reactions_enablednumber0
extra.giscus.emit_metadatanumber0
extra.giscus.input_positionstringtop
extra.giscus.themestringlight
extra.giscus.langstringen
extra.giscus.loadingstringlazy

See giscus.app. Only available when extra.comment in the frontmatter or extra.comment in the config is set to true.

[extra.giscus]
repo = ""
repo_id = ""
category = ""
category_id = ""
mapping = "pathname"
strict = 1
reactions_enabled = 0
emit_metadata = 0
input_position = "top"
theme = "light"
lang = "en"
loading = "lazy"

License

See the MIT License file.

Contributing

Pull requests are welcome on Codeberg and Github. Open bug reports and feature requests on Codeberg.

Blogs using this theme

If you use Linkita, feel free to create a pull request to add your site to this list.

See also Google results and Bing results.