{"id":50896468,"url":"https://github.com/kako-jun/avel","last_synced_at":"2026-06-16T00:35:24.505Z","repository":{"id":361782342,"uuid":"1255767861","full_name":"kako-jun/avel","owner":"kako-jun","description":"A minimal, no-JS Zola theme. Inspired by Abe Hiroshi's homepage.","archived":false,"fork":false,"pushed_at":"2026-06-15T22:02:59.000Z","size":510,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-16T00:34:51.980Z","etag":null,"topics":["blog","minimal","zola","zola-theme"],"latest_commit_sha":null,"homepage":"https://avel.llll-ll.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kako-jun.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["kako-jun"]}},"created_at":"2026-06-01T06:44:33.000Z","updated_at":"2026-06-15T22:03:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kako-jun/avel","commit_stats":null,"previous_names":["kako-jun/avel"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kako-jun/avel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Favel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Favel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Favel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Favel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kako-jun","download_url":"https://codeload.github.com/kako-jun/avel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kako-jun%2Favel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34386320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog","minimal","zola","zola-theme"],"created_at":"2026-06-16T00:35:23.806Z","updated_at":"2026-06-16T00:35:24.497Z","avatar_url":"https://github.com/kako-jun.png","language":"JavaScript","funding_links":["https://github.com/sponsors/kako-jun"],"categories":[],"sub_categories":[],"readme":"# avel\n\n\u003cp\u003e\u003cimg src=\"static/lighthouse.webp\" alt=\"Lighthouse 100 / 100 / 100 / 100\" width=\"453\"\u003e\u003c/p\u003e\n\n**The fastest-rendering [Zola](https://www.getzola.org/) blog theme.** A homage to [Hiroshi Abe's famously lightweight official homepage](http://abehiroshi.la.coocan.jp/) — the page that renders before you blink — rebuilt as a modern, responsive, fully customizable theme.\n\n- **Instant display** — pure HTML streamed top-to-bottom; the first characters paint immediately, nothing waits on a wrapper to finish loading\n- **Instant navigation** — internal links are prerendered on hover (Speculation Rules) and cross-faded with CSS View Transitions; clicking feels like ~0ms\n- **No runtime JavaScript, zero external resources** by default (no web fonts, no CDN; the only `\u003cscript\u003e` tags are declarative data — JSON-LD structured data and a Speculation Rules hint — never executed code, and both can be switched off)\n- **Inline critical CSS** — no render-blocking external stylesheet request\n- **`content-visibility` for off-screen content** — long lists/articles skip rendering until scrolled into view, while on-screen text still paints instantly\n- **Responsive** — Abe's site isn't; avel is (one CSS media query, no JS)\n- **SEO out of the box** — canonical URL, Open Graph, Twitter Card, JSON-LD structured data, sitemap and feed autodiscovery, all emitted by default with zero runtime JS\n- **Optional no-JS comments** — show a per-post Nostalgic BBS image that opens the interactive posting page only when clicked\n- **Syntax-highlighted code** — build-time highlighting with inline styles (no JS, no extra request)\n- **Automatic dark mode** — follows `prefers-color-scheme` with CSS only; set `dark_mode = false` to stay always-light\n- **Content organization** — tags, a year-grouped archive, tag-based related posts, and prev/next post navigation\n- **Custom 404 page and favicon** (favicon overridable via config)\n- **Lazy-loaded content images**, eager LCP image with `fetchpriority`\n- **Fully customizable** via `config.toml` — defaults recreate the Abe look, every knob is overridable, no HTML/CSS editing needed\n\n## Demo\n\n[avel.llll-ll.com](https://avel.llll-ll.com)\n\n## Installation\n\n```bash\ncd your-zola-site\ngit submodule add https://github.com/kako-jun/avel themes/avel\n```\n\nSet in `config.toml`:\n\n```toml\ntheme = \"avel\"\n```\n\n## Configuration\n\nAll style options are controlled via `[extra]` in `config.toml`. Copy and uncomment what you need:\n\nAll defaults recreate the Abe Hiroshi look. Uncomment any line to override it.\n\n```toml\n[extra]\n# --- Profile (shown centered in the top-page content, like Abe's right frame — not in the sidebar) ---\n# name = \"Your Name\"\n# profile_image = \"me.webp\"       # place in static/\n# profile_text = \"one-line bio\"   # optional, under the name\n\n# --- Background ---\n# background_image = \"bg.svg\"     # place in static/\n# body_bg = \"#ffffff\"\n\n# --- Layout ---\n# nav_width = \"18%\"               # default: 18% (Abe's frameset is cols=18,82)\n# nav_bg = \"#f0f0ff\"              # default: #f0f0ff (pale lavender)\n# nav_border = true               # default: true (right divider, a nod to the old frame border)\n# nav_item_gap = \"1.3em\"          # default: 1.3em\n# nav_bullet = \"●\"                # default: ● (set \"\" to remove)\n# nav_bullet_colors = [\"#ffcccc\",\"#00ff00\",\"#33ffff\",\"#0099ff\",\"#0000ff\",\"#333399\",\"#cc00cc\"]  # default: Abe's 7-color cycle\n# main_padding = \"1em\"\n# max_width = \"960px\"\n# content_align = \"left\"         # left / center / justify\n# title_align = \"center\"          # page \u003ch1\u003e alignment, default: center\n# line_height = \"1.5\"            # default: 1.5\n# to_top = true                   # fixed bottom-right back-to-top \"↑\" link (no JS, default: true); set false to remove\n\n# --- Font ---\n# font = \"sans-serif\"             # serif / sans-serif / monospace, default: sans-serif (Abe is unstyled, so it shows the browser default — gothic for Japanese)\n# font_family = \"Georgia\"         # system fonts only = no external requests\n# google_fonts_url = \"https://fonts.googleapis.com/css2?family=Noto+Serif+JP\u0026display=swap\"\n# font_size = \"16px\"              # default: 16px (browser default)\n\n# --- Colors ---\n# text_color = \"#000000\"          # default: black\n# link_color = \"#0000ee\"          # default: browser-default blue\n# link_visited = \"#551a8b\"\n\n# --- Speed (modern, keeps Lighthouse 100) ---\n# view_transitions = false        # off by default: cross-document CSS fade can flash on dark themes mid-navigation; set true to opt in (no JS)\n# speculation_rules = true        # prerender internal links on hover for ~0ms navigation (one declarative \u003cscript\u003e; external links and /atom.xml excluded)\n\n# --- Date ---\n# date_format = \"%Y-%m-%d\"        # e.g. \"%Y年%m月%d日\"\n\n# --- Nostalgic BBS comments (no JS on article pages; click the image to post) ---\n# nostalgic_bbs = false\n# nostalgic_bbs_limit = 3\n# nostalgic_bbs_width = 760\n# nostalgic_bbs_label = \"Comments\"\n# nostalgic_bbs_hint = \"Click to comment\"\n\n# --- Footer ---\n# footer = \"© Your Name\"\n\n# --- SEO / OGP (canonical, Open Graph, Twitter Card, and JSON-LD are emitted by default) ---\n# og_image = \"ogp.png\"            # OGP/Twitter card image, place in static/ (output as an absolute URL)\n# og_image_alt = \"My site\"        # alt text for social card images\n# og_image_type = \"image/png\"     # optional, helps social crawlers classify the image\n# og_image_width = \"1200\"         # optional, recommended for stable social previews\n# og_image_height = \"630\"         # optional, recommended for stable social previews\n# twitter = \"@yourhandle\"         # twitter:site handle for Twitter Cards\n\n# --- Navigation ---\n# nav = [\n#   { label = \"Home\", url = \"/\" },\n#   { label = \"Posts\", url = \"/posts/\" },\n# ]\n```\n\n## Content structure\n\n```\ncontent/\n  _index.md              # English top page (template = \"index.html\")\n  _index.ja.md           # Japanese top page, served under /ja/\n  posts/\n    _index.md            # English posts section\n    _index.ja.md         # Japanese posts section\n    my-post.md           # English post\n    my-post.ja.md        # Japanese translation with the same slug\n  archive.md             # English year-grouped archive\n  archive.ja.md          # Japanese year-grouped archive\n```\n\navel uses Zola's static multilingual support. English is the default language, so English files use the plain slug and Japanese translations use `.ja.md`. The generated URLs are `/posts/my-post/` and `/ja/posts/my-post/`. The theme does not use JavaScript or browser language detection; it renders static language links when `extra.language_switch = true` is set.\n\nThe top page pulls the latest posts directly via `get_section(path=\"posts/_index.md\", lang=lang)` in `index.html`, so each language shows only its own posts and no `transparent` flag is needed on `posts/_index.md`.\n\nPrev/next post links at the bottom of each post follow the section's `sort_by` order. `sort_by = \"date\"` is recommended: without `sort_by` the links are not rendered, and with `weight` the left/right direction no longer means older/newer.\n\n## Nostalgic BBS comments\n\navel can show a per-post [Nostalgic BBS](https://nostalgic.llll-ll.com/bbs) image below the share links. Article pages stay JavaScript-free; clicking the image opens Nostalgic's interactive BBS page where visitors can post.\n\nEnable rendering:\n\n```toml\n[extra]\nnostalgic_bbs = true\nnostalgic_bbs_limit = 3\nnostalgic_bbs_width = 760\nnostalgic_bbs_label = \"Comments\"\nnostalgic_bbs_hint = \"クリックで書き込めます\"\n```\n\nFor per-post provisioning, set `NOSTALGIC_TOKEN` in the build environment and run:\n\n```bash\nnpm run sync:nostalgic-bbs\nzola build\n```\n\nCloudflare Pages can use:\n\n```bash\nnpm run sync:nostalgic-bbs \u0026\u0026 zola build\n```\n\nThe sync script looks up or creates one BBS per final post URL (`base_url` + article path) and writes the returned public ids to `data/nostalgic_bbs.toml`, keyed by the article URL path.\n\n```toml\n[posts]\n\"/posts/my-post/\" = \"my-post-bbs-id\"\n```\n\nExisting ids in this generated mapping are reused, so repeated builds do not call Nostalgic for every article. New posts are looked up with Nostalgic BBS `batchLookup` by their final permalink URL, then only missing BBS entries are created. If `NOSTALGIC_TOKEN` is not set, the script leaves the existing mapping untouched and skips missing posts, so ordinary theme builds still work.\n\n### Large batches (200+ posts)\n\nThe sync script is built to provision many BBS entries in one run. These optional environment variables tune its behavior:\n\n| Variable | Default | Description |\n|---|---|---|\n| `NOSTALGIC_LOOKUP_LIMIT` | `50` | URLs resolved per `batchLookup` request (also the chunk size). |\n| `NOSTALGIC_CREATE_DELAY_MS` | `1200` | Delay in ms applied after each create, to pace requests. Set `0` to disable pacing. |\n| `NOSTALGIC_RETRY_BASE_MS` | `2000` | Base for the linear backoff on retryable failures (429/503/network). Minimum `500`. |\n| `NOSTALGIC_MAX_RETRIES` | `5` | Maximum retry attempts per request. |\n\nWhen provisioning hundreds of posts, the script paces creates and retries `429`/`503` responses and network exceptions with a linear backoff, so a busy API or a brief network hiccup does not abort the whole run. It also writes `data/nostalgic_bbs.toml` incrementally and atomically — after every successful create and at the end of each chunk, via a temp file plus rename — so if the run is interrupted partway, the ids created so far are preserved and a re-run resumes without re-creating them.\n\nThe generated `data/nostalgic_bbs.toml` may be committed for stable local builds, or generated only in the deployment environment. In either case, never put `NOSTALGIC_TOKEN` in front matter, templates, or generated HTML. If you need to pin an existing BBS manually, edit this mapping file rather than individual post Markdown.\n\nNostalgic handles any lower-level D1 query chunking internally, so splitting requests to satisfy SQLite bind limits is unnecessary. This script still chunks lookups into `NOSTALGIC_LOOKUP_LIMIT` (default `50`) URLs per `batchLookup` — not for bind limits, but to pace requests and checkpoint progress, writing `data/nostalgic_bbs.toml` after each chunk so an interrupted run resumes without re-creating BBS entries.\n\n## Using the auto comment section with any Zola theme\n\nThe Nostalgic BBS comment block is an independent component that works with any Zola theme. You do not need to modify the theme itself. Follow these four steps to embed per-post comment sections without adding JavaScript to your article pages.\n\n**Prerequisites:** Nostalgic is free and requires no account sign-up. Choose any 8–16 character string as your token. See [nostalgic.llll-ll.com](https://nostalgic.llll-ll.com) for details.\n\n\u003e **avel users:** If you are using avel as your theme, this is already set up. Just set `nostalgic_bbs = true` in your `config.toml` and run `npm run sync:nostalgic-bbs` before building. No further steps are needed.\n\n### Step 1 — Copy the sync script\n\nCopy `scripts/sync-nostalgic-bbs.mjs` from this repository into your own site's `scripts/` directory. Then change your build command to run the script before Zola:\n\n```bash\nnode scripts/sync-nostalgic-bbs.mjs \u0026\u0026 zola build\n```\n\nFor Cloudflare Pages (or any CI environment), set the same command as the build command in the dashboard.\n\nThe script scans `content/posts/` by default. If your posts live in a different directory, set the `NOSTALGIC_CONTENT_DIR` environment variable:\n\n```bash\nNOSTALGIC_CONTENT_DIR=content/articles node scripts/sync-nostalgic-bbs.mjs \u0026\u0026 zola build\n```\n\n### Step 2 — Set the token as an environment variable\n\nThe script reads `process.env.NOSTALGIC_TOKEN`. **Never commit the token to your repository.**\n\nFor local builds, set it in your shell:\n\n```bash\nexport NOSTALGIC_TOKEN=your-token-here\n```\n\nFor Cloudflare Pages or other CI environments, add `NOSTALGIC_TOKEN` as an environment variable in the project settings.\n\nIf `NOSTALGIC_TOKEN` is not set, the script keeps any existing BBS ids intact and exits without creating new entries. Posts that already have a BBS id will continue to show their comment section; only new posts without an id are skipped.\n\n### Step 3 — Add settings to `config.toml`\n\nOnly `nostalgic_bbs = true` is required. The other four keys are optional — the template snippet provides defaults for all of them.\n\n```toml\n[extra]\nnostalgic_bbs = true\nnostalgic_bbs_limit = 3          # number of recent comments shown in the image (optional, default 3)\nnostalgic_bbs_width = 760        # pixel width used when generating the SVG image (optional, default 760)\nnostalgic_bbs_label = \"Comments\" # heading text above the image (optional, default \"Comments\")\nnostalgic_bbs_hint = \"Click to comment\" # caption text below the image (optional, default \"Click to comment\")\n```\n\n### Step 4 — Add the snippet to your `templates/page.html`\n\nOverride your theme's page template by creating `templates/page.html` in your site root (Zola looks there before the theme directory). Copy this snippet and place it where you want the comment section to appear — typically after the article body.\n\n\u003e This is the minimal paste-in fragment. avel's own `templates/page.html` is the full version with CSS classes and additional markup. If you want the comment section to match avel's styling, refer to that file instead.\n\n```html\n{% if config.extra.nostalgic_bbs | default(value=false) %}\n{% set_global nostalgic_bbs_public_id = \"\" %}\n{% set nostalgic_bbs = load_data(path=\"data/nostalgic_bbs.toml\", required=false) %}\n{% if nostalgic_bbs and nostalgic_bbs.posts %}\n  {% for post_path, post_bbs_id in nostalgic_bbs.posts %}\n    {% if post_path == page.path %}\n      {% set_global nostalgic_bbs_public_id = post_bbs_id %}\n    {% endif %}\n  {% endfor %}\n{% endif %}\n{% if nostalgic_bbs_public_id %}\n  {% set limit = config.extra.nostalgic_bbs_limit | default(value=3) %}\n  {% set width = config.extra.nostalgic_bbs_width | default(value=760) %}\n  {% set label = config.extra.nostalgic_bbs_label | default(value=\"Comments\") %}\n  {% set hint  = config.extra.nostalgic_bbs_hint  | default(value=\"Click to comment\") %}\n  \u003cdiv class=\"nostalgic-bbs\"\u003e\n    \u003cp\u003e{{ label }}\u003c/p\u003e\n    \u003ca href=\"https://nostalgic.llll-ll.com/bbs?id={{ nostalgic_bbs_public_id | urlencode }}\"\n       target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n      \u003cimg src=\"https://api.nostalgic.llll-ll.com/bbs?action=get\u0026id={{ nostalgic_bbs_public_id | urlencode }}\u0026format=image\u0026limit={{ limit }}\u0026width={{ width }}\"\n           alt=\"{{ label }} for {{ page.title }}\"\n           loading=\"lazy\" decoding=\"async\"\u003e\n    \u003c/a\u003e\n    \u003cp\u003e{{ hint }}\u003c/p\u003e\n  \u003c/div\u003e\n{% endif %}\n{% endif %}\n```\n\n### Why no JavaScript?\n\nThe comment display is a static `\u003cimg\u003e` tag pointing to a Nostalgic API endpoint that returns an SVG. Clicking it navigates to the interactive Nostalgic BBS page where visitors post. This means:\n\n- Article pages contain **zero JavaScript** for comments\n- Comments render even with JavaScript disabled in the browser\n- No third-party script is loaded; only one image request is made per article page view\n\n## Tags\n\nAdd a taxonomy to `config.toml`:\n\n```toml\n[[taxonomies]]\nname = \"tags\"\nurl = \"tags\"\nfeed = false\n\n[languages.ja]\ntaxonomies = [\n  { name = \"tags\", url = \"tags\", feed = false },\n]\n```\n\nThen add tags to your posts:\n\n```toml\n+++\ntitle = \"My Post\"\ndate = 2026-01-01\n\n[taxonomies]\ntags = [\"foo\", \"bar\"]\n+++\n```\n\nTag pages are generated at `/tags/` and `/tags/{name}/`. In multilingual sites, translated tag pages are generated under the language path, such as `/ja/tags/` and `/ja/tags/{name}/`.\n\n## Theme gallery\n\nThis theme has been submitted to [getzola.org/themes](https://www.getzola.org/themes/).\nGitHub topic: [`zola-theme`](https://github.com/topics/zola-theme)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkako-jun%2Favel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkako-jun%2Favel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkako-jun%2Favel/lists"}