klise 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7b9e5a649c9061c3bc5b95f9ec5e35d3b64142f2b5e2e9ed1c877a9dc9e3d34
4
- data.tar.gz: 1fdb701edc67a471a1200c2429f9e4bcc0264c9e22d5193f609fef06e7fb307f
3
+ metadata.gz: dc8b52939500630b7e1e4fc48e93f7b3aecf28e824234b79717c7f6fa6e31120
4
+ data.tar.gz: 31d160ae8043ee92676cc2a33bb02e3038cfd2861af1b8e7cd52947ba5318774
5
5
  SHA512:
6
- metadata.gz: 3980a7ee19dae324802266181ffddc007cc6dd94b971f12bcb33e925e28a87448d5b3db726219401da37cdb48350fdec3e3451a8e713116016b778296debcf2d
7
- data.tar.gz: 9a6cf5513628c915d4e9eea29b0a0f99ebb9e637b5ca92cd4fce1b14ea60777d1e9d66d9d9674a4af4de232b1b453664e9ea4dd1b0fee97b369f38f019204956
6
+ metadata.gz: 67c5dc052e8feb591e3127f03adbb2053a3c7fd402c62a507c6c37a43e9ee68de76c9b79d4ec2d12c7a69d7bcf6e9991c4307e43eeabb63f0b0ccb5b8603f22a
7
+ data.tar.gz: 0d7bf3fbd21f4c1019f4e74047a9281e8be649e323998d10c78b40b329aae25653079f6aebe6abcde1a945bc7f60c069b65c2cfde6a323b6f96fcc35182e9ccf
data/README.md CHANGED
@@ -5,15 +5,20 @@ For demo <a href="https://klise.now.sh" target="_blank" rel="noopener">klise.now
5
5
 
6
6
  ## Features
7
7
 
8
- - [x] Light & Dark Mode support :waxing_crescent_moon:
8
+ - [x] Light & Dark Mode support, follows OS preference by default :waxing_crescent_moon:
9
9
  - [x] Customizable (using `.scss`)
10
10
  - [x] Responsive (desktop, tab and mobile)
11
+ - [x] RTL / LTR text direction support
11
12
  - [x] Mobile First Design
12
13
  - [x] SEO Optimized
14
+ - [x] schema.org structured data (JSON-LD)
13
15
  - [x] Images of post Organized ([`jekyll-postfiles`](https://github.com/nhoizey/jekyll-postfiles))
16
+ - [x] Image lightbox (click an image in a post to zoom, no dependencies)
17
+ - [x] Image gallery (`<div class="gallery">`) — neat responsive rows of images
14
18
  - [x] Generate Sitemap ([`jekyll-sitemap`](https://github.com/jekyll/jekyll-sitemap))
15
19
  - [x] RSS Feed ([`jekyll-feed`](https://github.com/jekyll/jekyll-feed))
16
20
  - [x] Syntax Highlighter ([`rouge`](https://github.com/rouge-ruby/rouge))
21
+ - [x] Table of contents, opt-in per post with `toc: true`
17
22
  - [x] Next & Previous Post
18
23
  - [x] Comment layout, enable in frontmatter if you wish
19
24
  - [x] Google analytics
@@ -23,37 +28,59 @@ For demo <a href="https://klise.now.sh" target="_blank" rel="noopener">klise.now
23
28
 
24
29
  ![Lighthouse Result](./lighthouse.png)
25
30
 
26
- ## Backlogs
27
-
28
- - [ ] Intergrated with PhotoSwipe.
29
- - [ ] Add schema.org meta information.
30
- - [ ] Transform class selector to BEM metodology.
31
-
32
31
  ## Installation
33
32
 
33
+ Requires **Ruby >= 3.0** and **Jekyll ~> 4.4**.
34
+
34
35
  Run local server:
35
36
 
36
37
  ```bash
37
- git clone https://github.com/piharpi/jekyll-klise.git
38
- cd jekyll-klise
39
- bundle install
40
- bundle exec jekyll serve
38
+ $ git clone https://github.com/piharpi/jekyll-klise.git
39
+ $ cd jekyll-klise
40
+ $ bundle install
41
+ $ bundle exec jekyll serve
41
42
  ```
42
43
 
43
44
  Navigate to `localhost:4000`. You're Welcome, Fork and be Stargazer.
44
45
 
45
- [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/piharpi/jekyll-klise) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/piharpi/jekyll-klise)
46
+ [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/piharpi/jekyll-klise) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/piharpi/jekyll-klise) [![Deploy to Azure button](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/?feature.customportal=false&WT.mc_id=javascript-0000-jopapa#create/Microsoft.StaticApp)
47
+
48
+ ## Table of contents
49
+
50
+ Add `toc: true` to a post's front matter to render an automatic table of
51
+ contents (built from its `h2`/`h3` headings) above the article:
52
+
53
+ ```yaml
54
+ ---
55
+ title: My post
56
+ toc: true
57
+ ---
58
+ ```
59
+
60
+ ## Text direction (RTL)
61
+
62
+ Set the site-wide direction in `_config.yml`:
63
+
64
+ ```yaml
65
+ direction: rtl # "ltr" (default) | "rtl"
66
+ ```
67
+
68
+ You can override it per page/post in the front matter with `direction: rtl`.
69
+ Post content also resolves its direction per paragraph, so an LTR site can
70
+ host individual RTL posts (and vice versa) without extra markup.
46
71
 
47
72
  ## Limitation
48
73
 
49
- - Since [`jekyll-postfiles`](https://github.com/nhoizey/jekyll-postfiles#compatibility) plugin isn't supported by github pages, this cause will make your site problems, path broken or post images won't show up, you can host alternatively using likes [netlify.com](https://netlify.com), [vercel.com](https://vercel.com) or [surge.sh](https://surge.sh) services, which support 3rd party.
74
+ - Since [`jekyll-postfiles`](https://github.com/nhoizey/jekyll-postfiles#compatibility) plugin isn't supported by github pages, this cause will make your site problems, path broken or post images won't show up, you can host alternatively using likes [netlify.com](https://netlify.com), [vercel.com](https://vercel.com), [azure.com](https://docs.microsoft.com/azure/static-web-apps/publish-jekyll) or [surge.sh](https://surge.sh) services, which support 3rd party.
50
75
 
51
76
  ## Contributing
52
77
 
53
78
  If you see any typos or formatting errors in a post, or want to helping reduce backlogs or any other issue that needs to be addressed, please do not hesitate to open a pull request and fix it!, please read [contributing](./CONTRIBUTING.md) before PR.
54
79
 
55
80
  Yeaaa feel free to open a pull request.
81
+ ## Support
56
82
 
83
+ [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/W7W7MJ4X) [![saweria.co](./assets/img/saweria-button.png)](https://saweria.co/piharpi)
57
84
  ## License
58
85
 
59
86
  This project is open source and available under the [MIT License](LICENSE).
@@ -18,12 +18,20 @@
18
18
  </small>
19
19
  </footer>
20
20
  <script src="/assets/js/main.js" defer="defer"></script>
21
-
22
- {%- if page.google_analytics -%}
23
- <script src="/assets/js/galite.js"></script>
21
+ {%- if site.google_analytics -%}
22
+ <!-- Google tag (gtag.js) -->
23
+ <script
24
+ async
25
+ src="https://www.googletagmanager.com/gtag/js?id={{ site.google_analytics }}"
26
+ ></script>
24
27
  <script>
25
- var galite = galite || {};
26
- galite.UA = "{{ site.google_analytics }}";
28
+ window.dataLayer = window.dataLayer || [];
29
+ function gtag() {
30
+ dataLayer.push(arguments);
31
+ }
32
+ gtag("js", new Date());
33
+
34
+ gtag("config", "{{ site.google_analytics }}");
27
35
  </script>
28
36
  {%- endif -%}
29
37
  {%- if page.url == '/archive/' -%}
@@ -59,13 +59,13 @@
59
59
  <meta
60
60
  property="og:site_name"
61
61
  content="{% if page.title %}{{ page.title | escape }} | {{
62
- site.author.facebook
62
+ site.author.name
63
63
  }}{% else %}{{ site.title | escape }}{% endif %}"
64
64
  />
65
65
  <meta
66
66
  property="og:title"
67
67
  content="{% if page.title %}{{ page.title | escape }} | {{
68
- site.author.facebook
68
+ site.author.name
69
69
  }}{% else %}{{ site.title | escape }}{% endif %}"
70
70
  />
71
71
  {% if page.location %}
@@ -161,4 +161,17 @@
161
161
  <meta name="theme-color" content="#2c2c2c" />
162
162
 
163
163
  <link rel="stylesheet" href="/assets/css/style.css" />
164
+
165
+ <!-- for mathjax support -->
166
+ {% if page.usemathjax %}
167
+ <script type="text/x-mathjax-config">
168
+ MathJax.Hub.Config({
169
+ TeX: { equationNumbers: { autoNumber: "AMS" } }
170
+ });
171
+ </script>
172
+ <script type="text/javascript" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js?config=TeX-AMS-MML_HTMLorMML"></script>
173
+ {% endif %}
174
+
175
+ {% include schema.html %}
176
+
164
177
  </head>
@@ -0,0 +1,56 @@
1
+ {%- comment -%}
2
+ schema.org structured data (JSON-LD).
3
+ Emits BlogPosting for posts and WebSite for every other page.
4
+ {%- endcomment -%}
5
+ {%- capture description -%}
6
+ {{ page.description | default: page.excerpt | default: site.description | strip_html | normalize_whitespace | truncate: 200 }}
7
+ {%- endcapture -%}
8
+ {%- assign description = description | strip -%}
9
+ {%- if page.image -%}
10
+ {%- assign page_image = page.image | absolute_url -%}
11
+ {%- else -%}
12
+ {%- assign page_image = site.image | absolute_url -%}
13
+ {%- endif -%}
14
+
15
+ <script type="application/ld+json">
16
+ {%- if page.layout == 'post' -%}
17
+ {
18
+ "@context": "https://schema.org",
19
+ "@type": "BlogPosting",
20
+ "mainEntityOfPage": {
21
+ "@type": "WebPage",
22
+ "@id": {{ page.url | absolute_url | jsonify }}
23
+ },
24
+ "headline": {{ page.title | jsonify }},
25
+ "description": {{ description | jsonify }},
26
+ "image": {{ page_image | jsonify }},
27
+ "datePublished": {{ page.date | date_to_xmlschema | jsonify }},
28
+ "dateModified": {{ page.modified | default: page.date | date_to_xmlschema | jsonify }},
29
+ "author": {
30
+ "@type": "Person",
31
+ "name": {{ site.author.name | jsonify }},
32
+ "url": {{ site.url | append: site.baseurl | jsonify }}
33
+ },
34
+ "publisher": {
35
+ "@type": "Organization",
36
+ "name": {{ site.author.name | jsonify }},
37
+ "logo": {
38
+ "@type": "ImageObject",
39
+ "url": {{ site.author.avatar | default: site.image | absolute_url | jsonify }}
40
+ }
41
+ }
42
+ }
43
+ {%- else -%}
44
+ {
45
+ "@context": "https://schema.org",
46
+ "@type": "WebSite",
47
+ "name": {{ site.title | jsonify }},
48
+ "url": {{ site.url | append: site.baseurl | jsonify }},
49
+ "description": {{ description | jsonify }},
50
+ "author": {
51
+ "@type": "Person",
52
+ "name": {{ site.author.name | jsonify }}
53
+ }
54
+ }
55
+ {%- endif -%}
56
+ </script>
@@ -0,0 +1,34 @@
1
+ {% comment %}
2
+ Builds a table of contents from the h2/h3 headings in the given HTML.
3
+
4
+ Usage:
5
+ {% include toc.html html=content %}
6
+
7
+ Optional parameters:
8
+ * title (string) : "Table of Contents" - heading shown above the list
9
+ {% endcomment %}
10
+
11
+ {%- assign toc_nodes = include.html | split: '<h' -%}
12
+ {%- capture toc_items -%}
13
+ {%- for node in toc_nodes -%}
14
+ {%- assign level = node | slice: 0, 1 -%}
15
+ {%- if level == '2' or level == '3' -%}
16
+ {%- assign heading = node | split: '</h' | first -%}
17
+ {%- if heading contains 'id="' -%}
18
+ {%- assign id = heading | split: 'id="' | last | split: '"' | first -%}
19
+ {%- assign text = heading | split: '>' | last | strip_html | strip -%}
20
+ <li class="toc-item toc-h{{ level }}"><a href="#{{ id }}">{{ text }}</a></li>
21
+ {%- endif -%}
22
+ {%- endif -%}
23
+ {%- endfor -%}
24
+ {%- endcapture -%}
25
+ {%- assign toc_items = toc_items | strip -%}
26
+
27
+ {%- if toc_items != '' -%}
28
+ <nav class="toc" aria-label="Table of contents">
29
+ <p class="toc-title">{{ include.title | default: 'Table of Contents' }}</p>
30
+ <ul class="toc-list">
31
+ {{ toc_items }}
32
+ </ul>
33
+ </nav>
34
+ {%- endif -%}
data/_layouts/404.html CHANGED
@@ -3,10 +3,32 @@ layout: compress
3
3
  ---
4
4
 
5
5
  <!DOCTYPE html>
6
- <html lang="{{ page.lang | default: site.lang | default: " en " }}">
6
+ <html lang="{{ page.lang | default: site.lang | default: " en " }}" dir="{{ page.direction | default: site.direction | default: 'ltr' }}">
7
7
 
8
8
  {% include header.html %}
9
- <body>
9
+ <body data-theme="{{ site.mode }}" class="notransition">
10
+
11
+ <script>
12
+ const body = document.body;
13
+
14
+ const initTheme = (state) => {
15
+ if (state === "dark") {
16
+ body.setAttribute("data-theme", "dark");
17
+ } else if (state === "light") {
18
+ body.removeAttribute("data-theme");
19
+ } else {
20
+ // No stored preference yet: follow the OS color scheme.
21
+ window.matchMedia("(prefers-color-scheme: dark)").matches
22
+ ? body.setAttribute("data-theme", "dark")
23
+ : body.removeAttribute("data-theme");
24
+ }
25
+ };
26
+
27
+ initTheme(localStorage.getItem("theme"));
28
+
29
+ setTimeout(() => body.classList.remove("notransition"), 75);
30
+ </script>
31
+
10
32
  {% include navbar.html %}
11
33
  <div class="wrapper">
12
34
  <main aria-label="Content">
@@ -2,10 +2,32 @@
2
2
  layout: compress
3
3
  ---
4
4
  <!DOCTYPE html>
5
- <html lang="{{ page.lang | default: site.lang | default: " en " }}">
5
+ <html lang="{{ page.lang | default: site.lang | default: " en " }}" dir="{{ page.direction | default: site.direction | default: 'ltr' }}">
6
6
  {% include header.html %}
7
7
 
8
- <body data-theme="{{ site.mode }}">
8
+ <body data-theme="{{ site.mode }}" class="notransition">
9
+
10
+ <script>
11
+ const body = document.body;
12
+
13
+ const initTheme = (state) => {
14
+ if (state === "dark") {
15
+ body.setAttribute("data-theme", "dark");
16
+ } else if (state === "light") {
17
+ body.removeAttribute("data-theme");
18
+ } else {
19
+ // No stored preference yet: follow the OS color scheme.
20
+ window.matchMedia("(prefers-color-scheme: dark)").matches
21
+ ? body.setAttribute("data-theme", "dark")
22
+ : body.removeAttribute("data-theme");
23
+ }
24
+ };
25
+
26
+ initTheme(localStorage.getItem("theme"));
27
+
28
+ setTimeout(() => body.classList.remove("notransition"), 75);
29
+ </script>
30
+
9
31
  {% include navbar.html %}
10
32
  <div class="wrapper">
11
33
  {% include author.html %}
data/_layouts/page.html CHANGED
@@ -3,11 +3,33 @@ layout: compress
3
3
  ---
4
4
 
5
5
  <!DOCTYPE html>
6
- <html lang="{{ page.lang | default: site.lang | default: " en " }}">
6
+ <html lang="{{ page.lang | default: site.lang | default: " en " }}" dir="{{ page.direction | default: site.direction | default: 'ltr' }}">
7
7
 
8
8
  {% include header.html %}
9
9
 
10
- <body data-theme="{{ site.mode }}">
10
+ <body data-theme="{{ site.mode }}" class="notransition">
11
+
12
+ <script>
13
+ const body = document.body;
14
+
15
+ const initTheme = (state) => {
16
+ if (state === "dark") {
17
+ body.setAttribute("data-theme", "dark");
18
+ } else if (state === "light") {
19
+ body.removeAttribute("data-theme");
20
+ } else {
21
+ // No stored preference yet: follow the OS color scheme.
22
+ window.matchMedia("(prefers-color-scheme: dark)").matches
23
+ ? body.setAttribute("data-theme", "dark")
24
+ : body.removeAttribute("data-theme");
25
+ }
26
+ };
27
+
28
+ initTheme(localStorage.getItem("theme"));
29
+
30
+ setTimeout(() => body.classList.remove("notransition"), 75);
31
+ </script>
32
+
11
33
  {% include navbar.html %}
12
34
  <div class="wrapper">
13
35
  <header class="header">
data/_layouts/post.html CHANGED
@@ -3,11 +3,33 @@ layout: compress
3
3
  ---
4
4
 
5
5
  <!DOCTYPE html>
6
- <html lang="{{ page.lang | default: site.lang | default: "en" }}">
6
+ <html lang="{{ page.lang | default: site.lang | default: "en" }}" dir="{{ page.direction | default: site.direction | default: 'ltr' }}">
7
7
 
8
8
  {% include header.html %}
9
9
 
10
- <body data-theme="{{ site.mode }}">
10
+ <body data-theme="{{ site.mode }}" class="notransition">
11
+
12
+ <script>
13
+ const body = document.body;
14
+
15
+ const initTheme = (state) => {
16
+ if (state === "dark") {
17
+ body.setAttribute("data-theme", "dark");
18
+ } else if (state === "light") {
19
+ body.removeAttribute("data-theme");
20
+ } else {
21
+ // No stored preference yet: follow the OS color scheme.
22
+ window.matchMedia("(prefers-color-scheme: dark)").matches
23
+ ? body.setAttribute("data-theme", "dark")
24
+ : body.removeAttribute("data-theme");
25
+ }
26
+ };
27
+
28
+ initTheme(localStorage.getItem("theme"));
29
+
30
+ setTimeout(() => body.classList.remove("notransition"), 75);
31
+ </script>
32
+
11
33
  {% include navbar.html %}
12
34
  <div class="wrapper post">
13
35
  <main class="page-content" aria-label="Content">
@@ -44,6 +66,10 @@ layout: compress
44
66
  {% endif %}
45
67
  </header>
46
68
 
69
+ {% if page.toc %}
70
+ {% include toc.html html=content %}
71
+ {% endif %}
72
+
47
73
  <div class="page-content" itemprop="articleBody">
48
74
  {% include anchor_headings.html html=content anchorClass="anchor-head" beforeHeading=true h_min=1 h_max=4 %}
49
75
  {% if page.tweet %}
@@ -55,7 +81,7 @@ layout: compress
55
81
  </article>
56
82
 
57
83
  {% if page.comments %}
58
- {% include comments.html%}
84
+ {% include comments.html %}
59
85
  {% endif %}
60
86
 
61
87
  </main>
@@ -71,4 +97,4 @@ layout: compress
71
97
  </div>
72
98
  </body>
73
99
 
74
- </html>
100
+ </html>
@@ -7,9 +7,28 @@
7
7
  transition: background-color 75ms ease-in, border-color 75ms ease-in;
8
8
  }
9
9
 
10
+ .notransition {
11
+ -webkit-transition: none;
12
+ -moz-transition: none;
13
+ -ms-transition: none;
14
+ -o-transition: none;
15
+ transition: none;
16
+ }
17
+
10
18
  html {
11
19
  overflow-x: hidden;
20
+ overflow-y: scroll;
12
21
  width: 100%;
22
+ // Give the canvas its own background. Safari does not always propagate
23
+ // the body background to the canvas when the body has `overflow-x:
24
+ // hidden`, which left the area below short pages blank (#45).
25
+ background-color: $white;
26
+ }
27
+
28
+ @media (prefers-color-scheme: dark) {
29
+ html {
30
+ background-color: $dark-black;
31
+ }
13
32
  }
14
33
 
15
34
  body,
@@ -139,7 +158,7 @@ ul {
139
158
  top: -1px;
140
159
  width: 1.2em;
141
160
  position: relative;
142
- margin-left: -1.3em;
161
+ margin-inline-start: -1.3em;
143
162
  font-weight: 700;
144
163
  }
145
164
  }
@@ -154,10 +173,9 @@ ol {
154
173
  content: counter(li);
155
174
  display: inline-block;
156
175
  width: 1em;
157
- margin-right: 0.5em;
158
- margin-left: -1.6em;
159
- text-align: right;
160
- direction: rtl;
176
+ margin-inline-end: 0.5em;
177
+ margin-inline-start: -1.6em;
178
+ text-align: end;
161
179
  font-weight: $bold-weight;
162
180
  font-size: $small-font-size;
163
181
  }
@@ -167,7 +185,7 @@ ol {
167
185
  ul,
168
186
  ol {
169
187
  margin-top: 0;
170
- margin-left: $spacing-full;
188
+ margin-inline-start: $spacing-full;
171
189
  }
172
190
 
173
191
  li {
@@ -234,12 +234,31 @@ body[data-theme="dark"] {
234
234
  .solution:hover {
235
235
  color: $dark-text-link-blue-active;
236
236
  }
237
- }
237
+ }
238
238
 
239
239
  .search-article {
240
240
  input[type="search"] {
241
241
  color: $dark-text-base-color;
242
+ &::-webkit-input-placeholder {
243
+ color: rgba(128,128,128,0.8);
244
+ }
242
245
  }
243
246
  }
244
247
 
248
+ // Table of contents
249
+ .toc {
250
+ border-color: $dark-light;
251
+
252
+ .toc-title {
253
+ color: $dark-gray;
254
+ }
255
+
256
+ .toc-item a {
257
+ color: $dark-text-base-color;
258
+
259
+ &:hover {
260
+ color: $dark-text-link-blue;
261
+ }
262
+ }
263
+ }
245
264
  }
@@ -710,3 +710,17 @@
710
710
  U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
711
711
  U+FEFF, U+FFFD;
712
712
  }
713
+
714
+ // Arabic / Farsi subset (Vazirmatn) mapped onto the Roboto family so RTL
715
+ // content renders with proper glyphs. Credit: @srezasm (PR #94).
716
+ @font-face {
717
+ font-family: "Roboto";
718
+ font-style: normal;
719
+ font-weight: 100 900;
720
+ font-display: swap;
721
+ src: url(https://fonts.gstatic.com/s/vazirmatn/v13/Dxxo8j6PP2D_kU2muijlGMWWMmk.woff2)
722
+ format("woff2");
723
+ unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891,
724
+ U+0898-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41,
725
+ U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
726
+ }
@@ -338,18 +338,27 @@
338
338
  }
339
339
 
340
340
  input[type="search"] {
341
- border: 0;
342
341
  top: 0;
343
342
  left: 0;
344
- position: absolute;
343
+ border: 0;
345
344
  width: 100%;
345
+ height: 40px;
346
+ outline: none;
347
+ position: absolute;
346
348
  border-radius: 5px;
347
349
  padding: 10px 10px 10px 35px;
348
- font-size: $base-font-size;
349
350
  color: $text-base-color;
351
+ -webkit-appearance: none;
352
+ font-size: $base-font-size;
350
353
  background-color: rgba(128, 128, 128, 0.1);
351
354
  border: 1px solid rgba(128, 128, 128, 0.1);
352
- outline: none;
355
+ &::-webkit-input-placeholder {
356
+ color: #808080;
357
+ }
358
+ &::-webkit-search-decoration,
359
+ &::-webkit-search-results-decoration {
360
+ display: none;
361
+ }
353
362
  }
354
363
  }
355
364
 
@@ -368,4 +377,42 @@
368
377
  border: 1px solid rgba(128, 128, 128, 0.1);
369
378
  background-color: rgba(128, 128, 128, 0.1);
370
379
  }
380
+ }
381
+
382
+ // RTL adjustments: floats and relative offsets do not mirror on their
383
+ // own, so flip the pieces that are positioned by physical side.
384
+ [dir="rtl"] {
385
+ .menu {
386
+ a#mode {
387
+ float: right;
388
+ left: auto;
389
+ right: 8px;
390
+ }
391
+
392
+ .trigger {
393
+ float: left;
394
+ }
395
+
396
+ .menu-link:not(:last-child) {
397
+ margin-right: 0;
398
+ margin-left: 5px;
399
+ }
400
+
401
+ @include media-query($on-mobile) {
402
+ a#mode {
403
+ left: auto;
404
+ right: 10px;
405
+ }
406
+
407
+ .menu-icon {
408
+ right: auto;
409
+ left: 0;
410
+ }
411
+ }
412
+ }
413
+
414
+ .post-item .post-item-date {
415
+ padding-right: 0;
416
+ padding-left: 10px;
417
+ }
371
418
  }
@@ -39,3 +39,48 @@
39
39
  position: absolute;
40
40
  }
41
41
  }
42
+
43
+ // Image lightbox
44
+ .page-content img.zoomable {
45
+ cursor: zoom-in;
46
+ }
47
+
48
+ body.lightbox-open {
49
+ overflow: hidden;
50
+ }
51
+
52
+ .lightbox-overlay {
53
+ position: fixed;
54
+ top: 0;
55
+ right: 0;
56
+ bottom: 0;
57
+ left: 0;
58
+ z-index: 100;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ padding: 20px;
63
+ cursor: zoom-out;
64
+ background-color: rgba(0, 0, 0, 0.85);
65
+ opacity: 0;
66
+ visibility: hidden;
67
+ transition: opacity 0.2s ease, visibility 0.2s ease;
68
+
69
+ &.is-open {
70
+ opacity: 1;
71
+ visibility: visible;
72
+ }
73
+
74
+ .lightbox-image {
75
+ max-width: 100%;
76
+ max-height: 100%;
77
+ border-radius: 2px;
78
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
79
+ }
80
+ }
81
+
82
+ @media (prefers-reduced-motion: reduce) {
83
+ .lightbox-overlay {
84
+ transition: none;
85
+ }
86
+ }
@@ -71,6 +71,22 @@
71
71
  .page-content {
72
72
  padding-top: 8px;
73
73
 
74
+ // Resolve direction per block from its own text, so a mostly-LTR
75
+ // theme can still host RTL content (and vice versa) without manual
76
+ // markup. In LTR text this behaves exactly like the defaults.
77
+ p,
78
+ li,
79
+ blockquote,
80
+ h1,
81
+ h2,
82
+ h3,
83
+ h4,
84
+ h5,
85
+ h6 {
86
+ unicode-bidi: plaintext;
87
+ text-align: start;
88
+ }
89
+
74
90
  iframe {
75
91
  text-align: center;
76
92
  }
@@ -157,7 +173,7 @@
157
173
  &::before {
158
174
  content: "#";
159
175
  position: absolute;
160
- right: -3px;
176
+ inset-inline-end: -3px;
161
177
  width: 1em;
162
178
  font-weight: $bold-weight;
163
179
  }
@@ -191,6 +207,83 @@
191
207
  }
192
208
  }
193
209
 
210
+ // Table of contents
211
+ .toc {
212
+ margin-top: $spacing-full;
213
+ padding: 15px 20px;
214
+ border: 1px solid $light;
215
+ border-radius: 4px;
216
+
217
+ .toc-title {
218
+ margin: 0 0 8px;
219
+ color: $gray;
220
+ font-weight: $bold-weight;
221
+ font-size: $small-font-size;
222
+ letter-spacing: 0.5px;
223
+ text-transform: uppercase;
224
+ }
225
+
226
+ .toc-list {
227
+ margin: 0;
228
+ padding: 0;
229
+ list-style: none;
230
+ }
231
+
232
+ .toc-item {
233
+ line-height: 1.6;
234
+
235
+ &::before {
236
+ content: none;
237
+ }
238
+
239
+ a {
240
+ color: $text-base-color;
241
+ text-decoration: none;
242
+
243
+ &:hover {
244
+ color: $text-link-blue;
245
+ }
246
+ }
247
+ }
248
+
249
+ .toc-h3 {
250
+ padding-left: 18px;
251
+ font-size: $small-font-size;
252
+ }
253
+ }
254
+
255
+ // Image gallery: a neat, responsive row of equal-height images.
256
+ // Wrap images in <div class="gallery"> … </div>; each stays zoomable.
257
+ .page-content .gallery {
258
+ display: grid;
259
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
260
+ gap: 10px;
261
+ margin-top: $spacing-full - 15;
262
+ margin-bottom: $spacing-full - 15;
263
+
264
+ img {
265
+ width: 100%;
266
+ height: 180px;
267
+ margin: 0;
268
+ object-fit: cover;
269
+ border-radius: 2px;
270
+ }
271
+
272
+ figure {
273
+ margin: 0;
274
+ }
275
+ }
276
+
277
+ // Images inside list items render as small, aligned thumbnails so the
278
+ // list stays tidy. They remain zoomable via the lightbox.
279
+ .page-content li img {
280
+ max-height: 2.4em;
281
+ width: auto;
282
+ margin: 0 0.3em;
283
+ vertical-align: middle;
284
+ border-radius: 2px;
285
+ }
286
+
194
287
  .post-nav {
195
288
  display: flex;
196
289
  position: relative;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klise
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mahendrata Harpi
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-29 00:00:00.000000000 Z
11
+ date: 2026-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.1'
19
+ version: '4.4'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.1'
26
+ version: '4.4'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jekyll-feed
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.13'
33
+ version: '0.18'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.13'
40
+ version: '0.18'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: jekyll-sitemap
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.12.0
61
+ version: '0.12'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.12.0
68
+ version: '0.12'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: jekyll-postfiles
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webrick
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.8'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.8'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -94,7 +108,7 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '2.1'
97
- description:
111
+ description:
98
112
  email:
99
113
  - justharpi@gmail.com
100
114
  executables: []
@@ -111,6 +125,8 @@ files:
111
125
  - _includes/navbar.html
112
126
  - _includes/navigation.html
113
127
  - _includes/pagination.html
128
+ - _includes/schema.html
129
+ - _includes/toc.html
114
130
  - _layouts/404.html
115
131
  - _layouts/compress.html
116
132
  - _layouts/default.html
@@ -130,7 +146,7 @@ licenses:
130
146
  - MIT
131
147
  metadata:
132
148
  plugin_type: theme
133
- post_install_message:
149
+ post_install_message:
134
150
  rdoc_options: []
135
151
  require_paths:
136
152
  - lib
@@ -145,8 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
145
161
  - !ruby/object:Gem::Version
146
162
  version: '0'
147
163
  requirements: []
148
- rubygems_version: 3.1.2
149
- signing_key:
164
+ rubygems_version: 3.5.22
165
+ signing_key:
150
166
  specification_version: 4
151
167
  summary: "\U0001F3D6 Klisé is a minimalist Jekyll theme for running a personal site
152
168
  or blog, light & dark mode support."