monoholic 1.1.0 โ†’ 1.3.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: 82d9f14b72e9e12fbd2938a1e085e139cd3a8b1de2415914ee8aede7be2363b2
4
- data.tar.gz: d440773f3a4ace2da0bc9906ef8ee4a847d557eee3e4967392d0d660f8b29e2f
3
+ metadata.gz: 944d762c5b588ea620fe59093575adf3fe51488e84a04cbb31dd5b34619d9aed
4
+ data.tar.gz: 1ba6737b6d4e3b1e6e207f4cebadb2b51ea7f8608ff89cb139e89d1de8c25561
5
5
  SHA512:
6
- metadata.gz: 97ed2791cf9e2ec59c07724bfb7804565a0b71a02ca625f41f5b5fcfacf042def622fdec81d74245790907d2eb0b9932523819decd1589a8137d0e582ab6694c
7
- data.tar.gz: 4773401bdc07a85bf3a819edeef94c9d20354c60c9d806db2a66c604f51c59f637f2e40e34cf753fe2d364415102a4953b845190d4c9b675e483cbb73f682cb8
6
+ metadata.gz: 2051528bae7940745198bb450bf329d07f0e203230e05226735625a8da40d6b73bf726a58452f3d016fdefb4a052bd2d38e61655d07c828ec7d5c2adedef313a
7
+ data.tar.gz: 650a541107d5966c16485b3c483654ab3c5f9fa3ca7897401d92f00b423d8eadd4722107b8c97844bbf389879af30966c58a9649d625dc4dc082d52c5a3c3237
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Gem Version](https://img.shields.io/gem/v/monoholic?color=black)](https://rubygems.org/gems/monoholic)
7
7
  [![GitHub Pages](https://img.shields.io/badge/GitHub_Pages-ready-black?logo=github&logoColor=white)](https://stiermid.github.io/monoholic)
8
8
 
9
- A minimal, monochrome dark Jekyll theme๐Ÿงช
9
+ A minimal, monochrome dark Jekyll theme
10
10
 
11
11
  ## Features
12
12
 
@@ -38,14 +38,28 @@ theme: monoholic
38
38
  $ bundle install
39
39
  ```
40
40
 
41
- ### Manual Installation
41
+ ### Manual Installation (GitHub Pages)
42
42
 
43
43
  If you're running Jekyll without RubyGems or prefer to use GitHub Pages remote themes, update your `_config.yml`:
44
44
 
45
45
  ```yaml
46
46
  remote_theme: stiermid/monoholic
47
+ plugins:
48
+ - jekyll-remote-theme
49
+ - jekyll-feed
50
+ - jekyll-seo-tag
51
+ - jekyll-sitemap
47
52
  ```
48
53
 
54
+ `jekyll-remote-theme` fetches the theme on Pages; `jekyll-feed` and `jekyll-seo-tag` are required by the theme's `head.html` (`{% feed_meta %}`, `{% seo %}`), and `jekyll-sitemap` generates `sitemap.xml` referenced by the theme's `robots.txt`. For project sites, also set `baseurl` (e.g. `/your-repo`) and `url` (e.g. `https://<user>.github.io`). To pin a release: `remote_theme: stiermid/monoholic@v1.3.0`.
55
+
56
+ > **Pages note:** Native GitHub Pages builds run Jekyll 3.9. The `monoholic` RubyGem requires Jekyll 4.4, so on GitHub Pages use `remote_theme` only (not `theme:` + `gem "monoholic"`). SCSS intentionally uses `@import` for 3.9 compatibility.
57
+
58
+ | Install method | Jekyll | How |
59
+ | -------------------------------------------------------- | ------------- | --------------------------- |
60
+ | RubyGems (`theme: monoholic`) | 4.4 | local / CI / non-Pages host |
61
+ | GitHub Pages (`remote_theme: stiermid/monoholic@v1.3.0`) | 3.9 via Pages | Pages only |
62
+
49
63
  Or, simply fork this repository, adapt the `_config.yml` according to your needs, and you're good to go!
50
64
 
51
65
  ## Usage
@@ -54,14 +68,16 @@ Or, simply fork this repository, adapt the `_config.yml` according to your needs
54
68
 
55
69
  Once installed, build your site using the provided layouts (`default`, `home`, `page`, `post`).
56
70
 
71
+ On pages using the `home` layout, page content appears before the sections defined in `_data/menu.yml`.
72
+
57
73
  ### Configuration
58
74
 
59
75
  Override the default settings in your `_config.yml`. Key theme configuration options:
60
76
 
61
77
  ```yaml
62
78
  theme_config:
63
- back: ".." # Text for backlink on post pages
64
- date_format: "%Y%m%d" # Date format for post metadata
79
+ back: "Back" # Text for backlink on post pages
80
+ date_format: "%Y-%m-%d" # Date format for post metadata
65
81
  monochrome_images: true # Apply grayscale filter to images (default: true)
66
82
  footer: true # Show/hide site footer (default: true)
67
83
  ```
@@ -77,7 +93,7 @@ fediverse_creator: "@username@social.example.com"
77
93
  Setting `fediverse_creator` tag appends following tag to the site's header:
78
94
 
79
95
  ```html
80
- <meta name="fediverse:creator" content="@username@social.example.com">
96
+ <meta name="fediverse:creator" content="@username@social.example.com" />
81
97
  ```
82
98
 
83
99
  [See Also](https://docs.joinmastodon.org/user/profile/#attribution)
@@ -120,6 +136,15 @@ entries:
120
136
 
121
137
  Nesting can go as deep as needed โ€” `menu.html` includes itself recursively for each `entries` array it encounters.
122
138
 
139
+ Top-level titles use `h2`. Each titled parent advances its children by one heading level, capped at `h6`. Entries with missing, empty, or whitespace-only titles are unlabelled groups: they render no heading and do not advance the heading level. Later siblings always retain their own level, regardless of the depth of earlier branches.
140
+
141
+ Each titled section is labelled by its heading. Heading IDs include the ancestor path and sibling index, so repeated titles remain distinct. When including multiple copies of a menu on the same page, give each copy a distinct `id_prefix` to avoid overlapping IDs:
142
+
143
+ ```liquid
144
+ {% include menu.html collection=site.data.menu.entries level=2 id_prefix='primary' %}
145
+ {% include menu.html collection=site.data.menu.entries level=2 id_prefix='secondary' %}
146
+ ```
147
+
123
148
  #### Using External Content Files
124
149
 
125
150
  You can reference external markdown files instead of inline content:
@@ -142,20 +167,39 @@ Monoholic ships with a default favicon. To use your own, replace the following f
142
167
 
143
168
  ### Stylesheet
144
169
 
145
- Monoholic's stylesheet is written in SCSS and compiled at build time. The source lives in `_sass/` and is loaded from `assets/css/style.scss`. Compilation is handled by `jekyll-sass-converter`, which is declared as a runtime dependency in the theme's `gemspec` โ€” so no extra setup is required when you add `gem "monoholic"` to your `Gemfile`.
170
+ Monoholic's stylesheet is written in SCSS and compiled at build time. The source lives in `_sass/` and is imported from `assets/css/style.scss`. Compilation is handled by `jekyll-sass-converter`, which is declared as a runtime dependency in the theme's `gemspec` โ€” so no extra setup is required when you add `gem "monoholic"` to your `Gemfile`.
146
171
 
147
- If you want to override or extend the styles, the recommended approach is to add your own partials in your site's `_sass/` directory and load them **after** the theme's entry point:
172
+ If you want to override or extend the styles, the recommended approach is to add your own partials in your site's `_sass/` directory and import them **after** the theme's entry point:
148
173
 
149
174
  ```scss
150
175
  ---
151
176
  ---
152
177
 
153
- @use "monoholic";
154
- @use "your-overrides";
178
+ @import "monoholic";
179
+ @import "your-overrides";
155
180
  ```
156
181
 
182
+ Note: the theme uses `@import` (not `@use`) to stay compatible with GitHub Pages' Sass stack.
183
+
157
184
  To override CSS custom properties (colors, spacing, fonts) without touching SCSS, redeclare the variables in your own stylesheet โ€” they are exposed under `:root` in `_sass/_variables.scss`.
158
185
 
186
+ ### Responsive Content
187
+
188
+ Long words, URLs, and inline code wrap when necessary. Plain and highlighted code blocks preserve indentation and source lines, scrolling horizontally inside the block instead of expanding the page.
189
+
190
+ The small, dependency-free `assets/js/content-scroll.js` script progressively enhances tables with scroll containers while preserving native table semantics. Overflowing tables and code blocks receive a keyboard tab stop, an accessible name, and a visible scrolling hint. These are updated when the viewport, content dimensions, or fonts change; fitting content does not add automatic tab stops.
191
+
192
+ Table captions are used as accessible names when available. Existing author-provided labels, descriptions, and tab indices are preserved. You can translate the default labels and hint in `_config.yml`:
193
+
194
+ ```yaml
195
+ theme_config:
196
+ table_scroll_label: "Table"
197
+ code_scroll_label: "Code block"
198
+ scroll_hint: "Scroll horizontally to see more."
199
+ ```
200
+
201
+ Without JavaScript, tables use fixed-layout, wrapping cells, and code blocks still scroll. For especially wide tables that should scroll even without JavaScript, supply a `.table-scroll` wrapper with `tabindex="0"`, `role="region"`, and an accessible name (`aria-label` or `aria-labelledby`). The enhancement reuses that wrapper rather than nesting another one.
202
+
159
203
  ### Per-Page JavaScript
160
204
 
161
205
  To load custom JavaScript files on a specific page, add a `custom_js` list to the page's front matter:
@@ -173,6 +217,52 @@ Each entry is a filename (without `.js`) relative to `assets/js/`. The example a
173
217
 
174
218
  ## Contributing
175
219
 
220
+ ### Running Tests
221
+
222
+ ```sh
223
+ bundle install
224
+ npm ci
225
+ npx playwright install --with-deps chromium firefox webkit
226
+ bundle exec ruby _tests/release_workflow_test.rb
227
+ bundle exec ruby _tests/release_tag_test.rb
228
+ bundle exec ruby _tests/home_layout_test.rb
229
+ bundle exec ruby _tests/menu_layout_test.rb
230
+ JEKYLL_SAFE=1 bundle exec ruby _tests/menu_layout_test.rb
231
+ npm run test:browser
232
+ ```
233
+
234
+ Browser tests build and serve an isolated fixture site. They cover Chromium, Firefox, and WebKit at 320px, 375px, 768px, and 1280px, including long text, plain and highlighted code, Markdown and HTML tables, keyboard scrolling, resizing, and JavaScript-disabled layouts. External fonts are blocked so the tests do not depend on Google Fonts being available.
235
+
236
+ Menu rendering checks cover branching recursion, deep nesting, untitled groups, repeated titles, unique IDs, escaped labels, and independent menu instances in normal and Jekyll safe mode. Browser tests also verify native heading and section semantics with JavaScript disabled.
237
+
238
+ To run one browser, use `npm run test:browser -- --project=chromium`. You can also set `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH` to use a locally installed Chromium binary. Generated sites, screenshots, and traces are ignored by Git; CI retains failure diagnostics for seven days.
239
+
240
+ ### Release Validation
241
+
242
+ Both pull requests and release-tag pushes run the build and test checks. Publishing depends on all checks succeeding and is restricted to pushed `v*` tags; pull requests never publish. The tag must be `v` followed by the exact gem version in `monoholic.gemspec`, including any RubyGems prerelease suffix. Update the version metadata and lockfiles together before tagging.
243
+
244
+ CI builds the gem once and installs it into a temporary gem home. A clean consumer site uses that installed package with the existing lockfile dependencies, without downloading gems or falling back to repository theme files. The checks build root and `/blog` sites and verify layouts, compiled CSS, scripts, favicons, feeds, and URLs. Negative tests reject broken packages and invalid versions.
245
+
246
+ After validation, tag runs upload the gem and its SHA-256 manifest as a seven-day artifact. The publishing job downloads those same bytes, verifies the checksum and package name/version, and pushes the same gem to GitHub Packages and RubyGemsโ€”without checking out or rebuilding the source. Rerunning validation replaces the artifact only after checks succeed; if it has expired, rerun validation rather than just the publishing job.
247
+
248
+ Publishing keeps the existing authentication: `GITHUB_TOKEN` with package-write permission for GitHub Packages, and the repository's `RUBYGEMS_API_KEY` secret for RubyGems. Validation has read-only repository permissions; registry credentials are used only by publishing steps.
249
+
250
+ To exercise package checks locally without publishing (publisher verification requires Bash and `sha256sum` from GNU coreutils, matching the Linux CI runner):
251
+
252
+ ```sh
253
+ mkdir -p _tests/.release
254
+ bundle exec gem build monoholic.gemspec --output _tests/.release/monoholic.gem
255
+ bundle exec ruby _tests/package_consumer_test.rb _tests/.release/monoholic.gem
256
+ bundle exec ruby _tests/package_consumer_negative_test.rb _tests/.release/monoholic.gem
257
+ (
258
+ cd _tests/.release
259
+ sha256sum monoholic.gem > monoholic.gem.sha256
260
+ )
261
+ bundle exec ruby _tests/package_integrity_test.rb _tests/.release/monoholic.gem _tests/.release/monoholic.gem.sha256
262
+ ```
263
+
264
+ `package_integrity_test.rb` exercises the actual publisher verification recipe with valid, tampered, missing, and malformed inputs, plus a mismatched release tag. It never runs registry publishing commands. Release outputs are ignored by Git, and temporary package installations are removed after the checks finish.
265
+
176
266
  Bug reports and pull requests are welcome on GitHub at [https://github.com/stiermid/monoholic](https://github.com/stiermid/monoholic). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct.
177
267
 
178
268
  ## License
data/_includes/head.html CHANGED
@@ -2,6 +2,8 @@
2
2
  <meta charset="utf-8">
3
3
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
4
4
  <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta name="theme-color" content="#030303">
6
+ <meta name="color-scheme" content="dark">
5
7
 
6
8
  {%- if site.fediverse_creator -%}
7
9
  <meta
@@ -39,6 +41,7 @@
39
41
  >
40
42
  <link
41
43
  rel="apple-touch-icon"
44
+ sizes="180x180"
42
45
  href="{{ "/assets/apple-touch-icon.png" | relative_url }}"
43
46
  >
44
47
  {%- feed_meta -%}
data/_includes/menu.html CHANGED
@@ -1,21 +1,25 @@
1
- {%- assign _level = include.level | default: 2 | plus: 0 -%}
2
- {%- if _level < 2 -%}
3
- {%- assign _level = 2 -%}
4
- {%- elsif _level > 6 -%}
5
- {%- assign _level = 6 -%}
6
- {%- endif -%}
7
- {%- assign _next_level = _level | plus: 1 -%}
8
- {%- if _next_level > 6 -%}
9
- {%- assign _next_level = 6 -%}
10
- {%- endif -%}
11
1
  {%- for item in include.collection -%}
2
+ {%- comment -%}
3
+ Liquid assigns are shared across recursive includes. Recompute each entry
4
+ from the scoped include parameters before rendering its heading or children.
5
+ {%- endcomment -%}
6
+ {%- assign _level = include.level | default: 2 | plus: 0 -%}
7
+ {%- if _level < 2 -%}
8
+ {%- assign _level = 2 -%}
9
+ {%- elsif _level > 6 -%}
10
+ {%- assign _level = 6 -%}
11
+ {%- endif -%}
12
12
  {%- assign _title_stripped = item.title | strip -%}
13
13
  {%- assign _has_title = true -%}
14
- {%- if item.title == nil or _title_stripped == empty -%}
14
+ {%- if item.title == null or _title_stripped == empty -%}
15
15
  {%- assign _has_title = false -%}
16
16
  {%- endif -%}
17
+ {%- assign _next_level = _level -%}
18
+ {%- if _has_title and _level < 6 -%}
19
+ {%- assign _next_level = _level | plus: 1 -%}
20
+ {%- endif -%}
17
21
  {%- assign _title_slug = item.title | slugify -%}
18
- {%- if _title_slug == empty or _title_slug == nil -%}
22
+ {%- if _title_slug == empty or _title_slug == null -%}
19
23
  {%- assign _title_slug = 'section' -%}
20
24
  {%- endif -%}
21
25
  {%- if include.parent_id -%}
@@ -38,32 +42,34 @@
38
42
  | append: forloop.index
39
43
  -%}
40
44
  {%- endif -%}
41
- {%- if _has_title -%}
42
- <section aria-labelledby="{{ _heading_id }}">
45
+ <section
46
+ {% if _has_title %}
47
+ aria-labelledby="{{ _heading_id | escape }}"
48
+ {% endif %}
49
+ >
50
+ {%- if _has_title -%}
43
51
  {%- if _level == 3 -%}
44
- <h3 class="section-title" id="{{ _heading_id }}">
52
+ <h3 class="section-title" id="{{ _heading_id | escape }}">
45
53
  {{ item.title | escape }}
46
54
  </h3>
47
55
  {%- elsif _level == 4 -%}
48
- <h4 class="section-title" id="{{ _heading_id }}">
56
+ <h4 class="section-title" id="{{ _heading_id | escape }}">
49
57
  {{ item.title | escape }}
50
58
  </h4>
51
59
  {%- elsif _level == 5 -%}
52
- <h5 class="section-title" id="{{ _heading_id }}">
60
+ <h5 class="section-title" id="{{ _heading_id | escape }}">
53
61
  {{ item.title | escape }}
54
62
  </h5>
55
63
  {%- elsif _level >= 6 -%}
56
- <h6 class="section-title" id="{{ _heading_id }}">
64
+ <h6 class="section-title" id="{{ _heading_id | escape }}">
57
65
  {{ item.title | escape }}
58
66
  </h6>
59
67
  {%- else -%}
60
- <h2 class="section-title" id="{{ _heading_id }}">
68
+ <h2 class="section-title" id="{{ _heading_id | escape }}">
61
69
  {{ item.title | escape }}
62
70
  </h2>
63
71
  {%- endif -%}
64
- {%- else -%}
65
- <section>
66
- {%- endif -%}
72
+ {%- endif -%}
67
73
  {%- if item.content_file -%}
68
74
  {%- assign _menu_page = site.pages
69
75
  | where: 'path', item.content_file
@@ -77,7 +83,8 @@
77
83
  {%- endif -%}
78
84
  {%- if item.post_list -%}{%- include post_list.html -%}{%- endif -%}
79
85
  {%- if item.entries -%}
80
- {%- include menu.html
86
+ {%-
87
+ include menu.html
81
88
  collection=item.entries
82
89
  level=_next_level
83
90
  parent_id=_heading_id
@@ -8,7 +8,7 @@
8
8
  {% assign _date_fmt = site.theme_config.date_format
9
9
  | default: '%Y-%m-%d'
10
10
  %}
11
- * {{ post.date | date: _date_fmt }}
11
+ {{ post.date | date: _date_fmt }}
12
12
  </time>
13
13
  <a href="{{ post.url | relative_url }}">
14
14
  {{ post.title | escape }}
@@ -4,7 +4,9 @@
4
4
  <body class="{% if site.theme_config.monochrome_images != false %}images-monochrome{% endif %}">
5
5
  <a class="skip-link" href="#main">Skip to content</a>
6
6
  <div class="wrapper">
7
- {%- include header.html -%}
7
+ {%- unless page.layout == 'post' -%}
8
+ {%- include header.html -%}
9
+ {%- endunless -%}
8
10
  <main id="main" class="page-content">
9
11
  {{ content }}
10
12
  </main>
@@ -14,10 +16,18 @@
14
16
  {%- include footer.html -%}
15
17
  {%- endunless -%}
16
18
 
19
+ <script
20
+ defer
21
+ src="{{ '/assets/js/content-scroll.js' | relative_url }}"
22
+ data-table-label="{{ site.theme_config.table_scroll_label | default: 'Table' | escape }}"
23
+ data-code-label="{{ site.theme_config.code_scroll_label | default: 'Code block' | escape }}"
24
+ data-scroll-hint="{{ site.theme_config.scroll_hint | default: 'Scroll horizontally to see more.' | escape }}"
25
+ ></script>
26
+
17
27
  {% if page.custom_js %}
18
28
  {% for js_file in page.custom_js %}
19
29
  <script
20
- type="text/javascript"
30
+ defer
21
31
  src="{{ '/assets/js/' | append: js_file | append: '.js' | relative_url }}"
22
32
  ></script>
23
33
  {% endfor %}
data/_layouts/home.html CHANGED
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  layout: default
3
3
  ---
4
+ {{ content }}
5
+
4
6
  <nav aria-label="Site sections">
5
7
  {%- include menu.html collection=site.data.menu.entries level=2 -%}
6
8
  </nav>
data/_sass/_base.scss CHANGED
@@ -8,6 +8,8 @@ body {
8
8
  font-family: var(--font-family);
9
9
  font-size: var(--font-size-base);
10
10
  line-height: 1.5;
11
+ // Break unusually long words and URLs only when they cannot fit.
12
+ overflow-wrap: anywhere;
11
13
  min-height: 100vh;
12
14
  margin: 0;
13
15
  padding: 0;
@@ -9,6 +9,13 @@
9
9
  align-items: baseline;
10
10
  gap: var(--space-md);
11
11
  flex-wrap: wrap; // Mobile safety
12
+
13
+ &::before {
14
+ content: "*";
15
+ color: var(--meta-color);
16
+ // Keep marker snug to the date (flex `gap` would push it too far).
17
+ margin-right: calc(1ch - var(--space-md));
18
+ }
12
19
  }
13
20
 
14
21
  .post-meta {
data/_sass/_content.scss CHANGED
@@ -4,20 +4,22 @@
4
4
  color: var(--bg-color);
5
5
  }
6
6
 
7
- div.highlighter-rouge {
8
- code {
9
- display: block;
10
- overflow-x: auto;
11
- white-space: pre-wrap;
12
- }
7
+ .page-content pre {
8
+ max-width: 100%;
9
+ overflow-x: auto;
10
+ // Keep indentation and source lines intact; scroll the block, not the page.
11
+ white-space: pre;
12
+ overflow-wrap: normal;
13
+ }
13
14
 
14
- pre.highlight {
15
- padding: var(--space-md);
16
- }
15
+ div.highlighter-rouge pre.highlight {
16
+ padding: var(--space-md);
17
17
  }
18
18
 
19
19
  table {
20
20
  width: 100%;
21
+ // Readable wrapping is the fallback when scripts are unavailable.
22
+ table-layout: fixed;
21
23
  margin-bottom: var(--space-md);
22
24
 
23
25
  &,
@@ -29,6 +31,26 @@ table {
29
31
  }
30
32
  }
31
33
 
34
+ .table-scroll {
35
+ max-width: 100%;
36
+ overflow-x: auto;
37
+ margin-bottom: var(--space-md);
38
+
39
+ table {
40
+ // Keep native table layout and semantics inside the scroll container.
41
+ table-layout: auto;
42
+ overflow-wrap: normal;
43
+ margin-bottom: 0;
44
+ }
45
+ }
46
+
47
+ .scroll-hint {
48
+ color: var(--meta-color);
49
+ font-size: var(--font-size-sm);
50
+ margin-top: calc(-1 * var(--space-sm));
51
+ margin-bottom: var(--space-md);
52
+ }
53
+
32
54
  blockquote {
33
55
  font-style: italic;
34
56
  border: thin solid var(--border-color);
data/_sass/monoholic.scss CHANGED
@@ -1,6 +1,6 @@
1
1
  // monoholic.scss
2
- @use "variables";
3
- @use "base";
4
- @use "content";
5
- @use "layout";
6
- @use "components";
2
+ @import "variables";
3
+ @import "base";
4
+ @import "content";
5
+ @import "layout";
6
+ @import "components";
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  ---
3
3
 
4
- @use "monoholic";
4
+ @import "monoholic";
data/assets/favicon.svg CHANGED
@@ -1,58 +1 @@
1
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
- <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
-
4
- <svg
5
- width="32"
6
- height="32"
7
- viewBox="0 0 32 32"
8
- version="1.1"
9
- id="svg1"
10
- inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
11
- sodipodi:docname="icon.svg"
12
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
- xmlns="http://www.w3.org/2000/svg"
15
- xmlns:svg="http://www.w3.org/2000/svg">
16
- <sodipodi:namedview
17
- id="namedview1"
18
- pagecolor="#505050"
19
- bordercolor="#eeeeee"
20
- borderopacity="1"
21
- inkscape:showpageshadow="0"
22
- inkscape:pageopacity="0"
23
- inkscape:pagecheckerboard="0"
24
- inkscape:deskcolor="#505050"
25
- inkscape:document-units="px"
26
- inkscape:zoom="16"
27
- inkscape:cx="8.125"
28
- inkscape:cy="19.59375"
29
- inkscape:window-width="1918"
30
- inkscape:window-height="1052"
31
- inkscape:window-x="0"
32
- inkscape:window-y="0"
33
- inkscape:window-maximized="1"
34
- inkscape:current-layer="layer1" />
35
- <defs
36
- id="defs1" />
37
- <g
38
- inkscape:label="Layer 1"
39
- inkscape:groupmode="layer"
40
- id="layer1">
41
- <path
42
- style="font-size:48px;font-family:Monospace;-inkscape-font-specification:Monospace"
43
- d="M 4.4799998,27.981593 V 4.4984075 h 3.744 c 0,0 0.72,1.36 1.488,0.624 0.8000002,-0.736 1.8080002,-1.104 3.0240002,-1.104 1.152,0 2.128,0.336 2.928,1.008 0.8,0.672 2.496,0.672 3.264,0 0.8,-0.672 1.808,-1.008 3.024,-1.008 1.696,0 3.04,0.64 4.032,1.92 1.024,1.248 1.536,2.88 1.536,4.8960005 V 27.981593 H 23.488 V 10.786408 c 0,-1.0560005 -0.256,-1.8880005 -0.768,-2.4960005 -0.512,-0.64 -1.216,-0.96 -2.112,-0.96 -0.864,0 -1.552,0.304 -2.064,0.912 -0.512,0.608 -0.768,1.44 -0.768,2.4960005 V 27.981593 H 14.224 V 10.786408 c 0,-1.0560005 -0.256,-1.8880005 -0.768,-2.4960005 -0.512,-0.64 -1.2,-0.96 -2.064,-0.96 -0.896,0 -1.6000002,0.304 -2.1120002,0.912 -0.512,0.608 -0.768,1.44 -0.768,2.4960005 v 17.243185 z"
44
- id="text2"
45
- aria-label="m"
46
- sodipodi:nodetypes="ccccsscscsccscsssccscssscc" />
47
- <text
48
- xml:space="preserve"
49
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:48px;font-family:Monospace;-inkscape-font-specification:Monospace;writing-mode:lr-tb;direction:ltr;fill:#000000"
50
- x="-171.11984"
51
- y="24.041632"
52
- id="text3"><tspan
53
- sodipodi:role="line"
54
- id="tspan2"
55
- x="-171.11984"
56
- y="24.041632" /></text>
57
- </g>
58
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img"><title>m</title><path fill="#fff" d="M 4.4799998,27.981593 V 4.4984075 h 3.744 c 0,0 0.72,1.36 1.488,0.624 0.8000002,-0.736 1.8080002,-1.104 3.0240002,-1.104 1.152,0 2.128,0.336 2.928,1.008 0.8,0.672 2.496,0.672 3.264,0 0.8,-0.672 1.808,-1.008 3.024,-1.008 1.696,0 3.04,0.64 4.032,1.92 1.024,1.248 1.536,2.88 1.536,4.8960005 V 27.981593 H 23.488 V 10.786408 c 0,-1.0560005 -0.256,-1.8880005 -0.768,-2.4960005 -0.512,-0.64 -1.216,-0.96 -2.112,-0.96 -0.864,0 -1.552,0.304 -2.064,0.912 -0.512,0.608 -0.768,1.44 -0.768,2.4960005 V 27.981593 H 14.224 V 10.786408 c 0,-1.0560005 -0.256,-1.8880005 -0.768,-2.4960005 -0.512,-0.64 -1.2,-0.96 -2.064,-0.96 -0.896,0 -1.6000002,0.304 -2.1120002,0.912 -0.512,0.608 -0.768,1.44 -0.768,2.4960005 v 17.243185 z"/></svg>
data/assets/js/.keep ADDED
@@ -0,0 +1,5 @@
1
+ # Placeholder to keep assets/js/ in version control.
2
+ # Per-page scripts referenced via `custom_js` front matter live here,
3
+ # e.g. `assets/js/my-script.js` loaded with:
4
+ # custom_js:
5
+ # - my-script
@@ -0,0 +1,109 @@
1
+ (() => {
2
+ const settings = document.currentScript?.dataset || {};
3
+ const updates = [];
4
+ let hintIndex = 0;
5
+
6
+ const updateAll = () => updates.forEach((update) => update());
7
+ const observer =
8
+ typeof ResizeObserver === "function" ? new ResizeObserver(updateAll) : null;
9
+ const contentObserver =
10
+ typeof MutationObserver === "function"
11
+ ? new MutationObserver(updateAll)
12
+ : null;
13
+
14
+ function enhance(region, content, hintAnchor, label, labelledBy) {
15
+ // Also respect author-provided wrappers and repeated script inclusion.
16
+ if (region.hasAttribute("data-scroll-enhanced")) return;
17
+ region.setAttribute("data-scroll-enhanced", "");
18
+ const originalAttributes = new Set(region.getAttributeNames());
19
+ const hint = document.createElement("p");
20
+ do {
21
+ hint.id = `monoholic-scroll-hint-${++hintIndex}`;
22
+ } while (document.getElementById(hint.id));
23
+ hint.className = "scroll-hint";
24
+ hint.textContent =
25
+ settings.scrollHint || "Scroll horizontally to see more.";
26
+ hint.hidden = true;
27
+ hintAnchor.after(hint);
28
+
29
+ function setAutomaticAttribute(name, value) {
30
+ if (originalAttributes.has(name)) return;
31
+ if (value === null) region.removeAttribute(name);
32
+ else region.setAttribute(name, value);
33
+ }
34
+
35
+ function update() {
36
+ const overflows = region.scrollWidth > region.clientWidth + 1;
37
+ hint.hidden = !overflows;
38
+ setAutomaticAttribute("tabindex", overflows ? "0" : null);
39
+ setAutomaticAttribute("role", overflows ? "region" : null);
40
+ if (
41
+ !originalAttributes.has("aria-label") &&
42
+ !originalAttributes.has("aria-labelledby")
43
+ ) {
44
+ setAutomaticAttribute(
45
+ labelledBy ? "aria-labelledby" : "aria-label",
46
+ overflows ? labelledBy || label : null,
47
+ );
48
+ }
49
+
50
+ // Preserve descriptions supplied by the author or other scripts.
51
+ const descriptions = new Set(
52
+ (region.getAttribute("aria-describedby") || "")
53
+ .split(/\s+/)
54
+ .filter(Boolean),
55
+ );
56
+ if (overflows) descriptions.add(hint.id);
57
+ else descriptions.delete(hint.id);
58
+ if (descriptions.size) {
59
+ region.setAttribute("aria-describedby", [...descriptions].join(" "));
60
+ } else {
61
+ region.removeAttribute("aria-describedby");
62
+ }
63
+ }
64
+
65
+ updates.push(update);
66
+ update();
67
+ observer?.observe(region);
68
+ if (content !== region) observer?.observe(content);
69
+ contentObserver?.observe(content, {
70
+ childList: true,
71
+ characterData: true,
72
+ subtree: true,
73
+ });
74
+ }
75
+
76
+ document.querySelectorAll(".page-content table").forEach((table) => {
77
+ let wrapper = table.parentElement;
78
+ if (!wrapper.classList.contains("table-scroll")) {
79
+ wrapper = document.createElement("div");
80
+ wrapper.className = "table-scroll";
81
+ table.before(wrapper);
82
+ wrapper.append(table);
83
+ }
84
+ const label =
85
+ table.getAttribute("aria-label") ||
86
+ table.caption?.textContent.trim() ||
87
+ settings.tableLabel ||
88
+ "Table";
89
+ enhance(
90
+ wrapper,
91
+ table,
92
+ wrapper,
93
+ label,
94
+ table.getAttribute("aria-labelledby"),
95
+ );
96
+ });
97
+
98
+ document.querySelectorAll(".page-content pre").forEach((block) => {
99
+ const hintAnchor = block.closest("div.highlighter-rouge") || block;
100
+ enhance(block, block, hintAnchor, settings.codeLabel || "Code block");
101
+ });
102
+
103
+ window.addEventListener("resize", updateAll);
104
+ // A font swap can change overflow without resizing the container itself.
105
+ if (document.fonts) {
106
+ document.fonts.ready.then(updateAll);
107
+ document.fonts.addEventListener("loadingdone", updateAll);
108
+ }
109
+ })();
data/robots.txt ADDED
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: null
3
+ sitemap: false
4
+ ---
5
+ User-agent: *
6
+ Allow: /
7
+
8
+ Sitemap: {{ site.url }}{{ site.baseurl }}/sitemap.xml
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monoholic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Agil Mammadov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-12 00:00:00.000000000 Z
11
+ date: 2026-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: jekyll-sitemap
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
69
83
  description:
70
84
  email:
71
85
  - mammadovagil@proton.me
@@ -93,10 +107,11 @@ files:
93
107
  - _sass/monoholic.scss
94
108
  - assets/apple-touch-icon.png
95
109
  - assets/css/style.scss
96
- - assets/favicon-16x16.png
97
- - assets/favicon-32x32.png
98
110
  - assets/favicon.ico
99
111
  - assets/favicon.svg
112
+ - assets/js/.keep
113
+ - assets/js/content-scroll.js
114
+ - robots.txt
100
115
  homepage: https://github.com/stiermid/monoholic
101
116
  licenses:
102
117
  - GPL-3.0-only
Binary file
Binary file