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.
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
.
linkita
as your theme in your config.toml
file.theme = "linkita"
linkita
branch to the latest release:cd themes/linkita
npm run switch-to-latest
git submodule update --merge --remote themes/linkita
# cd themes/linkita
# npm run switch-to-latest
+++
title = ""
description = ""
# date =
# updated =
[taxonomies]
categories = []
tags = []
authors = []
[extra]
# comment = true
# math = true
# mermaid = true
# page_info = []
[extra.cover]
# image = ""
# alt = ""
+++
---
title: ""
description: ""
date:
# updated:
taxonomies:
categories:
tags:
authors:
extra:
comment: false
math: false
mermaid: false
cover:
image: ""
alt: ""
---
[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 = ""
[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 =
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
.
You should add extra.profiles.author_username
table in your config.toml
file for each author.
Replace author_username
with author's username.
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"]
+++
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"]
+++
+++
title = "Archive"
template = "archive.html"
[extra]
section = "_index.md"
+++
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
.
Action | Shortcut |
---|---|
Home | Alt+! |
Search | Alt+/ |
Toggle menu | Alt++ |
Toggle dark mode | Alt+$ |
Go to prev page | Alt+, |
Go to next page | Alt+. |
Table of contents | Alt+= |
Skip to footer | Alt+_ |
Skip to main | Alt+- |
Copy and paste the examples into your config.toml
file
and comment out the options you don't use instead of setting empty values.
key | type |
---|---|
default_language | string |
author | string |
title | string |
description | string |
generate_feeds | boolean |
feed_filenames | array of strings |
build_search_index | boolean |
extra | table |
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
key | type |
---|---|
extra.math | boolean |
extra.mermaid | boolean |
extra.comment | boolean |
extra.title_separator | string |
extra.header_menu_name | string |
extra.header_buttons | array of strings |
extra.page_info | array of strings |
extra.disable_default_favicon | boolean |
extra.disable_javascript | boolean |
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 both | only on page | only on paginator |
---|---|---|
date | date_on_page | date_on_paginator |
date_updated | date_updated_on_page | date_updated_on_paginator |
reading_time | reading_time_on_page | reading_time_on_paginator |
word_count | word_count_on_page | word_count_on_paginator |
authors | authors_on_page | authors_on_paginator |
tags | tags_on_page | tags_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
key | type | default value |
---|---|---|
extra.style.bg_color | string | "#f4f4f5" |
extra.style.bg_dark_color | string | "#18181b" |
extra.style.header_blur | boolean | false |
extra.style.header_color | string | "#e4e4e7" |
extra.style.header_dark_color | string | "#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"
key | type |
---|---|
extra.menus[menu_name].menu[].url | string |
extra.menus[menu_name].menu[].name | string |
extra.menus[menu_name].menu[].names | table |
extra.menus[menu_name].menu[].names[lang] | string |
extra.menus[menu_name].menu[].names_i18n | string |
$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"
key | type |
---|---|
extra.profiles[username].avatar_url | string |
extra.profiles[username].avatar_alt | string |
extra.profiles[username].avatar_invert | boolean |
extra.profiles[username].name | string |
extra.profiles[username].bio | string |
extra.profiles[username].email | string |
extra.profiles[username].url | string |
extra.profiles[username].languages | table |
extra.profiles[username].social | array of tables |
extra.profiles[username].open_graph | table |
[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 = ""
key | type |
---|---|
extra.profiles[username].languages[lang].name | string |
extra.profiles[username].languages[lang].bio | string |
extra.profiles[username].languages[lang].url | string |
extra.profiles[username].languages[lang].avatar_alt | string |
[extra.profiles.your_username.languages.fr]
# Profile name in French
name = ""
# Profile bio in French
bio = ""
key | type |
---|---|
extra.profiles[username].social[].name | string |
extra.profiles[username].social[].url | string |
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"
key | type |
---|---|
extra.profiles[username].open_graph.image | string |
extra.profiles[username].open_graph.image_alt | string |
extra.profiles[username].open_graph.first_name | string |
extra.profiles[username].open_graph.last_name | string |
extra.profiles[username].open_graph.username | string |
extra.profiles[username].open_graph.gender | string |
extra.profiles[username].open_graph.fb_app_id | string |
extra.profiles[username].open_graph.fb_admins | array of strings |
extra.profiles[username].open_graph.fediverse_creator | table |
extra.profiles[username].open_graph.fediverse_creator.handle | string |
extra.profiles[username].open_graph.fediverse_creator.domain | string |
extra.profiles[username].open_graph.fediverse_creator.url | string |
extra.profiles[username].open_graph.languages[lang] | table |
[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 = ""
key | type |
---|---|
extra.footer.since | number |
extra.footer.copyright | string |
extra.footer.license_url | string |
extra.footer.privacy_policy_url | string |
extra.footer.terms_of_service_url | string |
extra.footer.search_page_url | string |
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:
$BASE_URL
$YEAR
(uses since
)$LICENSE_URL
(uses license_url
)[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/"
key | type | default value |
---|---|---|
extra.languages[lang].locale | string | |
extra.languages[lang].date_format | string | %F |
extra.languages[lang].date_format_archive | string | %m-%d |
extra.languages[lang].header_menu_name | string | |
extra.languages[lang].header_buttons | array of strings | |
extra.languages[lang].art_x_lang | string |
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"
key | type |
---|---|
extra.goatcounter.endpoint | string |
extra.goatcounter.src | string |
Set only if you use GoatCounter.
[extra.goatcounter]
endpoint = "https://MYCODE.goatcounter.com/count"
src = "//gc.zgo.at/count.js"
key | type | default value |
---|---|---|
extra.giscus.repo | string | |
extra.giscus.repo_id | string | |
extra.giscus.category | string | |
extra.giscus.category_id | string | |
extra.giscus.mapping | string | pathname |
extra.giscus.strict | number | 1 |
extra.giscus.reactions_enabled | number | 0 |
extra.giscus.emit_metadata | number | 0 |
extra.giscus.input_position | string | top |
extra.giscus.theme | string | light |
extra.giscus.lang | string | en |
extra.giscus.loading | string | lazy |
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"
See the MIT License file.
Pull requests are welcome on Codeberg and Github. Open bug reports and feature requests on Codeberg.
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.