monoholic 1.2.0 → 1.4.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 +258 -12
- data/_includes/menu.html +36 -24
- data/_layouts/default.html +18 -2
- data/_layouts/home.html +7 -1
- data/_sass/_base.scss +2 -0
- data/_sass/_content.scss +31 -9
- data/_sass/_variables.scss +1 -1
- data/assets/js/content-scroll.js +109 -0
- data/robots.txt +8 -0
- metadata +38 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b2feef2d72bb935b6e4e6bdb2d5c39034616dadd12f3c6ca9158a43cafe24d8
|
|
4
|
+
data.tar.gz: 354b3cc49d0afcbcfe40e21f2e0da506310c34b3ee44a06392a397d8e90360a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3acf3815b503469a52aab55448404d6c2f3869fb636c11831c4d698d962510558b1d6f282f749fe51b8e923c9c29ed9e9ed56705f1ea6d28c5a940f5f3c57a21
|
|
7
|
+
data.tar.gz: 929ca0d46b670294db583b75f4e633bbe2eabc990cd916b360694159ac357a689bcf3409fffc9f7e06b6d5cbc930e65e1b50010cc4dc6d16642022169eb2091f
|
data/README.md
CHANGED
|
@@ -40,26 +40,69 @@ $ bundle install
|
|
|
40
40
|
|
|
41
41
|
### Manual Installation (GitHub Pages)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
For GitHub's built-in Jekyll build, add the following to `_config.yml`:
|
|
44
44
|
|
|
45
45
|
```yaml
|
|
46
|
-
|
|
46
|
+
title: "Your site"
|
|
47
|
+
description: "A short description of your site."
|
|
48
|
+
remote_theme: stiermid/monoholic@v1.4.0
|
|
49
|
+
url: "https://<user>.github.io"
|
|
50
|
+
baseurl: "/your-repo"
|
|
47
51
|
plugins:
|
|
48
52
|
- jekyll-remote-theme
|
|
49
53
|
- jekyll-feed
|
|
50
54
|
- jekyll-seo-tag
|
|
55
|
+
- jekyll-sitemap
|
|
51
56
|
```
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
Replace the user and repository placeholders. Use `baseurl: ""` for a user
|
|
59
|
+
site or a custom domain served at its root. Pinning a release keeps theme
|
|
60
|
+
updates explicit; omit `@v1.4.0` only if you want the repository's default
|
|
61
|
+
branch instead.
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
`jekyll-remote-theme` loads the theme. Feed and SEO tags require
|
|
64
|
+
`jekyll-feed` and `jekyll-seo-tag`; `jekyll-sitemap` generates the sitemap
|
|
65
|
+
and a default `robots.txt` when the site does not supply one.
|
|
56
66
|
|
|
57
|
-
|
|
58
|
-
| -------------------------------------------------------- | ------------- | --------------------------- |
|
|
59
|
-
| RubyGems (`theme: monoholic`) | 4.4 | local / CI / non-Pages host |
|
|
60
|
-
| GitHub Pages (`remote_theme: stiermid/monoholic@v1.2.0`) | 3.9 via Pages | Pages only |
|
|
67
|
+
Create a homepage, such as `index.md`:
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
```markdown
|
|
70
|
+
---
|
|
71
|
+
layout: home
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
Welcome to my site.
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Also create `_data/menu.yml` in your site:
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
entries:
|
|
81
|
+
- title: all posts
|
|
82
|
+
post_list: true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Native Jekyll 3.10 does not inherit a remote theme's `_data` directory, so
|
|
86
|
+
your site must supply its own menu. Add your posts under `_posts/` and
|
|
87
|
+
extend the menu using the options below.
|
|
88
|
+
|
|
89
|
+
> **Pages note:** Native Pages currently uses Jekyll 3.10.0 and Sass converter
|
|
90
|
+
> 1.5.2 with Ruby Sass. The `monoholic` gem requires Jekyll 4.4, so do not
|
|
91
|
+
> combine it with the native Pages bundle. Use `remote_theme` instead of
|
|
92
|
+
> `theme: monoholic` for that build. SCSS retains `@import` for this older
|
|
93
|
+
> Sass compiler.
|
|
94
|
+
|
|
95
|
+
| Method | Jekyll | Build |
|
|
96
|
+
| ------------ | ------ | ------------------- |
|
|
97
|
+
| RubyGem | 4.4 | Local / custom CI |
|
|
98
|
+
| Remote theme | 3.10 | Native GitHub Pages |
|
|
99
|
+
|
|
100
|
+
You can still deploy a Jekyll 4.4 gem-based site to Pages as static output
|
|
101
|
+
from a custom build workflow. The version restriction applies to GitHub's
|
|
102
|
+
built-in Jekyll build, not to static hosting.
|
|
103
|
+
|
|
104
|
+
For a full source checkout instead, fork this repository and adapt
|
|
105
|
+
`_config.yml` to your site.
|
|
63
106
|
|
|
64
107
|
## Usage
|
|
65
108
|
|
|
@@ -67,6 +110,8 @@ Or, simply fork this repository, adapt the `_config.yml` according to your needs
|
|
|
67
110
|
|
|
68
111
|
Once installed, build your site using the provided layouts (`default`, `home`, `page`, `post`).
|
|
69
112
|
|
|
113
|
+
On pages using the `home` layout, page content appears before the sections defined in `_data/menu.yml`.
|
|
114
|
+
|
|
70
115
|
### Configuration
|
|
71
116
|
|
|
72
117
|
Override the default settings in your `_config.yml`. Key theme configuration options:
|
|
@@ -77,8 +122,35 @@ theme_config:
|
|
|
77
122
|
date_format: "%Y-%m-%d" # Date format for post metadata
|
|
78
123
|
monochrome_images: true # Apply grayscale filter to images (default: true)
|
|
79
124
|
footer: true # Show/hide site footer (default: true)
|
|
125
|
+
skip_link_label: "Skip to content" # Keyboard skip-link text
|
|
126
|
+
site_sections_label: "Site sections" # Accessible name of the homepage navigation
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Language and Interface Labels
|
|
130
|
+
|
|
131
|
+
The document's `<html lang>` uses the page's `lang` front matter, then the site's `lang` setting, then `en`. Use BCP 47 language tags such as `de`, `en-GB`, or `zh-Hant-TW`. Surrounding whitespace is trimmed; missing, empty, whitespace-only, or `false` values fall back to the next setting.
|
|
132
|
+
|
|
133
|
+
For example, configure a German site in `_config.yml`:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
lang: de
|
|
137
|
+
theme_config:
|
|
138
|
+
skip_link_label: "Zum Inhalt"
|
|
139
|
+
site_sections_label: "Seitenbereiche"
|
|
140
|
+
back: "Zurück"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Override the document language for an individual page or post in its front matter:
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
---
|
|
147
|
+
layout: page
|
|
148
|
+
lang: fr-CA
|
|
149
|
+
---
|
|
80
150
|
```
|
|
81
151
|
|
|
152
|
+
Declaring `lang` helps browsers and assistive technology interpret your content; it does not translate the content or interface automatically. Interface labels above are site-wide and rendered as plain text, not HTML. Missing or blank labels retain their English defaults so the skip link and navigation keep accessible names. Existing scroll labels and hints can also be translated under [Responsive Content](#responsive-content).
|
|
153
|
+
|
|
82
154
|
### Fediverse Creator tag
|
|
83
155
|
|
|
84
156
|
Monoholic supports `fediverse:creator` meta tag:
|
|
@@ -113,9 +185,12 @@ entries:
|
|
|
113
185
|
|
|
114
186
|
- `title`: The section header.
|
|
115
187
|
- `content`: Custom HTML or text for the menu section.
|
|
116
|
-
- `
|
|
188
|
+
- `content_include`: Path relative to `_includes/` for a Markdown fragment with Liquid support (recommended for reusable content).
|
|
189
|
+
- `content_file`: Path to an existing Jekyll page (e.g., `about.md`) relative to your site root. Retained for compatibility; does not reliably evaluate Liquid.
|
|
117
190
|
- `post_list`: Set to `true` to auto-generate a list of your Jekyll posts under this section.
|
|
118
191
|
|
|
192
|
+
Use one content source per entry. If several are supplied, precedence is `content_include`, then `content_file`, then `content`.
|
|
193
|
+
|
|
119
194
|
#### Nested Menu Sections
|
|
120
195
|
|
|
121
196
|
Menu entries can contain nested `entries` to create recursive sub-sections:
|
|
@@ -133,9 +208,40 @@ entries:
|
|
|
133
208
|
|
|
134
209
|
Nesting can go as deep as needed — `menu.html` includes itself recursively for each `entries` array it encounters.
|
|
135
210
|
|
|
211
|
+
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.
|
|
212
|
+
|
|
213
|
+
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:
|
|
214
|
+
|
|
215
|
+
```liquid
|
|
216
|
+
{% include menu.html collection=site.data.menu.entries level=2 id_prefix='primary' %}
|
|
217
|
+
{% include menu.html collection=site.data.menu.entries level=2 id_prefix='secondary' %}
|
|
218
|
+
```
|
|
219
|
+
|
|
136
220
|
#### Using External Content Files
|
|
137
221
|
|
|
138
|
-
|
|
222
|
+
For reusable content containing Liquid, create a Markdown fragment in your site's `_includes/` directory, for example `_includes/fragments/about.md`:
|
|
223
|
+
|
|
224
|
+
```liquid
|
|
225
|
+
Welcome to **{{ site.title }}**.
|
|
226
|
+
|
|
227
|
+
[Home]({{ '/' | relative_url }})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Reference it using `content_include`:
|
|
231
|
+
|
|
232
|
+
```yaml
|
|
233
|
+
entries:
|
|
234
|
+
- title: about
|
|
235
|
+
content_include: fragments/about.md
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Jekyll renders Liquid first, then Monoholic converts the resulting Markdown to HTML once. Standard Liquid variables, filters, tags, and nested includes work without a custom plugin, including in safe mode. `page` refers to the page displaying the menu, not the fragment. Use `relative_url` for links that must work on project sites such as `/blog`.
|
|
239
|
+
|
|
240
|
+
Fragments must **not** have YAML front matter. They do not generate standalone pages. Paths are relative to `_includes/`, even when the menu appears on a nested page; missing files and invalid paths fail the build with Jekyll's include error rather than silently omitting content. Only include trusted, author-controlled files.
|
|
241
|
+
|
|
242
|
+
##### Existing `content_file` references
|
|
243
|
+
|
|
244
|
+
Existing page references remain supported:
|
|
139
245
|
|
|
140
246
|
```yaml
|
|
141
247
|
entries:
|
|
@@ -143,7 +249,17 @@ entries:
|
|
|
143
249
|
content_file: about.md
|
|
144
250
|
```
|
|
145
251
|
|
|
146
|
-
|
|
252
|
+
The referenced file must be a Jekyll page with YAML front matter (an empty `---` block is sufficient) and must not be excluded from the build. Its Markdown content is embedded without its layout; the page can also generate its own standalone URL. Missing page references are silently omitted, preserving existing behavior.
|
|
253
|
+
|
|
254
|
+
`content_file` does not run a separate Liquid rendering pass. Results for Liquid expressions can depend on page rendering order, so use `content_include` whenever the content contains Liquid.
|
|
255
|
+
|
|
256
|
+
To migrate, move reusable content from `about.md` to `_includes/fragments/about.md`, remove its YAML front matter, and replace `content_file: about.md` with `content_include: fragments/about.md`. If you still need a standalone About page, keep `about.md` with its front matter and include the fragment in its body:
|
|
257
|
+
|
|
258
|
+
```liquid
|
|
259
|
+
{% include fragments/about.md %}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Use a Markdown page for this wrapper so Jekyll converts the included Markdown normally. Remove an old standalone page only if you no longer need its URL.
|
|
147
263
|
|
|
148
264
|
### Favicon
|
|
149
265
|
|
|
@@ -171,6 +287,27 @@ Note: the theme uses `@import` (not `@use`) to stay compatible with GitHub Pages
|
|
|
171
287
|
|
|
172
288
|
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`.
|
|
173
289
|
|
|
290
|
+
### Responsive Content
|
|
291
|
+
|
|
292
|
+
Body text defaults to `1rem`, following the browser's default font size
|
|
293
|
+
instead of fixing it at 16 pixels. When overriding `--font-size-base`, use
|
|
294
|
+
a relative value such as `1rem` or `1.125rem` to preserve that preference.
|
|
295
|
+
|
|
296
|
+
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.
|
|
297
|
+
|
|
298
|
+
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.
|
|
299
|
+
|
|
300
|
+
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`:
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
theme_config:
|
|
304
|
+
table_scroll_label: "Table"
|
|
305
|
+
code_scroll_label: "Code block"
|
|
306
|
+
scroll_hint: "Scroll horizontally to see more."
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
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.
|
|
310
|
+
|
|
174
311
|
### Per-Page JavaScript
|
|
175
312
|
|
|
176
313
|
To load custom JavaScript files on a specific page, add a `custom_js` list to the page's front matter:
|
|
@@ -188,6 +325,115 @@ Each entry is a filename (without `.js`) relative to `assets/js/`. The example a
|
|
|
188
325
|
|
|
189
326
|
## Contributing
|
|
190
327
|
|
|
328
|
+
### Running Tests
|
|
329
|
+
|
|
330
|
+
```sh
|
|
331
|
+
bundle install
|
|
332
|
+
npm ci
|
|
333
|
+
npx playwright install --with-deps chromium firefox webkit
|
|
334
|
+
bundle exec ruby _tests/release_workflow_test.rb
|
|
335
|
+
bundle exec ruby _tests/release_tag_test.rb
|
|
336
|
+
bundle exec ruby _tests/home_layout_test.rb
|
|
337
|
+
bundle exec ruby _tests/localization_test.rb
|
|
338
|
+
JEKYLL_SAFE=1 bundle exec ruby _tests/localization_test.rb
|
|
339
|
+
bundle exec ruby _tests/menu_layout_test.rb
|
|
340
|
+
JEKYLL_SAFE=1 bundle exec ruby _tests/menu_layout_test.rb
|
|
341
|
+
bundle exec ruby _tests/menu_content_test.rb
|
|
342
|
+
JEKYLL_SAFE=1 bundle exec ruby _tests/menu_content_test.rb
|
|
343
|
+
npm run test:browser
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
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.
|
|
347
|
+
|
|
348
|
+
Text-sizing checks enlarge the root font to 200% at each viewport width,
|
|
349
|
+
with and without JavaScript. A Firefox-specific test also changes the
|
|
350
|
+
actual browser default-font preference to 32 pixels. These are focused
|
|
351
|
+
regression checks, not a complete WCAG conformance audit.
|
|
352
|
+
|
|
353
|
+
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.
|
|
354
|
+
|
|
355
|
+
Menu content checks build root and `/blog` sites in normal and safe mode. They cover Liquid-before-Markdown rendering, nested includes and host pages, recursive sections, literal Liquid examples, legacy content sources, and invalid include paths.
|
|
356
|
+
|
|
357
|
+
Localization checks cover language inheritance across all four layouts, page overrides, blank-value fallbacks, escaped attributes, translated labels, and unchanged skip-link destinations in normal and safe mode.
|
|
358
|
+
|
|
359
|
+
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.
|
|
360
|
+
|
|
361
|
+
### Native GitHub Pages Checks
|
|
362
|
+
|
|
363
|
+
The Pages tests use a separate Gemfile and lockfile so the theme's Jekyll 4.4
|
|
364
|
+
dependency cannot affect the native Pages graph. CI runs them on Ruby 3.3.
|
|
365
|
+
Install and run them with:
|
|
366
|
+
|
|
367
|
+
```sh
|
|
368
|
+
BUNDLE_GEMFILE=_tests/pages/Gemfile bundle install
|
|
369
|
+
BUNDLE_GEMFILE=_tests/pages/Gemfile bundle exec \
|
|
370
|
+
ruby _tests/pages/consumer_test.rb
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
This bundle pins GitHub Pages 232, Jekyll 3.10.0, Sass converter 1.5.2, and
|
|
374
|
+
the other published Pages dependencies. The test invokes the real Pages
|
|
375
|
+
build command with its safe-mode configuration and default plugins. It
|
|
376
|
+
checks root and `/blog` sites, layouts, menu data, nested Liquid includes,
|
|
377
|
+
recursive headings, language settings, compiled CSS, scripts, favicons,
|
|
378
|
+
feeds, sitemap URLs, and rejection of a broken theme archive.
|
|
379
|
+
|
|
380
|
+
Only the theme-download HTTP response is supplied locally, using a ZIP of
|
|
381
|
+
the checkout. The real `jekyll-remote-theme` plugin downloads, extracts, and
|
|
382
|
+
loads it. No live GitHub requests or installed `monoholic` gem are used.
|
|
383
|
+
The generated lockfile retains Bundler's checksum format.
|
|
384
|
+
|
|
385
|
+
Review the [published dependency matrix](https://pages.github.com/versions/)
|
|
386
|
+
when updating this bundle. Update its Gemfile, lockfile, and expected versions
|
|
387
|
+
in `_tests/pages/consumer_test.rb` together.
|
|
388
|
+
|
|
389
|
+
### Release Validation
|
|
390
|
+
|
|
391
|
+
Pull requests and release-tag pushes run gem checks on Ruby 3.3 and 3.4,
|
|
392
|
+
plus the separate native Pages check on Ruby 3.3. Publishing requires all
|
|
393
|
+
three jobs to succeed and is restricted to pushed `v*` tags; pull requests
|
|
394
|
+
never publish. The tag must be `v` followed by the exact gem version in
|
|
395
|
+
`monoholic.gemspec`, including any RubyGems prerelease suffix. Update the
|
|
396
|
+
version metadata and lockfiles together before tagging.
|
|
397
|
+
|
|
398
|
+
Each gem-validation job builds the gem once and installs it into a temporary
|
|
399
|
+
gem home. A clean consumer's Gemfile declares only `monoholic`; Bundler resolves
|
|
400
|
+
its runtime dependencies from installed gems without downloading gems or
|
|
401
|
+
falling back to repository theme files. The checks run
|
|
402
|
+
`bundle exec jekyll build` for root and `/blog` sites and verify layouts,
|
|
403
|
+
compiled CSS, scripts, favicons, feeds, and URLs. Negative tests reject broken
|
|
404
|
+
packages and invalid versions. ERB must be declared as a runtime dependency,
|
|
405
|
+
even on hosts that provide it as a default gem; this is checked on both Rubies.
|
|
406
|
+
|
|
407
|
+
Consumer builds use resolved Ruby and gem executable paths, with no Jekyll
|
|
408
|
+
wrapper available on `PATH`. This also covers vendored CI installations
|
|
409
|
+
without relying on globally installed gem executables.
|
|
410
|
+
|
|
411
|
+
After validation, the Ruby 3.3 gem job uploads its gem and SHA-256 manifest
|
|
412
|
+
as a seven-day artifact; the other jobs do not upload a competing release
|
|
413
|
+
artifact. Once both gem jobs and native Pages pass, publishing downloads
|
|
414
|
+
those same bytes, verifies the checksum and package name/version, and pushes
|
|
415
|
+
the same gem to GitHub Packages and RubyGems—without checking out or rebuilding
|
|
416
|
+
the source. Rerunning validation replaces the artifact only after checks
|
|
417
|
+
succeed; if it has expired, rerun validation rather than just publishing.
|
|
418
|
+
|
|
419
|
+
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.
|
|
420
|
+
|
|
421
|
+
To exercise package checks locally without publishing (publisher verification requires Bash and `sha256sum` from GNU coreutils, matching the Linux CI runner):
|
|
422
|
+
|
|
423
|
+
```sh
|
|
424
|
+
mkdir -p _tests/.release
|
|
425
|
+
bundle exec gem build monoholic.gemspec --output _tests/.release/monoholic.gem
|
|
426
|
+
bundle exec ruby _tests/package_consumer_test.rb _tests/.release/monoholic.gem
|
|
427
|
+
bundle exec ruby _tests/package_consumer_negative_test.rb _tests/.release/monoholic.gem
|
|
428
|
+
(
|
|
429
|
+
cd _tests/.release
|
|
430
|
+
sha256sum monoholic.gem > monoholic.gem.sha256
|
|
431
|
+
)
|
|
432
|
+
bundle exec ruby _tests/package_integrity_test.rb _tests/.release/monoholic.gem _tests/.release/monoholic.gem.sha256
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
`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.
|
|
436
|
+
|
|
191
437
|
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.
|
|
192
438
|
|
|
193
439
|
## License
|
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,33 +42,40 @@
|
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
{%- endif -%}
|
|
73
|
+
{%- if item.content_include -%}
|
|
74
|
+
{%- capture _menu_content -%}
|
|
75
|
+
{%- include {{ item.content_include }} -%}
|
|
76
|
+
{%- endcapture -%}
|
|
77
|
+
{{ _menu_content | markdownify }}
|
|
78
|
+
{%- elsif item.content_file -%}
|
|
68
79
|
{%- assign _menu_page = site.pages
|
|
69
80
|
| where: 'path', item.content_file
|
|
70
81
|
| first
|
|
@@ -77,7 +88,8 @@
|
|
|
77
88
|
{%- endif -%}
|
|
78
89
|
{%- if item.post_list -%}{%- include post_list.html -%}{%- endif -%}
|
|
79
90
|
{%- if item.entries -%}
|
|
80
|
-
{%-
|
|
91
|
+
{%-
|
|
92
|
+
include menu.html
|
|
81
93
|
collection=item.entries
|
|
82
94
|
level=_next_level
|
|
83
95
|
parent_id=_heading_id
|
data/_layouts/default.html
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
{%- assign _page_lang = page.lang | default: '' | strip -%}
|
|
2
|
+
{%- assign _site_lang = site.lang | default: '' | strip -%}
|
|
3
|
+
{%- assign _skip_link_label = site.theme_config.skip_link_label
|
|
4
|
+
| default: ''
|
|
5
|
+
| strip
|
|
6
|
+
-%}
|
|
1
7
|
<!doctype html>
|
|
2
|
-
<html lang="{{
|
|
8
|
+
<html lang="{{ _page_lang | default: _site_lang | default: 'en' | escape }}">
|
|
3
9
|
{%- include head.html -%}
|
|
4
10
|
<body class="{% if site.theme_config.monochrome_images != false %}images-monochrome{% endif %}">
|
|
5
|
-
<a class="skip-link" href="#main">
|
|
11
|
+
<a class="skip-link" href="#main">
|
|
12
|
+
{{- _skip_link_label | default: 'Skip to content' | escape -}}
|
|
13
|
+
</a>
|
|
6
14
|
<div class="wrapper">
|
|
7
15
|
{%- unless page.layout == 'post' -%}
|
|
8
16
|
{%- include header.html -%}
|
|
@@ -16,6 +24,14 @@
|
|
|
16
24
|
{%- include footer.html -%}
|
|
17
25
|
{%- endunless -%}
|
|
18
26
|
|
|
27
|
+
<script
|
|
28
|
+
defer
|
|
29
|
+
src="{{ '/assets/js/content-scroll.js' | relative_url }}"
|
|
30
|
+
data-table-label="{{ site.theme_config.table_scroll_label | default: 'Table' | escape }}"
|
|
31
|
+
data-code-label="{{ site.theme_config.code_scroll_label | default: 'Code block' | escape }}"
|
|
32
|
+
data-scroll-hint="{{ site.theme_config.scroll_hint | default: 'Scroll horizontally to see more.' | escape }}"
|
|
33
|
+
></script>
|
|
34
|
+
|
|
19
35
|
{% if page.custom_js %}
|
|
20
36
|
{% for js_file in page.custom_js %}
|
|
21
37
|
<script
|
data/_layouts/home.html
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
3
|
---
|
|
4
|
-
|
|
4
|
+
{{ content }}
|
|
5
|
+
|
|
6
|
+
{%- assign _site_sections_label = site.theme_config.site_sections_label
|
|
7
|
+
| default: ''
|
|
8
|
+
| strip
|
|
9
|
+
-%}
|
|
10
|
+
<nav aria-label="{{ _site_sections_label | default: 'Site sections' | escape }}">
|
|
5
11
|
{%- include menu.html collection=site.data.menu.entries level=2 -%}
|
|
6
12
|
</nav>
|
data/_sass/_base.scss
CHANGED
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/_variables.scss
CHANGED
|
@@ -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,15 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: monoholic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.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-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: erb
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.2'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '7.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '2.2'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
13
33
|
- !ruby/object:Gem::Dependency
|
|
14
34
|
name: jekyll
|
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,6 +86,20 @@ dependencies:
|
|
|
66
86
|
- - "~>"
|
|
67
87
|
- !ruby/object:Gem::Version
|
|
68
88
|
version: '2.8'
|
|
89
|
+
- !ruby/object:Gem::Dependency
|
|
90
|
+
name: jekyll-sitemap
|
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1.4'
|
|
96
|
+
type: :runtime
|
|
97
|
+
prerelease: false
|
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '1.4'
|
|
69
103
|
description:
|
|
70
104
|
email:
|
|
71
105
|
- mammadovagil@proton.me
|
|
@@ -96,6 +130,8 @@ files:
|
|
|
96
130
|
- assets/favicon.ico
|
|
97
131
|
- assets/favicon.svg
|
|
98
132
|
- assets/js/.keep
|
|
133
|
+
- assets/js/content-scroll.js
|
|
134
|
+
- robots.txt
|
|
99
135
|
homepage: https://github.com/stiermid/monoholic
|
|
100
136
|
licenses:
|
|
101
137
|
- GPL-3.0-only
|