├── .github
└── workflows
│ ├── create-template.yml
│ └── pages.yml
├── .gitignore
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── _bin
└── pagefind
├── _config.yml
├── _includes
├── catalogue_item.html
├── comments.html
├── footer.html
├── head.html
├── navigation.html
└── search.html
├── _layouts
├── default.html
├── home.html
├── page.html
└── post.html
├── _pages
├── 404.html
├── about.md
└── tags.html
├── _plugins
├── jekyll-last-modified.rb
├── no_date.rb
├── pagefind.rb
├── strip_footnotes.rb
└── wiki-links.rb
├── _posts
├── bidirectional.md
├── example-content.md
├── footnotes.md
├── introducing-tail.md
├── managing-excerpt.md
├── pagination-post.md
├── posts-without-date.md
├── posts-without-filename.md
├── reply-comments.md
├── search.md
├── sticky-posts.md
└── upgrades.md
├── assets
├── css
│ ├── _sass
│ │ ├── 404.scss
│ │ ├── base.scss
│ │ ├── catalogue.scss
│ │ ├── code.scss
│ │ ├── footnotes.scss
│ │ ├── layout.scss
│ │ ├── pagination.scss
│ │ ├── post.scss
│ │ ├── search.scss
│ │ ├── tags.scss
│ │ └── variables.scss
│ ├── main.scss
│ ├── sass-code-highlight
│ │ ├── default.scss
│ │ └── monokai.scss
│ ├── syntax-default.scss
│ └── syntax-monokai.scss
├── favicon
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ └── favicon-32x32.png
└── js
│ ├── disqusLoader.js
│ ├── footnotes.js
│ └── pagefind.js
├── favicon.ico
└── index.html
/.github/workflows/create-template.yml:
--------------------------------------------------------------------------------
1 | name: Create Template Branch
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | create-template-branch:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Set git identity
18 | run: |
19 | git config user.name "github-actions[bot]"
20 | git config user.email "github-actions[bot]@users.noreply.github.com"
21 |
22 | - name: Create template branch
23 | run: |
24 | git checkout main
25 | git branch -D template || true
26 | git checkout -b template
27 | git rm -rf --cached --ignore-unmatch "_posts" "Gemfile.lock"
28 | git commit -m "Update template branch"
29 | git push origin template -f
30 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/pages.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy to Github Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main # Here source code branch is `master`, it could be other branch
7 |
8 | jobs:
9 | build_and_deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | # The checkout action doesn't provide a way to get all commit history for a single branch
15 | # So we use the magic number 2147483647 here which means infinite depth for git fetch
16 | # See https://github.com/actions/checkout/issues/520, https://stackoverflow.com/a/6802238
17 | fetch-depth: 2147483647
18 |
19 | # Use GitHub Actions' cache to cache dependencies on servers
20 | - uses: actions/cache@v4
21 | with:
22 | path: |
23 | .asdf/**
24 | vendor/bundle
25 | key: ${{ runner.os }}-cache-${{ hashFiles('**/cache.key') }}
26 | restore-keys: |
27 | ${{ runner.os }}-cache-
28 |
29 |
30 | # Use GitHub Deploy Action to build and deploy to Github
31 | - uses: jeffreytse/jekyll-deploy-action@v0.6.0
32 | with:
33 | provider: 'github'
34 | token: ${{ secrets.GITHUB_TOKEN }} # It's your Personal Access Token(PAT)
35 | repository: '' # Default is current repository
36 | branch: 'gh-pages' # Default is gh-pages for github provider
37 | jekyll_src: './' # Default is root directory
38 | jekyll_cfg: '_config.yml' # Default is _config.yml
39 | jekyll_baseurl: '' # Default is according to _config.yml
40 | bundler_ver: '>=0' # Default is latest bundler version
41 | cname: '' # Default is to not use a cname
42 | actor: '' # Default is the GITHUB_ACTOR
43 | pre_build_commands: '' # Installing additional dependencies (Arch Linux)
44 |
45 | # Step 2: Checkout gh-pages and run Pagefind
46 | - name: Checkout gh-pages and Run Pagefind
47 | uses: actions/checkout@v4
48 | with:
49 | ref: gh-pages
50 | path: gh-pages-dir
51 |
52 | - name: Run Pagefind on gh-pages
53 | run: |
54 | # Download Pagefind binary for Linux
55 | curl -L -o pagefind.tar.gz https://github.com/CloudCannon/pagefind/releases/download/v1.3.0/pagefind-v1.3.0-x86_64-unknown-linux-musl.tar.gz
56 | tar -xzf pagefind.tar.gz
57 | chmod +x pagefind
58 |
59 | # Run Pagefind against the gh-pages directory
60 | ./pagefind --site gh-pages-dir
61 |
62 | # Commit and push the Pagefind index
63 | cd gh-pages-dir
64 | git config user.name "github-actions[bot]"
65 | git config user.email "github-actions[bot]@users.noreply.github.com"
66 | git add .
67 | git commit -m "Add Pagefind index"
68 | git push origin gh-pages
69 | env:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71 |
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _site/
2 | .sass-cache/
3 | .jekyll-metadata
4 | .jekyll-cache
5 | .DS_Store
6 | Gemfile.lock
7 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 |
5 | gem 'jekyll', "~> 4.0"
6 |
7 | group :jekyll_plugins do
8 | #plugins in _config.yml
9 | gem 'jekyll-seo-tag'
10 | gem 'jekyll-paginate'
11 | gem 'jekyll-email-protect'
12 | gem 'jekyll-feed'
13 |
14 | #dependeny for custom footnote plugin
15 | gem 'nokogiri'
16 | end
17 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | addressable (2.8.7)
5 | public_suffix (>= 2.0.2, < 7.0)
6 | base64 (0.2.0)
7 | bigdecimal (3.1.9)
8 | colorator (1.1.0)
9 | concurrent-ruby (1.3.5)
10 | csv (3.3.3)
11 | em-websocket (0.5.3)
12 | eventmachine (>= 0.12.9)
13 | http_parser.rb (~> 0)
14 | eventmachine (1.2.7)
15 | ffi (1.17.1-arm64-darwin)
16 | ffi (1.17.1-x86_64-darwin)
17 | ffi (1.17.1-x86_64-linux-gnu)
18 | forwardable-extended (2.6.0)
19 | google-protobuf (4.30.2-arm64-darwin)
20 | bigdecimal
21 | rake (>= 13)
22 | google-protobuf (4.30.2-x86_64-darwin)
23 | bigdecimal
24 | rake (>= 13)
25 | google-protobuf (4.30.2-x86_64-linux)
26 | bigdecimal
27 | rake (>= 13)
28 | http_parser.rb (0.8.0)
29 | i18n (1.14.7)
30 | concurrent-ruby (~> 1.0)
31 | jekyll (4.4.1)
32 | addressable (~> 2.4)
33 | base64 (~> 0.2)
34 | colorator (~> 1.0)
35 | csv (~> 3.0)
36 | em-websocket (~> 0.5)
37 | i18n (~> 1.0)
38 | jekyll-sass-converter (>= 2.0, < 4.0)
39 | jekyll-watch (~> 2.0)
40 | json (~> 2.6)
41 | kramdown (~> 2.3, >= 2.3.1)
42 | kramdown-parser-gfm (~> 1.0)
43 | liquid (~> 4.0)
44 | mercenary (~> 0.3, >= 0.3.6)
45 | pathutil (~> 0.9)
46 | rouge (>= 3.0, < 5.0)
47 | safe_yaml (~> 1.0)
48 | terminal-table (>= 1.8, < 4.0)
49 | webrick (~> 1.7)
50 | jekyll-email-protect (1.1.0)
51 | jekyll-feed (0.17.0)
52 | jekyll (>= 3.7, < 5.0)
53 | jekyll-paginate (1.1.0)
54 | jekyll-sass-converter (3.1.0)
55 | sass-embedded (~> 1.75)
56 | jekyll-seo-tag (2.8.0)
57 | jekyll (>= 3.8, < 5.0)
58 | jekyll-watch (2.2.1)
59 | listen (~> 3.0)
60 | json (2.10.2)
61 | kramdown (2.5.1)
62 | rexml (>= 3.3.9)
63 | kramdown-parser-gfm (1.1.0)
64 | kramdown (~> 2.0)
65 | liquid (4.0.4)
66 | listen (3.9.0)
67 | rb-fsevent (~> 0.10, >= 0.10.3)
68 | rb-inotify (~> 0.9, >= 0.9.10)
69 | mercenary (0.4.0)
70 | nokogiri (1.18.7-arm64-darwin)
71 | racc (~> 1.4)
72 | nokogiri (1.18.7-x86_64-darwin)
73 | racc (~> 1.4)
74 | nokogiri (1.18.7-x86_64-linux-gnu)
75 | racc (~> 1.4)
76 | pathutil (0.16.2)
77 | forwardable-extended (~> 2.6)
78 | public_suffix (6.0.1)
79 | racc (1.8.1)
80 | rake (13.2.1)
81 | rb-fsevent (0.11.2)
82 | rb-inotify (0.11.1)
83 | ffi (~> 1.0)
84 | rexml (3.4.1)
85 | rouge (4.5.1)
86 | safe_yaml (1.0.5)
87 | sass-embedded (1.86.3-arm64-darwin)
88 | google-protobuf (~> 4.30)
89 | sass-embedded (1.86.3-x86_64-darwin)
90 | google-protobuf (~> 4.30)
91 | sass-embedded (1.86.3-x86_64-linux-gnu)
92 | google-protobuf (~> 4.30)
93 | terminal-table (3.0.2)
94 | unicode-display_width (>= 1.1.1, < 3)
95 | unicode-display_width (2.6.0)
96 | webrick (1.9.1)
97 |
98 | PLATFORMS
99 | arm64-darwin-24
100 | x86_64-darwin-21
101 | x86_64-linux
102 |
103 | DEPENDENCIES
104 | jekyll (~> 4.0)
105 | jekyll-email-protect
106 | jekyll-feed
107 | jekyll-paginate
108 | jekyll-seo-tag
109 | nokogiri
110 |
111 | BUNDLED WITH
112 | 2.6.7
113 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Chester How
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tail
2 |
3 | Tail is a minimal Jekyll blog theme heavily based on [Tale](https://github.com/chesterhow/tale) (and hence the name). Apart from the minor fixes to Tale and significant changes under the hood, this theme is designed to make it easier to maintain a blog.
4 |
5 | If you already have a bunch of files in `*.md` format, you can simply copy them to the `_posts` folder after forking this theme and making a few changes to `_config.yml`, your blog is ready to go!
6 |
7 | ## What's new in Tail
8 | - Dark Mode
9 | - Support for posts without the `YYYY-MM-DD` in the post's filename
10 | - Search (using Pagefind)
11 | - Popup Footnotes
12 | - Bi-directional Wiki-style linking support compatible with Obsidian
13 | - Comments via Email
14 |
15 | ## Other Features from Tale (with minor updates)
16 | - Easy installation
17 | - Compatible with GitHub Pages
18 | - Responsive design
19 | - Pagination of posts
20 | - Sticky posts
21 | - Tags
22 | - Excerpt management
23 |
24 | ## Contributing
25 | Found a bug or have a suggestion? Feel free to create an issue or make a pull request!
26 |
27 | ## License
28 | See [LICENSE](https://github.com/jitinnair1/tail/blob/master/LICENSE)
29 |
30 | PS: If you liked the theme, do star :star: it! Thanks!
31 |
32 | ### Also, check out:
33 |
34 | - [autoCV](https://github.com/jitinnair1/autocv) - a LaTeX template that builds and deploys the CV using GitHub Actions, so you will always have a ready link for your latest CV
35 | - [gradfolio](https://github.com/jitinnair1/gradfolio) - a minimal, quick-setup template for a personal website/portfolio
36 | - [snippet-book](https://github.com/jitinnair1/snippet-book) - terminal style, clean Jekyll blog theme with catppuccin colours
37 |
--------------------------------------------------------------------------------
/_bin/pagefind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitinnair1/tail/0c2dbfca6315712f491b4454e74689c13c32641d/_bin/pagefind
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | # Site settings
2 | title: Tail
3 | description: "Minimal Jekyll blog theme"
4 | baseurl: "/tail"
5 | url:
6 |
7 | # https://jekyllrb.com/docs/configuration/options/
8 | timezone: Asia/Kolkata
9 |
10 | # Author
11 | # This is the default author name displayed on all posts, can be overridden by setting a different `author: Your Name`
12 | # in the frontmatter
13 | author:
14 | name: Your Name
15 |
16 | # Build settings
17 | markdown: kramdown
18 | highlighter: rouge
19 |
20 |
21 | # Comments (Reply via email)
22 | comments: true
23 | comment_email: "" #enter an email address where you want comment emails to be sent
24 |
25 | kramdown:
26 | input: GFM
27 | syntax_highlighter: rouge
28 |
29 | include:
30 | - _pages
31 |
32 | # Assets
33 | sass:
34 | sass_dir: /assets/css/_sass
35 | style: compressed
36 |
37 | # Gems
38 | plugins:
39 | - jekyll-feed
40 | - jekyll-paginate
41 | - jekyll-seo-tag
42 |
43 | # Permalinks
44 | permalink: /:title
45 | paginate: 5
46 |
47 | # Excludes
48 | exclude:
49 | - README.md
50 | - LICENSE.md
51 |
52 | # Disqus (Set to your disqus id)
53 | disqus:
54 |
--------------------------------------------------------------------------------
/_includes/catalogue_item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% if include.sticky == 'true' %}
4 |
📌 ·
5 | {% endif %}
6 |
{{ post.date | date: "%B %d, %Y" }}
7 |
{{ post.title }}
8 |
9 |
10 |
11 | {% if post.excerpt_separator %}
12 | {{ post.excerpt | strip_footnotes | strip_html}}
13 | {% else %}
14 | {{ post.content | strip_footnotes | strip_html | truncatewords: 30 }}
15 | {% endif %}
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/_includes/comments.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/_includes/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | © {{ site.time | date: '%Y' }} {{ site.author.name }}. Made with Jekyll using the Tail theme.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/_includes/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% seo %}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {% feed_meta %}
26 |
27 |
28 |
--------------------------------------------------------------------------------
/_includes/navigation.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
--------------------------------------------------------------------------------
/_includes/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
×
6 |
7 | You can also use Ctrl /Cmd + K to toggle search
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include head.html %}
5 |
6 | {% include navigation.html %}
7 |
8 | {% include search.html %}
9 |
10 |
11 |
12 |
13 | {{ content }}
14 |
15 |
16 |
17 |
18 | {% include footer.html %}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/_layouts/home.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 | {% for post in site.posts %}
7 | {% if post.sticky %}
8 | {% include catalogue_item.html sticky='true' %}
9 | {% endif %}
10 | {% endfor %}
11 |
12 | {% for post in paginator.posts %}
13 | {% include catalogue_item.html %}
14 | {% endfor %}
15 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/_layouts/page.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include head.html %}
5 |
6 |
7 |
8 | {% include navigation.html %}
9 |
10 | {% include search.html %}
11 |
12 |
49 |
--------------------------------------------------------------------------------
/_plugins/jekyll-last-modified.rb:
--------------------------------------------------------------------------------
1 | # Using https://github.com/michaelx/jekyll-last-modified
2 |
3 | module Jekyll
4 | class LastModifiedTag < Liquid::Tag
5 |
6 | def initialize(tag_name, path, tokens)
7 | super
8 | @path = path
9 | end
10 |
11 | def render(context)
12 | # Pipe parameter through Liquid to make additional replacements possible
13 | url = Liquid::Template.parse(@path).render context
14 |
15 | # Adds the site source, so that it also works with a custom one
16 | site_source = context.registers[:site].config['source']
17 | file_path = site_source + '/' + url
18 |
19 | # Return last modified date
20 | File.mtime(file_path.strip!)
21 | end
22 | end
23 | end
24 |
25 | Liquid::Template.register_tag('last_modified', Jekyll::LastModifiedTag)
--------------------------------------------------------------------------------
/_plugins/no_date.rb:
--------------------------------------------------------------------------------
1 | # From here: https://stackoverflow.com/a/68287682/9523246
2 |
3 | class Jekyll::PostReader
4 | # Don't use DATE_FILENAME_MATCHER so we don't need to put those stupid dates
5 | # in the filename. Also limit to just *.markdown, so it won't process binary
6 | # files from e.g. drags.
7 | def read_posts(dir)
8 | read_publishable(dir, "_posts", /.*\.md$/)
9 | end
10 | def read_drafts(dir)
11 | read_publishable(dir, "_drafts", /.*\.md$/)
12 | end
13 | end
--------------------------------------------------------------------------------
/_plugins/pagefind.rb:
--------------------------------------------------------------------------------
1 | module Jekyll
2 | class PostCompileCommand < Jekyll::Generator
3 | safe true
4 | priority :lowest
5 |
6 | def generate(site)
7 | Jekyll::Hooks.register :site, :post_write do |_site|
8 | command = './_bin/pagefind --site _site'
9 | puts "Running: #{command}"
10 | system(command)
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/_plugins/strip_footnotes.rb:
--------------------------------------------------------------------------------
1 | # From: https://battlepenguin.com/tech/removing-footnotes-from-excerpts-in-jekyll/
2 |
3 | require 'nokogiri'
4 |
5 | module Jekyll
6 | module StripFootnotesFilter
7 |
8 | def strip_footnotes(raw)
9 | doc = Nokogiri::HTML.fragment(raw.encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => ''))
10 |
11 | for block in ['div', 'sup', 'a'] do
12 | doc.css(block).each do |ele|
13 | ele.remove if (ele['class'] == 'footnotes' or ele['class'] == 'footnote')
14 | end
15 | end
16 |
17 | doc.inner_html
18 |
19 | end
20 | end
21 | end
22 |
23 | Liquid::Template.register_filter(Jekyll::StripFootnotesFilter)
--------------------------------------------------------------------------------
/_plugins/wiki-links.rb:
--------------------------------------------------------------------------------
1 | # _plugins/wiki_links.rb
2 |
3 | Jekyll::Hooks.register [:posts], :pre_render do |post, payload|
4 | site = post.site
5 |
6 | #build a quick lookup of existing post slugs (without extensions)
7 | post_slugs = site.posts.docs.map { |p| File.basename(p.path, ".md") }
8 |
9 | #this regex finds [[wikilinks]] with optional custom titles
10 | post.content.gsub!(/\[\[([^\]\|]+)(\|([^\]]+))?\]\]/) do |_match|
11 | target_slug = Regexp.last_match(1).strip
12 | custom_title = Regexp.last_match(3)&.strip
13 |
14 | #convert target slug for URL (spaces to hyphens, lowercase)
15 | url_slug = target_slug.downcase.gsub(' ', '-')
16 |
17 | #validate if target post actually exists
18 | unless post_slugs.include?(url_slug)
19 | Jekyll.logger.warn "WikiLink Warning:", "Post '#{url_slug}' not found. Leaving raw link."
20 | next "[[#{target_slug}]]" #leave the link untouched
21 | end
22 |
23 | #build the URL
24 | url = "#{site.baseurl}/#{url_slug}"
25 |
26 | #use custom title if available, else the target slug
27 | link_text = custom_title || target_slug
28 |
29 | #return the HTML anchor tag
30 | "#{link_text} "
31 | end
32 | end
33 |
34 |
--------------------------------------------------------------------------------
/_posts/bidirectional.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: Bidirectional Links
4 | date: 24th April 2025
5 | tags:
6 | - New
7 | ---
8 | You can use Obsidian-styte bidirectional links `[[ name_of_note ]]` and it will link to the correct post after the site is built..
9 |
10 | Also, check out other features like [[search]], [[footnotes]] and [[reply-comments]].
11 |
--------------------------------------------------------------------------------
/_posts/example-content.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Example Content"
4 | date: 4th Oct 2022
5 | tags: Old
6 | excerpt_separator:
7 | ---
8 |
9 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tincidunt ornare nibh, non elementum augue tempus eget. Pellentesque tempus scelerisque iaculis. Nullam interdum ultricies nibh quis sollicitudin. Donec ornare fermentum facilisis. Ut at sem ac sem imperdiet varius a eget tortor. Nam eu augue eget orci semper maximus in eget augue. Mauris ornare, nisl ut suscipit consectetur, mi quam interdum tellus, at rutrum quam eros ultrices mi.
10 |
11 | # Headers
12 | ```markdown
13 | # H1
14 | ## H2
15 | ### H3
16 | #### H4
17 | ##### H5
18 | ###### H6
19 | ```
20 |
21 | # H1
22 | ## H2
23 | ### H3
24 | #### H4
25 | ##### H5
26 | ###### H6
27 |
28 | # Text formatting
29 | ```markdown
30 | - **Bold**
31 | - _Italics_
32 | - ~~Strikethrough~~
33 | - Underline
34 | - Superscript
35 | - Subscript
36 | - Abbreviation: HTML
37 | - Citation: — Your Name
38 | ```
39 |
40 | gives you:
41 |
42 | - **Bold**
43 | - _Italics_
44 | - ~~Strikethrough~~
45 | - Underline
46 | - Superscript
47 | - Subscript
48 | - Abbreviation: HTML
49 | - Citation: — Your Name
50 |
51 | # Lists
52 |
53 | ```markdown
54 | 1. Ordered list item 1
55 | 2. Ordered list item 2
56 | 3. Ordered list item 3
57 |
58 | * Unordered list item 1
59 | * Unordered list item 2
60 | * Unordered list item 3
61 | ```
62 |
63 | look like this:
64 |
65 | 1. Ordered list item 1
66 | 2. Ordered list item 2
67 | 3. Ordered list item 3
68 |
69 | * Unordered list item 1
70 | * Unordered list item 2
71 | * Unordered list item 3
72 |
73 | # Links
74 |
75 | ```markdown
76 | Check out tail on [GitHub](https://github.com/jitinnair1/tail).
77 | ```
78 |
79 | give you this:
80 |
81 | Check out tail on [GitHub](https://github.com/jitinnair1/tail).
82 |
83 | # Images
84 |
85 | ```markdown
86 | 
87 |
88 | 
89 | _This is an image with a caption_
90 | ```
91 |
92 | 
93 |
94 | 
95 | _This is an image with a caption_
96 |
97 | # Code and Syntax Highlighting
98 |
99 | Use back-ticks for `inline code`. Multi-line code snippets are supported too through. Specify the language after the back-ticks for language specific syntax highlighting.
100 |
101 | `````
102 | ```ruby
103 | require 'redcarpet'
104 | markdown = Redcarpet.new("Hello World!")
105 | puts markdown.to_html
106 | ```
107 | `````
108 |
109 | which will give you syntax highlighting like this:
110 |
111 | ```ruby
112 | require 'redcarpet'
113 | markdown = Redcarpet.new("Hello World!")
114 | puts markdown.to_html
115 | ```
116 |
117 | ## To display line numbers in codeblocks, you can set them as an option in `_config.yml`
118 |
119 | ```yaml
120 | kramdown:
121 | syntax_highlighter: rouge
122 | syntax_highlighter_opts:
123 | block:
124 | line_numbers: true
125 |
126 | ```
127 |
128 |
129 | # Blockquotes
130 |
131 | ```markdown
132 | > Curabitur blandit tempus porttitor. Nullam quis risus eget urna mollis ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.
133 | ```
134 |
135 | > Curabitur blandit tempus porttitor. Nullam quis risus eget urna mollis ornare vel eu leo. Nullam id dolor id nibh ultricies vehicula ut id elit.
136 |
137 | # Horizontal Rule & Line Break
138 |
139 | Use ` ` for horizontal rules like this
140 |
141 | ```markdown
142 |
143 | ```
144 |
145 | gives you
146 |
147 |
148 |
149 | and ` ` for line breaks:
150 |
151 | ```markdown
152 | This breaks the line
153 | ```
154 |
155 | which will give you:
156 |
157 | This breaks the line
158 |
159 |
160 | _The end_
161 |
--------------------------------------------------------------------------------
/_posts/footnotes.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Better Footnotes"
4 | date: 10th April 2025
5 | tags: New
6 | ---
7 |
8 | When you write something that needs additional context or a citation, you can add a footnote[^1].
9 |
10 | Footnotes are helpful for providing more information without disrupting the flow of your main content. They can be used for citations, clarifications, or just to add interesting asides[^2].
11 |
12 | > The best footnote is one that adds context without demanding it be read. It should be there for the curious reader, not the casual one.
13 |
14 | You can use these for technical clarifications as well[^3]. And unlike traditional print footnotes, these are interactive – try hovering over or clicking on the footnote references!
15 |
16 | ## How it works
17 |
18 | The system uses HTML, CSS, and a bit of JavaScript to make the footnotes interactive. The key components are:
19 |
20 | 1. Footnote references in the text with proper IDs and roles
21 | 2. The footnotes section at the bottom with corresponding IDs
22 | 3. JavaScript that shows the footnote content when you interact with the reference
23 |
24 | The markup follows accessibility standards by using proper ARIA roles. You can even navigate to the footnotes and back using the links[^4].
25 |
26 | ## Credits
27 |
28 | Lastly, this feature was adapted (read stolen) from [this post](https://tools.simonwillison.net/colophon#footnotes-experiment.html) by Simon Willison. Some of the changes I made are:
29 |
30 | - Clicking the footnote does not take you to the footnote listed at the end of the page, instead the interaction now works as hover to preview, click to toggle/hide.[^5]
31 | - The popup content now does not contain the back-link symbol.
32 |
33 |
34 | [^1]: This is the first footnote. It provides additional information that would otherwise clutter the main text.
35 |
36 | [^2]: This second footnote could be used for citation. For example: Smith, J. (2023). *The Art of Footnotes*. *Journal of Useless Knowledge*, 42(1), 123–145.
37 |
38 | [^3]: The footnote system uses the `:target` CSS selector and JavaScript to enhance the user experience. This technical approach allows for both progressive enhancement and accessibility.
39 |
40 | [^4]: The back-link (↩) takes you back to where you came from in the text, which is particularly useful in longer documents.
41 |
42 | [^5]: The footnotes are listed at the bottom of the page only so that the footnote references are visible say when the page is printed.
43 |
44 |
--------------------------------------------------------------------------------
/_posts/introducing-tail.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Introducing Tail"
4 | date: 6th Oct 2022
5 | comments: false
6 | tags: Tail
7 | excerpt_separator:
8 | sticky: true
9 | hidden: true
10 | ---
11 |
12 | How do take an already amazing theme like [Tale](https://github.com/chesterhow/tale) and make it better? Dark-mode![^1] - well, that and many more changes under the hood is what led to this theme called Tail (as a hat tip to the original theme of course)
13 |
14 |
15 | ## What's new in Tail
16 |
17 | - Dark Mode
18 | - Popup [[footnotes|Footnotes]]
19 | - Search (via [Pagefind](https://pagefind.app))
20 | - Comment via [[reply-comments | reply by email]]
21 | - Bi-directional Wiki-style linking support compatible with Obsidian
22 | - Support for posts without the `YYYY-MM-DD` in post's filename and `lastmod` dates
23 |
24 | ## Some great features from the original Tale (with minor updates)
25 | - Compatible with GitHub Pages
26 | - Responsive design (looks just as good on mobile)
27 | - Syntax highlighting
28 | - Markdown and HTML text formatting
29 | - Pagination of posts
30 | - Sticky posts
31 | - Tags
32 | - Excerpt management
33 |
34 | [^1]: For now, there is no toggle button for the dark mode. This is by design. I wanted the dark-mode to be on if the browser or system level dark-mode is enabled. Maybe, in a later version, I will add support for a dark/light toggle. Happy to accept a PR!
35 |
--------------------------------------------------------------------------------
/_posts/managing-excerpt.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Managing Excerpt"
4 | date: 2nd Oct 2022
5 | tags: Old
6 | excerpt_separator:
7 | ---
8 |
9 | You can customise the excerpt (the text displayed below each post on the homepage) using the `excerpt-separator`. Here's how you can do so!
10 |
11 | ## Steps
12 |
13 | 1. Add `excerpt_separator: ` to the frontmatter of your blog post.
14 |
15 | 2. Insert this `` at where you would like the excerpt to cut off in your blog post.
16 |
17 | ### Note
18 |
19 | This follows [Jekyll's recommended way of managing excerpts](https://jekyllrb.com/docs/posts/#post-excerpts).
20 |
21 |
--------------------------------------------------------------------------------
/_posts/pagination-post.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Pagination Post"
4 | date: 20th Sept 2022
5 | tags: Old
6 | ---
7 |
8 | Here we see **Tail's** pagination feature in action. It is set to 5 posts per page by default. Feel free to change this number in the `_config.yml` file!
9 |
--------------------------------------------------------------------------------
/_posts/posts-without-date.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Posts Without Date in Frontmatter"
3 | layout: post
4 | date: 1st Sept 2022
5 | lastmod: 3rd Dec 2022
6 | comments: false
7 | tags: Tail
8 | excerpt_separator:
9 | hidden: false
10 | ---
11 |
12 | Typically, posts in Jekyll have a `date` entry in the post’s frontmatter. This date, if not specified will be taken to be the date when the post was last modified.
13 |
14 |
15 |
16 | This is done using the `jekyll-last-modified` custom plugin (check the `_plugins` directory in the source files). The last-modified date is only used, if a `date` in not specified in the front matter. Also, you can specify the `lastmod` date in the front matter using the `last_modified_at` keyword:
17 |
18 | ```yaml
19 | ---
20 | title: "Posts Without Date in Frontmatter"
21 | layout: post
22 | date: 1st Sept 2022
23 | showlastmod: yes
24 | lastmod: 3rd Dec 2022
25 | comments: false
26 | tags: Tail
27 | excerpt_separator:
28 | hidden: false
29 | ---
30 | ```
--------------------------------------------------------------------------------
/_posts/posts-without-filename.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Posts Without Date in Filename"
3 | date: 1st Oct 2022
4 | layout: post
5 | comments: false
6 | tags: New
7 | excerpt_separator:
8 | hidden: false
9 | ---
10 |
11 | Typically, posts in Jekyll have a `YYYY-MM-DD` portion in the post’s filename. The individual markdown posts can be freed from this naming convention to reduce the clutter in the filename.
12 |
13 |
14 |
15 | Also, it become easier to export a set of `*.md` files from any note-taking app and add them to the `_posts` folder without worrying about changing the filenames.
16 |
17 | Tail does this by using a [small plugin](https://stackoverflow.com/a/68287682/9523246) that changes the `DATE_FILENAME_MATCHER` for `_posts` and `_drafts` folders
18 |
19 | ```ruby
20 | class Jekyll::PostReader
21 | # Don't use DATE_FILENAME_MATCHER so we don't need to put those stupid dates
22 | # in the filename. Also limit to just *.markdown, so it won't process binary
23 | # files from e.g. drags.
24 | def read_posts(dir)
25 | read_publishable(dir, "_posts", /.*\.md$/)
26 | end
27 | def read_drafts(dir)
28 | read_publishable(dir, "_drafts", /.*\.md$/)
29 | end
30 | end
31 | ```
--------------------------------------------------------------------------------
/_posts/reply-comments.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Comments using Email"
4 | date: 25th April 2025
5 | tags: New
6 | ---
7 |
8 | To configure comments via on your blog, edit your `_config.yml`
9 |
10 | ```yaml
11 | # Comments (Reply via email)
12 | comments: true
13 | comment_email: "" #enter an email address where you want comment emails to be sent
14 | ```
15 | This adds a `Reply by email` button at the end of your posts, like so:
16 |
--------------------------------------------------------------------------------
/_posts/search.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Search"
4 | date: 25th April 2025
5 | tags: New
6 | ---
7 |
8 | You can search through all posts by using the search from the navigation bar. This can also be toggled using `Ctrl` or `Cmd` + 'K'. This functionality is built using [Pagefind](https://pagefind.app).
9 |
10 | The `pagefind` binary is a part of the repo under `_bin` and it is run after the site is built.
11 |
--------------------------------------------------------------------------------
/_posts/sticky-posts.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Sticky Posts"
4 | date: 17th Sept 2022
5 | tags: Old
6 | excerpt_separator:
7 | ---
8 |
9 | Sticky, or pinned, posts are featured on the top of every page. Tale provides some flexibility when it comes to this feature. There is no limit on the number of sticky posts you can have. Although do note that each page will show all your sticky posts + the paginated posts. So if you have 4 sticky posts and 5 posts per page, each page can display up to 9 posts.
10 |
11 | ## Making a post "sticky"
12 |
13 | Add `sticky: true` to the frontmatter of your blog post.
14 |
15 | ### Exclude sticky post from paginated posts
16 |
17 | By default, sticky posts are still included in the paginated posts. To exclude a sticky post from paginated posts, add `hidden: true` to the frontmatter of that blog post.
18 |
19 |
--------------------------------------------------------------------------------
/_posts/upgrades.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: post
3 | title: "Cleaner Theme Upgrades"
4 | date: 25th April 2025
5 | tags: New
6 | ---
7 |
8 | If you want to apply the latest updates from `Tale` onto your fork, you can use the `template` branch on the [GitHub repo](https://github.com/jitinnair/tail).
9 |
10 | This branch will always point to the lastest `main` branch but without the `_posts` folder. This is not really a seamless upgrade experience but perhaps a slighly convenient one.
11 |
--------------------------------------------------------------------------------
/assets/css/_sass/404.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .notfound {
4 | position: relative;
5 | text-align: center;
6 | margin: 4rem 0;
7 |
8 | &-error {
9 | font-size: 4rem;
10 | margin: 1rem 0;
11 | }
12 |
13 | &-line {
14 | border-top: 0.4rem solid $default-shade;
15 | display: block;
16 | margin: 0 auto 3rem;
17 | width: 4rem;
18 | }
19 |
20 | &-message {
21 | max-width: 25rem;
22 | margin: 0 auto;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/assets/css/_sass/base.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | * {
4 | @include box-sizing;
5 | line-height: 1.5;
6 | }
7 |
8 | html,
9 | body {
10 | color: $default-color;
11 | margin: 0;
12 | padding: 0;
13 |
14 | @media (prefers-color-scheme: dark) {
15 | color: $default-color-dark;
16 | background-color: $body-bg-dark;
17 | }
18 | }
19 |
20 | html {
21 | font-family: $serif-primary;
22 | font-size: 16px;
23 | overflow-y: scroll;
24 |
25 | @media (min-width: $on-small-screen) {
26 | font-size: 18px;
27 | }
28 | }
29 |
30 | body {
31 | -webkit-text-size-adjust: 100%;
32 | }
33 |
34 | h1,
35 | h2,
36 | h3,
37 | h4,
38 | h5,
39 | h6 {
40 | color: $default-shade;
41 | font-family: $sans-serif;
42 | line-height: normal;
43 |
44 | @media (prefers-color-scheme: dark) {
45 | color: $default-tint;
46 | }
47 | }
48 |
49 | a {
50 | color: $blue;
51 | text-decoration: none;
52 | }
53 |
54 | blockquote {
55 | border-left: .25rem solid $grey-2;
56 | color: $grey-1;
57 | margin: .5rem 1.25rem 0.5;
58 | padding: .1rem 1.5rem .1rem 1.5rem;
59 |
60 | @media (min-width: $on-small-screen) {
61 | padding: .1rem 1.5rem .1rem 1.5rem;
62 | }
63 |
64 | p:last-child {
65 | margin-bottom: 0;
66 | }
67 | }
68 |
69 | img {
70 | display: block;
71 | margin: 0 0 1rem;
72 | max-width: 100%;
73 | }
74 |
75 | td {
76 | vertical-align: top;
77 | }
78 |
--------------------------------------------------------------------------------
/assets/css/_sass/catalogue.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .catalogue {
4 | &-item {
5 | border-bottom: 1px solid $grey-2;
6 | color: $default-color;
7 | display: block;
8 | padding: 2rem 0;
9 |
10 | @media (prefers-color-scheme: dark) {
11 | border-bottom: 1px solid $grey-2-dark;
12 | color: $default-color-dark;
13 | }
14 |
15 | &:hover .catalogue-line,
16 | &:focus .catalogue-line {
17 | width: 5rem;
18 | }
19 |
20 | &:last-child {
21 | border: 0;
22 | }
23 | }
24 |
25 | &-pinned {
26 | color: $default-tint;
27 | font-family: $serif-secondary;
28 | letter-spacing: .5px;
29 | }
30 |
31 | &-time {
32 | color: $default-tint;
33 | font-family: $serif-secondary;
34 | letter-spacing: .5px;
35 | }
36 |
37 | &-title {
38 | color: $default-shade;
39 | display: block;
40 | font-family: $sans-serif;
41 | font-size: 2rem;
42 | font-weight: 700;
43 | margin: .5rem 0;
44 |
45 | @media (prefers-color-scheme: dark) {
46 | color: $default-color-dark;
47 | }
48 | }
49 |
50 | &-line {
51 | @include transition(all .3s ease-out);
52 | border-top: .2rem solid $default-shade;
53 | display: block;
54 | width: 2rem;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/assets/css/_sass/code.scss:
--------------------------------------------------------------------------------
1 | @use "sass:color";
2 | @use 'variables' as *;
3 |
4 | pre,
5 | code {
6 | font-family: $monospaced;
7 | }
8 |
9 | code {
10 | background-color: $grey-3;
11 | border-radius: 3px;
12 | color: $code-color;
13 | font-size: 85%;
14 | padding: .25em .5em;
15 | @media (prefers-color-scheme: dark) {
16 | background-color: color.adjust($body-bg-dark, $lightness: 10%);
17 | }
18 | }
19 |
20 | pre {
21 | margin: 0 0 1rem;
22 | white-space: pre-wrap;
23 | }
24 |
25 | pre code {
26 | background-color: transparent;
27 | color: inherit;
28 | font-size: 100%;
29 | padding: 0;
30 | }
31 |
32 | .highlight {
33 | background-color: $grey-3;
34 | border-radius: 3px;
35 | line-height: 1.4;
36 | margin: 0 0 1rem;
37 | padding: 1rem;
38 | @media (prefers-color-scheme: dark) {
39 | background-color: color.adjust($body-bg-dark, $lightness: 10%);
40 | }
41 |
42 | pre {
43 | margin-bottom: 0;
44 | overflow-x: auto;
45 | }
46 |
47 | .lineno {
48 | color: $default-tint;
49 | display: inline-block; // Ensures the null space also isn't selectable
50 | padding: 0 .75rem 0 .25rem;
51 | // Make sure numbers aren't selectable
52 | -webkit-user-select: none;
53 | -moz-user-select: none;
54 | user-select: none;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/assets/css/_sass/footnotes.scss:
--------------------------------------------------------------------------------
1 | //_footnotes.scss
2 |
3 | @use 'variables' as *;
4 |
5 | .footnote {
6 | text-decoration: none;
7 | color: $blue;
8 | vertical-align: super;
9 | font-size: 0.75em;
10 | padding: 0 2px;
11 | }
12 |
13 | .footnote:hover {
14 | background-color: $default-shade-dark;
15 | @media (prefers-color-scheme: dark){
16 | background-color: $default-shade;
17 | }
18 | }
19 |
20 | .footnotes {
21 | margin-top: 3rem;
22 | padding-top: 1rem;
23 | }
24 |
25 | .footnotes ol {
26 | padding-left: 1.5rem;
27 | }
28 |
29 | .footnotes li {
30 | margin-bottom: 1rem;
31 | font-size: 0.9em;
32 | }
33 |
34 | .reversefootnote {
35 | text-decoration: none;
36 | color: $blue;
37 | margin-left: 0.5rem;
38 | }
39 |
40 | #footnote-popup {
41 | position: fixed;
42 | display: none;
43 | width: 300px;
44 | background-color: $body-bg;
45 | border: 1px solid $grey-2;
46 | border-radius: 4px;
47 | box-shadow: 0 2px 10px rgba(0,0,0,0.1);
48 | padding: 15px;
49 | z-index: 1000;
50 | font-size: 0.9em;
51 | line-height: 1.5;
52 |
53 | @media (prefers-color-scheme: dark) {
54 | background-color: $body-bg-dark;
55 | border: 1px solid $grey-2-dark;
56 | }
57 | }
58 |
59 | #footnote-popup .reversefootnote {
60 | visibility: hidden;
61 | position: absolute;
62 | }
63 |
--------------------------------------------------------------------------------
/assets/css/_sass/layout.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .container {
4 | margin: 0 auto;
5 | max-width: 800px;
6 | width: 80%;
7 | }
8 |
9 | main,
10 | footer,
11 | .nav-container {
12 | display: block;
13 | margin: 0 auto;
14 | max-width: 800px;
15 | width: 80%;
16 | }
17 |
18 | .nav {
19 | box-shadow: 0 2px 2px -2px $shadow-color;
20 | overflow: auto;
21 |
22 | @media (prefers-color-scheme: dark) {
23 | box-shadow: 0 2px 2px -2px $shadow-color-dark;
24 | }
25 |
26 | &-container {
27 | margin: 1rem auto;
28 | position: relative;
29 | text-align: center;
30 | }
31 |
32 | &-title {
33 | @include transition(all .2s ease-out);
34 | color: $default-color;
35 | display: inline-block;
36 | margin: 0;
37 | padding-right: .2rem;
38 |
39 | @media (prefers-color-scheme: dark) {
40 | color: $default-color-dark;
41 | }
42 |
43 | &:hover,
44 | &:focus {
45 | opacity: .6;
46 | }
47 | }
48 |
49 | ul {
50 | list-style-type: none;
51 | margin: 1rem 0 0;
52 | padding: 0;
53 | text-align: center;
54 | }
55 |
56 | li {
57 | @include transition(all .2s ease-out);
58 | color: $default-color;
59 | display: inline-block;
60 | opacity: .6;
61 | padding: 0 2rem 0 0;
62 |
63 | @media (prefers-color-scheme: dark) {
64 | color: $default-color-dark;
65 | }
66 |
67 | &:last-child {
68 | padding-right: 0;
69 | }
70 |
71 | &:hover,
72 | &:focus {
73 | opacity: 1;
74 | }
75 | }
76 |
77 | a {
78 | color: $default-color;
79 | font-family: $sans-serif;
80 |
81 | @media (prefers-color-scheme: dark) {
82 | color: $default-color-dark;
83 | }
84 | }
85 | }
86 |
87 | @media (min-width: 600px) {
88 | .nav {
89 | &-container {
90 | text-align: left;
91 | }
92 |
93 | ul {
94 | bottom: 0;
95 | position: absolute;
96 | right: 0;
97 | }
98 | }
99 | }
100 |
101 | footer {
102 | font-family: $serif-secondary;
103 | padding: 2rem 0;
104 | text-align: center;
105 |
106 | span {
107 | color: $default-color;
108 | font-size: .8rem;
109 |
110 | @media (prefers-color-scheme: dark) {
111 | color: $default-color-dark;
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/assets/css/_sass/pagination.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .pagination {
4 | border-top: .5px solid #eee;
5 | font-family: $serif-secondary;
6 | padding-top: 2rem;
7 | position: relative;
8 | text-align: center;
9 |
10 | @media (prefers-color-scheme: dark) {
11 | border-top: .5px solid #353535;
12 | }
13 |
14 | span {
15 | color: $default-shade;
16 | font-size: 1.1rem;
17 |
18 | @media (prefers-color-scheme: dark) {
19 | color: $default-tint;
20 | }
21 | }
22 |
23 | .top {
24 | @include transition(all .3s ease-out);
25 | color: $default-color;
26 | font-family: $sans-serif;
27 | font-size: 1.1rem;
28 | opacity: .6;
29 |
30 | @media (prefers-color-scheme: dark) {
31 | color: $default-color-dark;
32 | }
33 |
34 | &:hover {
35 | opacity: 1;
36 | }
37 | }
38 |
39 | .arrow {
40 | @include transition(all .3s ease-out);
41 | color: $default-color;
42 | position: absolute;
43 |
44 | @media (prefers-color-scheme: dark) {
45 | color: $default-color-dark;
46 | }
47 |
48 | &:hover,
49 | &:focus {
50 | opacity: .6;
51 | text-decoration: none;
52 | }
53 | }
54 |
55 | .left {
56 | left: 0;
57 | }
58 |
59 | .right {
60 | right: 0;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/assets/css/_sass/post.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .post {
4 | padding: 3rem 0;
5 |
6 | &-info {
7 | color: $default-tint;
8 | font-family: $serif-secondary;
9 | letter-spacing: 0.5px;
10 | text-align: center;
11 |
12 | span {
13 | font-style: italic;
14 | }
15 | }
16 |
17 | &-title {
18 | color: $default-shade;
19 | font-family: $sans-serif;
20 | font-size: 2.5rem;
21 | margin: 1rem 0;
22 | text-align: center;
23 |
24 | @media (prefers-color-scheme: dark) {
25 | color: $default-color-dark;
26 | }
27 | }
28 |
29 | &-line {
30 | border-top: 0.4rem solid $default-shade;
31 | display: block;
32 | margin: 0 auto 3rem;
33 | width: 4rem;
34 | }
35 |
36 | p {
37 | margin: 0 0 1rem;
38 | text-align: justify;
39 | }
40 |
41 | a:hover {
42 | text-decoration: underline;
43 | }
44 |
45 | img {
46 | margin: 0 auto 0.5rem;
47 | }
48 |
49 | img + em {
50 | color: $default-tint;
51 | display: block;
52 | font-family: $sans-serif;
53 | font-size: 0.9rem;
54 | font-style: normal;
55 | text-align: center;
56 | }
57 |
58 | // CSS for making emoji inline
59 | img.emoji {
60 | display: inline-block;
61 | left: 0;
62 | transform: none;
63 | width: 1rem;
64 | height: 1rem;
65 | vertical-align: text-top;
66 | padding: 0;
67 | margin: 0;
68 | }
69 | }
70 |
71 | .comments {
72 | display: flex;
73 | justify-content: center;
74 | align-items: center;
75 | margin-top: 40px;
76 | height: 80px;
77 | }
78 |
79 | .comments a {
80 | text-decoration: none;
81 | color: $blue;
82 | display: flex;
83 | align-items: center;
84 | gap: 8px;
85 | }
86 |
87 | .comments a:hover {
88 | opacity: 0.5;
89 | }
90 |
--------------------------------------------------------------------------------
/assets/css/_sass/search.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | /* Modal hidden state */
4 | #search-modal.hidden {
5 | display: none;
6 | }
7 |
8 | /* Modal positioning */
9 | #search-modal {
10 | position: fixed;
11 | inset: 0;
12 | z-index: 999;
13 | font-family: $sans-serif;
14 | }
15 |
16 | /* Overlay */
17 | .modal-overlay {
18 | position: absolute;
19 | inset: 0;
20 | background: rgba(0, 0, 0, 0.5);
21 | }
22 |
23 | /* Modal content */
24 | .modal-content {
25 | position: relative;
26 | margin: 6vh auto;
27 | max-width: 600px;
28 | padding: 1.5rem;
29 | border-radius: 0;
30 | z-index: 1000;
31 | box-shadow: 0 1px 3px $shadow-color;
32 | background: $body-bg;
33 | color: $default-color;
34 |
35 | @media (max-width: $on-phone){
36 | width: 100%;
37 | }
38 |
39 | @media (prefers-color-scheme: dark) {
40 | background: $body-bg-dark;
41 | color: $default-color-dark;
42 | box-shadow: 0 1px 3px $shadow-color-dark;
43 | }
44 |
45 | p {
46 | margin: 0 0 1rem;
47 | font-size: 0.875rem;
48 | color: $default-color;
49 |
50 | @media (prefers-color-scheme: dark) {
51 | color: $default-color-dark;
52 | }
53 | }
54 | }
55 |
56 | /* Close button */
57 | #close-search {
58 | position: absolute;
59 | right: 1rem;
60 | top: 1.25rem;
61 | background: none;
62 | border: none;
63 | font-size: 1.25rem;
64 | color: $grey-1;
65 | cursor: pointer;
66 | line-height: 1;
67 |
68 | @media (prefers-color-scheme: dark) {
69 | color: $grey-1-dark;
70 | }
71 |
72 | &:hover {
73 | color: $default-shade;
74 |
75 | @media (prefers-color-scheme: dark) {
76 | color: $default-tint-dark;
77 | }
78 | }
79 | }
80 |
81 | /* Search toggle button */
82 | #search-toggle {
83 | color: $default-color;
84 | margin-left: 2px;
85 | align-items: center;
86 | vertical-align: middle;
87 |
88 | @media (prefers-color-scheme: dark) {
89 | color: $default-color-dark;
90 | }
91 | }
92 |
93 | :root {
94 | --pagefind-ui-border-radius: 4px;
95 | --pagefind-ui-primary: #{$default-color};
96 | --pagefind-ui-text: #{$default-shade};
97 | --pagefind-ui-background: #{$body-bg};
98 | --pagefind-ui-border: #{$grey-2};
99 | --pagefind-ui-tag: #{$default-tint};
100 | }
101 |
102 | @media (prefers-color-scheme: dark) {
103 | :root {
104 | --pagefind-ui-primary: #{$default-color-dark};
105 | --pagefind-ui-text: #{$default-shade-dark};
106 | --pagefind-ui-background: #{$body-bg-dark};
107 | --pagefind-ui-border: #{$grey-2-dark};
108 | --pagefind-ui-tag: #{$default-tint-dark};
109 | }
110 | }
111 |
112 | #search input:focus {
113 | border-color: $blue;
114 | box-shadow: none;
115 | outline: none;
116 | }
117 |
--------------------------------------------------------------------------------
/assets/css/_sass/tags.scss:
--------------------------------------------------------------------------------
1 | @use 'variables' as *;
2 |
3 | .tags {
4 | &-header {
5 | &-title {
6 | color: $default-shade;
7 | font-family: $sans-serif;
8 | font-size: 2.5rem;
9 | margin: 1rem 0;
10 | text-align: center;
11 |
12 | @media (prefers-color-scheme: dark) {
13 | color: $default-color-dark;
14 | }
15 | }
16 |
17 | &-line {
18 | border-top: 0.4rem solid $default-shade;
19 | display: block;
20 | margin: 0 auto 3rem;
21 | width: 4rem;
22 | }
23 | }
24 |
25 | &-clouds {
26 | text-align: center;
27 | font-family: $sans-serif;
28 |
29 | a {
30 | display: inline-block;
31 | margin: 0 0.1rem 0.2rem;
32 | padding: 0.2rem 0.5rem;
33 | background: rgba(0, 0, 0, 0.05);
34 | border-radius: 5px;
35 | color: $default-color;
36 | text-decoration: none;
37 |
38 | @media (prefers-color-scheme: dark) {
39 | color: $default-color-dark;
40 | }
41 |
42 | &:hover,
43 | &:active {
44 | background: rgba(0, 0, 0, 0.1);
45 | }
46 | }
47 | }
48 |
49 | &-item {
50 | &-icon {
51 | height: 1rem;
52 | }
53 |
54 | &-label {
55 | display: inline-block;
56 | margin: 2rem 0 0.5rem;
57 | font-family: $sans-serif;
58 | color: $default-color;
59 |
60 | @media (prefers-color-scheme: dark) {
61 | color: $default-color-dark;
62 | }
63 | }
64 | }
65 |
66 | &-post {
67 | display: flex;
68 | justify-content: space-between;
69 | padding: 5px 0;
70 |
71 | &-title {
72 | color: $default-color;
73 |
74 | @media (prefers-color-scheme: dark) {
75 | color: $default-color-dark;
76 | }
77 |
78 | @media (max-width: $on-phone) {
79 | width: 200px;
80 | display: block;
81 | text-decoration: none;
82 | white-space: nowrap;
83 | overflow: hidden;
84 | text-overflow: ellipsis;
85 | }
86 | }
87 |
88 | &-line {
89 | @include transition(all 0.3s ease-out);
90 | border-top: 0.1rem solid $default-shade;
91 | display: block;
92 | width: 0;
93 |
94 | @media (prefers-color-scheme: dark) {
95 | border-top: 0.1rem solid $default-tint;
96 | }
97 | }
98 |
99 | &-meta {
100 | color: $default-color;
101 | text-align: right;
102 | white-space: nowrap;
103 | font-family: $monospaced;
104 |
105 | @media (prefers-color-scheme: dark) {
106 | color: $default-color-dark;
107 | }
108 | }
109 |
110 | &:hover,
111 | &:active {
112 | .tags-post-line {
113 | width: 3rem;
114 | }
115 |
116 | .tags-post-meta {
117 | color: $default-shade;
118 |
119 | @media (prefers-color-scheme: dark) {
120 | color: $default-tint;
121 | }
122 | }
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/assets/css/_sass/variables.scss:
--------------------------------------------------------------------------------
1 | @use "sass:color";
2 |
3 | // Standard
4 | $white: #fff !default;
5 | $blue: #4a9ae1 !default;
6 |
7 | // Light Colors
8 | $default-color: #555 !default;
9 | $default-shade: #353535 !default;
10 | $default-tint: #aaa !default;
11 | $grey-1: #979797 !default;
12 | $grey-2: #e5e5e5 !default;
13 | $grey-3: #f9f9f9 !default;
14 | $shadow-color: rgba(0, 0, 0, .2) !default;
15 | $code-color: #bf616a !default;
16 | $body-bg: #fff;
17 |
18 |
19 | // Dark
20 | $default-color-dark: #98a0ac;
21 | $default-shade-dark: #ccc;
22 | $default-tint-dark: #444;
23 | $grey-1-dark: #a0a0a0;
24 | $grey-2-dark: #333;
25 | $grey-3-dark: #1c1c1c;
26 | $shadow-color-dark: rgba(255, 255, 255, .2);
27 | $code-color-dark: #db1400;
28 | $body-bg-dark: #0F1216;
29 |
30 |
31 | // Fonts
32 | $serif-primary: 'Libre Baskerville', 'Times New Roman', 'Times', serif !default;
33 | $serif-secondary: 'Palatino', 'Palatino LT STD', 'Palatino Linotype', 'Book Antiqua', 'Georgia', serif !default;
34 | $sans-serif: 'Helvetica Neue', 'Segoe UI', 'Helvetica', 'Arial', sans-serif !default;
35 | $monospaced: 'IBM Plex Mono', 'Menlo', 'Monaco', monospace !default;
36 |
37 | //length variables for responsive design
38 | //only used in tags.scss for the time being. Todo: standardize useage of sizes across the theme
39 | $on-small-screen: 600px;
40 | $on-phone: 450px;
41 |
42 | @mixin box-sizing($type: border-box) {
43 | -webkit-box-sizing: $type;
44 | -moz-box-sizing: $type;
45 | box-sizing: $type;
46 | }
47 |
48 | @mixin transition($args...) {
49 | -webkit-transition: $args;
50 | -moz-transition: $args;
51 | transition: $args;
52 | }
53 |
--------------------------------------------------------------------------------
/assets/css/main.scss:
--------------------------------------------------------------------------------
1 | ---
2 | # Only the main Sass file needs front matter (the dashes are enough)
3 | ---
4 |
5 | @use 'variables' as *;
6 | @use 'base' as *;
7 | @use 'code' as *;
8 | @use 'post' as *;
9 | @use 'layout' as *;
10 | @use 'pagination' as *;
11 | @use 'catalogue' as *;
12 | @use '404' as *;
13 | @use 'tags' as *;
14 | @use 'footnotes' as *;
15 | @use 'search' as *;
16 |
--------------------------------------------------------------------------------
/assets/css/sass-code-highlight/default.scss:
--------------------------------------------------------------------------------
1 | .highlight {
2 | .hll {
3 | background-color: #ffffcc;
4 | }
5 | background: #f8f8f8;
6 | .c {
7 | color: #408080;
8 | font-style: italic;
9 | }
10 | .err {
11 | border: 1px solid #FF0000;
12 | }
13 | .k {
14 | color: #008000;
15 | font-weight: bold;
16 | }
17 | .o {
18 | color: #666666;
19 | }
20 | .cm {
21 | color: #408080;
22 | font-style: italic;
23 | }
24 | .cp {
25 | color: #BC7A00;
26 | }
27 | .c1, .cs {
28 | color: #408080;
29 | font-style: italic;
30 | }
31 | .gd {
32 | color: #A00000;
33 | }
34 | .ge {
35 | font-style: italic;
36 | }
37 | .gr {
38 | color: #FF0000;
39 | }
40 | .gh {
41 | color: #000080;
42 | font-weight: bold;
43 | }
44 | .gi {
45 | color: #00A000;
46 | }
47 | .go {
48 | color: #808080;
49 | }
50 | .gp {
51 | color: #000080;
52 | font-weight: bold;
53 | }
54 | .gs {
55 | font-weight: bold;
56 | }
57 | .gu {
58 | color: #800080;
59 | font-weight: bold;
60 | }
61 | .gt {
62 | color: #0040D0;
63 | }
64 | .kc, .kd, .kn {
65 | color: #008000;
66 | font-weight: bold;
67 | }
68 | .kp {
69 | color: #008000;
70 | }
71 | .kr {
72 | color: #008000;
73 | font-weight: bold;
74 | }
75 | .kt {
76 | color: #B00040;
77 | }
78 | .m {
79 | color: #666666;
80 | }
81 | .s {
82 | color: #BA2121;
83 | }
84 | .na {
85 | color: #7D9029;
86 | }
87 | .nb {
88 | color: #008000;
89 | }
90 | .nc {
91 | color: #0000FF;
92 | font-weight: bold;
93 | }
94 | .no {
95 | color: #880000;
96 | }
97 | .nd {
98 | color: #AA22FF;
99 | }
100 | .ni {
101 | color: #999999;
102 | font-weight: bold;
103 | }
104 | .ne {
105 | color: #D2413A;
106 | font-weight: bold;
107 | }
108 | .nf {
109 | color: #0000FF;
110 | }
111 | .nl {
112 | color: #A0A000;
113 | }
114 | .nn {
115 | color: #0000FF;
116 | font-weight: bold;
117 | }
118 | .nt {
119 | color: #008000;
120 | font-weight: bold;
121 | }
122 | .nv {
123 | color: #19177C;
124 | }
125 | .ow {
126 | color: #AA22FF;
127 | font-weight: bold;
128 | }
129 | .w {
130 | color: #bbbbbb;
131 | }
132 | .mf, .mh, .mi, .mo {
133 | color: #666666;
134 | }
135 | .sb, .sc {
136 | color: #BA2121;
137 | }
138 | .sd {
139 | color: #BA2121;
140 | font-style: italic;
141 | }
142 | .s2 {
143 | color: #BA2121;
144 | }
145 | .se {
146 | color: #BB6622;
147 | font-weight: bold;
148 | }
149 | .sh {
150 | color: #BA2121;
151 | }
152 | .si {
153 | color: #BB6688;
154 | font-weight: bold;
155 | }
156 | .sx {
157 | color: #008000;
158 | }
159 | .sr {
160 | color: #BB6688;
161 | }
162 | .s1 {
163 | color: #BA2121;
164 | }
165 | .ss {
166 | color: #19177C;
167 | }
168 | .bp {
169 | color: #008000;
170 | }
171 | .vc, .vg, .vi {
172 | color: #19177C;
173 | }
174 | .il {
175 | color: #666666;
176 | }
177 | }
--------------------------------------------------------------------------------
/assets/css/sass-code-highlight/monokai.scss:
--------------------------------------------------------------------------------
1 | $background: #272822;
2 |
3 | .highlight {
4 | background-color: $background;
5 |
6 | .hll {
7 | background-color: #49483e;
8 | }
9 | .c {
10 | color: #a8a38d;
11 | }
12 | .err {
13 | color: #960050;
14 | background-color: #1e0010;
15 | }
16 | .k {
17 | color: #66d9ef;
18 | }
19 | .l {
20 | color: #ae81ff;
21 | }
22 | .n {
23 | color: #f8f8f2;
24 | }
25 | .o {
26 | color: #f92672;
27 | }
28 | .p {
29 | color: #f8f8f2;
30 | }
31 | .cm, .cp, .c1, .cs {
32 | color: #a8a38d;
33 | }
34 | .ge {
35 | font-style: italic;
36 | }
37 | .gs {
38 | font-weight: bold;
39 | }
40 | .kc, .kd {
41 | color: #66d9ef;
42 | }
43 | .kn {
44 | color: #f92672;
45 | }
46 | .kp, .kr, .kt {
47 | color: #66d9ef;
48 | }
49 | .ld {
50 | color: #e6db74;
51 | }
52 | .m {
53 | color: #ae81ff;
54 | }
55 | .s {
56 | color: #e6db74;
57 | }
58 | .na {
59 | color: #a6e22e;
60 | }
61 | .nb {
62 | color: #f8f8f2;
63 | }
64 | .nc {
65 | color: #a6e22e;
66 | }
67 | .no {
68 | color: #66d9ef;
69 | }
70 | .nd {
71 | color: #a6e22e;
72 | }
73 | .ni {
74 | color: #f8f8f2;
75 | }
76 | .ne, .nf {
77 | color: #a6e22e;
78 | }
79 | .nl, .nn {
80 | color: #f8f8f2;
81 | }
82 | .nx {
83 | color: #a6e22e;
84 | }
85 | .py {
86 | color: #f8f8f2;
87 | }
88 | .nt {
89 | color: #f92672;
90 | }
91 | .nv {
92 | color: #f8f8f2;
93 | }
94 | .ow {
95 | color: #f92672;
96 | }
97 | .w {
98 | color: #f8f8f2;
99 | }
100 | .mf, .mh, .mi, .mo {
101 | color: #ae81ff;
102 | }
103 | .sb, .sc, .sd, .s2 {
104 | color: #e6db74;
105 | }
106 | .se {
107 | color: #ae81ff;
108 | }
109 | .sh, .si, .sx, .sr, .s1, .ss {
110 | color: #e6db74;
111 | }
112 | .bp, .vc, .vg, .vi {
113 | color: #f8f8f2;
114 | }
115 | .il {
116 | color: #ae81ff;
117 | }
118 | .gh {}
119 | .gu {
120 | color: #75715e;
121 | }
122 | .gd {
123 | color: #f92672;
124 | }
125 | .gi {
126 | color: #a6e22e;
127 | }
128 | }
129 |
130 | code{
131 | background-color: "#282C34";
132 | color: whitesmoke;}
133 | pre {
134 | background-color: "#282C34";
135 | color: whitesmoke;
136 | }
137 |
--------------------------------------------------------------------------------
/assets/css/syntax-default.scss:
--------------------------------------------------------------------------------
1 | ---
2 | # Front matter for Jekyll processing
3 | ---
4 |
5 | @use 'variables' as *;
6 | @use "sass-code-highlight/default";
7 |
--------------------------------------------------------------------------------
/assets/css/syntax-monokai.scss:
--------------------------------------------------------------------------------
1 | ---
2 | # Front matter for Jekyll processing
3 | ---
4 |
5 | @use 'variables' as *;
6 | @use "sass-code-highlight/monokai";
7 |
--------------------------------------------------------------------------------
/assets/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitinnair1/tail/0c2dbfca6315712f491b4454e74689c13c32641d/assets/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitinnair1/tail/0c2dbfca6315712f491b4454e74689c13c32641d/assets/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/assets/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitinnair1/tail/0c2dbfca6315712f491b4454e74689c13c32641d/assets/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/js/disqusLoader.js:
--------------------------------------------------------------------------------
1 | /*
2 | disqusLoader.js v1.0
3 | A JavaScript plugin for lazy-loading Disqus comments widget.
4 | -
5 | By Osvaldas Valutis, www.osvaldas.info
6 | Available for use under the MIT License
7 | */
8 | (function(b,f,l){var r=function(a){a=a.getBoundingClientRect();return{top:a.top+f.body.scrollTop,left:a.left+f.body.scrollLeft}},t=function(a,c){var d=f.createElement("script");d.src=a;d.async=!0;d.setAttribute("data-timestamp",+new Date);d.addEventListener("load",function(){"function"===typeof c&&c()});(f.head||f.body).appendChild(d)};l=function(a,c){var d,e;return function(){var g=this,f=arguments,b=+new Date;d&&bb.innerHeight*n||0 {
27 | // Clean up any existing event listeners
28 | const newLink = link.cloneNode(true);
29 | link.parentNode.replaceChild(newLink, link);
30 |
31 | // Add new event listeners
32 | newLink.addEventListener('click', handleFootnoteClick);
33 | newLink.addEventListener('mouseenter', handleFootnoteHover);
34 | newLink.addEventListener('mouseleave', handleFootnoteLeave);
35 | });
36 |
37 | // Event handlers
38 | function handleFootnoteClick(event) {
39 | event.preventDefault();
40 |
41 | const footnoteLink = event.currentTarget;
42 | if (currentFootnote === footnoteLink && isPopupVisible) {
43 | hidePopup();
44 | } else {
45 | showPopup(footnoteLink);
46 | }
47 | }
48 |
49 | function handleFootnoteHover(event) {
50 | // Clear any existing timers
51 | if (hoverTimer) clearTimeout(hoverTimer);
52 |
53 | // Show the popup
54 | showPopup(event.currentTarget);
55 | }
56 |
57 | function handleFootnoteLeave() {
58 | // Delay hiding to allow moving to popup
59 | if (hoverTimer) clearTimeout(hoverTimer);
60 | hoverTimer = setTimeout(() => {
61 | if (!isMouseOverElement(popup)) {
62 | hidePopup();
63 | }
64 | }, 200);
65 | }
66 |
67 | // Show popup with content from footnote
68 | function showPopup(footnoteLink) {
69 | // First, record which footnote we're showing
70 | currentFootnote = footnoteLink;
71 |
72 | // Get the target footnote content (without displaying yet)
73 | const footnoteId = footnoteLink.getAttribute('href').substring(1);
74 | const footnoteContent = document.getElementById(footnoteId);
75 | if (!footnoteContent) return;
76 |
77 | // Prepare popup content by extracting only the text content
78 | // This avoids the backlink issue completely
79 | let content = '';
80 |
81 | // Clone all child nodes except the backlink
82 | const children = footnoteContent.childNodes;
83 | for (let i = 0; i < children.length; i++) {
84 | const child = children[i];
85 | // Skip the backlink element
86 | if (child.nodeType === 1 && child.classList && child.classList.contains('reversefootnote')) {
87 | continue;
88 | }
89 | // Clone the node (whether it's text or element)
90 | content += child.nodeType === 3 ? child.textContent : child.outerHTML;
91 | }
92 |
93 | // Apply content to the hidden popup
94 | popup.style.display = 'none';
95 | popup.innerHTML = content;
96 |
97 | // Position popup while still hidden
98 | const linkRect = footnoteLink.getBoundingClientRect();
99 | positionPopup(linkRect);
100 |
101 | // Now show the fully prepared popup
102 | popup.style.display = 'block';
103 | isPopupVisible = true;
104 |
105 | // Add popup event listeners
106 | popup.addEventListener('mouseleave', handlePopupLeave);
107 | }
108 |
109 | // Hide the popup
110 | function hidePopup() {
111 | popup.style.display = 'none';
112 | isPopupVisible = false;
113 | currentFootnote = null;
114 |
115 | // Remove popup event listeners to prevent memory leaks
116 | popup.removeEventListener('mouseleave', handlePopupLeave);
117 | }
118 |
119 | function handlePopupLeave() {
120 | hidePopup();
121 | }
122 |
123 | // Position popup optimally based on viewport
124 | function positionPopup(linkRect) {
125 | const viewportWidth = window.innerWidth;
126 | const viewportHeight = window.innerHeight;
127 |
128 | // Get popup dimensions
129 | // We need to temporarily make it visible but off-screen to measure it
130 | popup.style.visibility = 'hidden';
131 | popup.style.display = 'block';
132 | popup.style.left = '-9999px';
133 | popup.style.top = '-9999px';
134 |
135 | const popupWidth = popup.offsetWidth;
136 | const popupHeight = popup.offsetHeight;
137 |
138 | // Calculate optimal position
139 | const linkCenter = linkRect.left + (linkRect.width / 2);
140 | let left = linkCenter - (popupWidth / 2);
141 | let top = linkRect.bottom + 10;
142 |
143 | // Adjust if popup would go off screen
144 | if (left + popupWidth > viewportWidth - 20) {
145 | left = viewportWidth - popupWidth - 20;
146 | }
147 | if (left < 20) {
148 | left = 20;
149 | }
150 |
151 | // If popup would go below viewport, position it above the link
152 | if (top + popupHeight > viewportHeight - 20) {
153 | top = linkRect.top - popupHeight - 10;
154 |
155 | // If it would now go above the viewport, just position it at the top
156 | if (top < 20) {
157 | top = 20;
158 | }
159 | }
160 |
161 | // Apply position
162 | popup.style.left = `${left}px`;
163 | popup.style.top = `${top}px`;
164 | popup.style.visibility = 'visible';
165 | }
166 |
167 | // Helper to detect if mouse is over an element
168 | function isMouseOverElement(el) {
169 | return el.matches(':hover');
170 | }
171 |
172 | // Close popup when clicking outside
173 | document.addEventListener('pointerdown', (e) => {
174 | if (isPopupVisible &&
175 | !e.target.closest('.footnote') &&
176 | !e.target.closest('#footnote-popup')) {
177 | hidePopup();
178 | }
179 | });
180 |
181 | // Update position if window resizes
182 | window.addEventListener('resize', () => {
183 | if (currentFootnote && isPopupVisible) {
184 | const linkRect = currentFootnote.getBoundingClientRect();
185 | positionPopup(linkRect);
186 | }
187 | });
188 | });
189 |
--------------------------------------------------------------------------------
/assets/js/pagefind.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("DOMContentLoaded", () => {
2 | new PagefindUI({
3 | element: "#search",
4 | showImages: false
5 | });
6 |
7 | const modal = document.getElementById("search-modal");
8 | const toggle = document.getElementById("search-toggle");
9 | const close = document.getElementById("close-search");
10 | const overlay = modal.querySelector(".modal-overlay");
11 |
12 | function toggleModal() {
13 | const isHidden = modal.classList.contains("hidden");
14 | modal.classList.toggle("hidden");
15 | if (isHidden) {
16 | modal.querySelector("input").focus(); // Focus input when opening
17 | }
18 | }
19 |
20 | // Toggle modal on button click
21 | toggle.addEventListener("click", (e) => {
22 | e.preventDefault();
23 | toggleModal();
24 | });
25 |
26 | // Close modal on close button click
27 | close.addEventListener("click", toggleModal);
28 |
29 | // Close modal on overlay click
30 | overlay.addEventListener("click", toggleModal);
31 |
32 | // Toggle modal on Cmd/Ctrl + K, close on Escape
33 | document.addEventListener("keydown", (e) => {
34 | if ((e.metaKey || e.ctrlKey) && e.key === "k") {
35 | e.preventDefault();
36 | toggleModal();
37 | } else if (e.key === "Escape") {
38 | modal.classList.add("hidden");
39 | }
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jitinnair1/tail/0c2dbfca6315712f491b4454e74689c13c32641d/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | title: Home
4 | ---
5 |
--------------------------------------------------------------------------------