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 +4 -4
- data/README.md +99 -9
- data/_includes/head.html +3 -0
- data/_includes/menu.html +30 -23
- data/_includes/post_list.html +1 -1
- data/_layouts/default.html +12 -2
- data/_layouts/home.html +2 -0
- data/_sass/_base.scss +2 -0
- data/_sass/_components.scss +7 -0
- data/_sass/_content.scss +31 -9
- data/_sass/monoholic.scss +5 -5
- data/assets/css/style.scss +1 -1
- data/assets/favicon.svg +1 -58
- data/assets/js/.keep +5 -0
- data/assets/js/content-scroll.js +109 -0
- data/robots.txt +8 -0
- metadata +19 -4
- data/assets/favicon-16x16.png +0 -0
- data/assets/favicon-32x32.png +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 944d762c5b588ea620fe59093575adf3fe51488e84a04cbb31dd5b34619d9aed
|
|
4
|
+
data.tar.gz: 1ba6737b6d4e3b1e6e207f4cebadb2b51ea7f8608ff89cb139e89d1de8c25561
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2051528bae7940745198bb450bf329d07f0e203230e05226735625a8da40d6b73bf726a58452f3d016fdefb4a052bd2d38e61655d07c828ec7d5c2adedef313a
|
|
7
|
+
data.tar.gz: 650a541107d5966c16485b3c483654ab3c5f9fa3ca7897401d92f00b423d8eadd4722107b8c97844bbf389879af30966c58a9649d625dc4dc082d52c5a3c3237
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://rubygems.org/gems/monoholic)
|
|
7
7
|
[](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: "
|
|
64
|
-
date_format: "%Y
|
|
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
|
|
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
|
|
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
|
-
@
|
|
154
|
-
@
|
|
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 ==
|
|
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 ==
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
{%-
|
|
86
|
+
{%-
|
|
87
|
+
include menu.html
|
|
81
88
|
collection=item.entries
|
|
82
89
|
level=_next_level
|
|
83
90
|
parent_id=_heading_id
|
data/_includes/post_list.html
CHANGED
data/_layouts/default.html
CHANGED
|
@@ -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
|
-
{%-
|
|
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
|
-
|
|
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
data/_sass/_base.scss
CHANGED
data/_sass/_components.scss
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
@
|
|
3
|
-
@
|
|
4
|
-
@
|
|
5
|
-
@
|
|
6
|
-
@
|
|
2
|
+
@import "variables";
|
|
3
|
+
@import "base";
|
|
4
|
+
@import "content";
|
|
5
|
+
@import "layout";
|
|
6
|
+
@import "components";
|
data/assets/css/style.scss
CHANGED
data/assets/favicon.svg
CHANGED
|
@@ -1,58 +1 @@
|
|
|
1
|
-
|
|
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,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
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.
|
|
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-
|
|
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
|
data/assets/favicon-16x16.png
DELETED
|
Binary file
|
data/assets/favicon-32x32.png
DELETED
|
Binary file
|