jekyll-theme-modootoday 1.1.0 → 1.1.1

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/README.ko.md +2 -1
  4. data/README.md +2 -1
  5. data/RULES.md +7 -2
  6. data/_includes/modootoday/breadcrumb.html +1 -1
  7. data/_includes/modootoday/config.html +5 -4
  8. data/_includes/modootoday/head-meta.html +4 -2
  9. data/_includes/modootoday/jsonld/breadcrumb.html +1 -1
  10. data/_includes/modootoday/jsonld/publisher.html +1 -1
  11. data/_includes/modootoday/jsonld/website.html +1 -1
  12. data/_includes/modootoday/lang-switch.html +4 -2
  13. data/_includes/modootoday/link.html +5 -2
  14. data/_includes/modootoday/markdown/lists.html +26 -0
  15. data/_includes/modootoday/markdown/sections.html +20 -6
  16. data/_includes/modootoday/ref-url.html +12 -0
  17. data/_includes/modootoday/stylesheets.html +1 -0
  18. data/_layouts/default.html +4 -2
  19. data/_sass/modootoday/base/_prose.scss +1 -0
  20. data/_sass/modootoday/components/core/inline/_figure.scss +3 -0
  21. data/_sass/modootoday/components/core/inline/_hero.scss +3 -0
  22. data/_sass/modootoday/components/core/inline/_reveal.scss +10 -0
  23. data/_sass/modootoday/components/lists/_lists.scss +16 -0
  24. data/_sass/modootoday/components/section/showcase/_card.scss +3 -0
  25. data/_sass/modootoday/components/section/structure/_split.scss +14 -0
  26. data/_sass/modootoday.scss +2 -0
  27. data/assets/css/modootoday/lists.scss +8 -0
  28. data/assets/css/modootoday/structure/split.scss +7 -0
  29. data/i18n/glossary.yml +3 -0
  30. data/lib/modootoday/check/a11y/illustration.rb +24 -0
  31. data/lib/modootoday/check/cli.rb +3 -2
  32. data/lib/modootoday/check/component_rules.rb +3 -1
  33. data/lib/modootoday/check/i18n.rb +43 -0
  34. data/lib/modootoday/check/page_sections.rb +13 -6
  35. data/lib/modootoday/check/page_syntax.rb +6 -3
  36. data/lib/modootoday/check/pages.rb +1 -0
  37. data/lib/modootoday/check/site_config.rb +15 -1
  38. data/lib/modootoday/check/structured_data.rb +13 -0
  39. metadata +9 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2d46e35ab0076998d0b4c63e7467f127e6c3821762af5deb6fae73b38300a70
4
- data.tar.gz: 975bd03dc80968327a2cbdb253c2fea15340a57864c2163739382976e9ce462a
3
+ metadata.gz: 497fb07373c85ca822bcf41f91d4d0703a01209ecaa819b9b7ac7ed98471e1cd
4
+ data.tar.gz: b71c8b2a29a3b01a081330e0d09c0db0233b3a6f2d53224ffa5bad8f65f6c167
5
5
  SHA512:
6
- metadata.gz: 8eb8f3fb6b6d43b27a1ba9b10513460df6d402acca489dc450da43b41d86f68a9d3c85ad4ba16e4fda29792cfde9dd9b9708c6cbf9c7c6f3f2f77f040c191ee0
7
- data.tar.gz: fa3eab12b29e6b53f6ffcb3717ca9df1316a17008038aeaf2d5da67b4cbffd28f10f5ed69c4e92c2f08f43c454309f9a60a55b394003ddf50743f49883ddc8b5
6
+ metadata.gz: ed3f80481897f18f963d7cc0bc3874bb9c80aaa2d5cb4e2cb75a2ffd9150cae1cd276d14f50239a380206c5eb1c9815a58353087cdc58ebe087de17f8ad5ae4b
7
+ data.tar.gz: 036c0ae7ee9ed578162fda1812f2ff3e51dacb39619d56e6e006278ac351597f0f47d78da59e95aeb7b4756f33049f6bc5c292f46e947bb55b565313a1c9faa6
data/CHANGELOG.md CHANGED
@@ -6,6 +6,33 @@ The content rules have their own version header (`md-components v1`) in [RULES.m
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.1.1] - 2026-10-31
10
+
11
+ ### Added
12
+
13
+ - Release workflow: after the gem is pushed to RubyGems, a GitHub Release is created for the tag with its `CHANGELOG.md` entry as the notes (`script/release-notes`); skipped when the Release already exists.
14
+ - `mt-flat-list`: put it on an ancestor of an unclassed Markdown list that a layout makes a grid or flex row (`.c-body > ul`) to drop the gap between list items (RULES.md §6, Customize).
15
+ - `badge: bottom` on a `c: features` section (`.mt-badge-bottom`): a status badge at the end of a card item sits at the card's bottom edge, so badges line up across a row.
16
+ - `reveal: true` on `hero:` or a section item (`.mt-reveal`): a CSS-only fade-in on scroll, inside `@supports (animation-timeline: view())` and `prefers-reduced-motion: no-preference`. Without support, or when the reader prefers less motion, the content is shown as is. No script.
17
+ - Optional list styles: `modootoday.lists: {link_chips: true, titled_items: true}` turns link-only lists on plain sections and pages into `ul.mt-chips` and `- **Title**: text` items into `strong.mt-item-title` + `span.mt-item-sep`; `hero: {checks: true}` makes the hero's first list a row of ✓ marks (`--mt-ok`). They use a new `lists` sheet (under 1.25 KiB) linked only where used; core CSS is unchanged. The hero lead paragraph gets `.mt-hero-lead`.
18
+ - `decor: <include>` on `hero:` or a section item: an `aria-hidden` ornament in `.mt-section-decor`, placed in the top end corner. `modootoday-check` reports a missing include.
19
+ - `.mt-illustration` convention: `figure[role=img]` with a non-empty `aria-label` and an optional `.mt-illustration-tag` badge; `modootoday-check` reports any other markup in the built site.
20
+ - `ref:` in front matter links translations that live under different paths: `hreflang` and the language switcher use it, and `modootoday/link.html` with `ref=` gives that page's URL in the current language, or in the default language. `modootoday-check` reports a `ref=` that no page defines.
21
+ - `modootoday.full_languages` and `modootoday-check --i18n-pages`: warnings for default-language pages with no translation in those languages; `--strict` makes them fail.
22
+ - `c: split` section: text and its `visual:` include side by side from 48rem, stacked below; params `side: start|end` and `ratio: "1:1"|"5:7"`, inner wrappers `.mt-section-in`, `.mt-section-body`, `.mt-section-visual` (split only, so other sections keep their markup), its own section sheet. `modootoday-check` requires `visual:` on a split.
23
+ - `visual:` include: any other key on the section item reaches the include as `include.section.<key>`, and `modootoday-check` accepts such keys on a section that has `visual:`.
24
+ - Docs: moving from `jekyll-seo-tag` to the kit's SEO tags (Migrate), and what to run when `modootoday-check` is not on `PATH` after `gem install` (Install).
25
+
26
+ ### Changed
27
+
28
+ - Brand: the visible site name is now **modoo.today theme** (Korean **모두의오늘 테마**, Japanese **modoo.today テーマ**, other languages localized) in the docs header, `<title>`, `og:site_name`, JSON-LD and breadcrumbs, so it is not confused with the modoo.today service. A new optional `site_title` in `_data/i18n/<lang>.yml` sets the header name per language and falls back to `site.title`. The gem name, repository paths, CSS classes, config keys and `modootoday-check` are unchanged.
29
+ - Size budgets have a little more headroom (QA, `qa/audit/config.mjs`): core CSS sheet 24 → 27 KiB, core JS 10 → 11 KiB, CSS one page links 32 → 35 KiB, `loading.css` 2 → 2.5 KiB, `figure.css` 1 → 1.25 KiB, each renderer script 2 → 2.5 KiB. Section sheets stay at 2 KiB. The built sizes did not grow (core CSS 24531 B, core JS 10227 B).
30
+
31
+ ### Fixed
32
+
33
+ - Checker: specfold value markers with arguments after the name (`<!-- spec:graph format=mermaid source=tutorial -->`, `<!-- spec:count rows="All commands" -->`) are no longer reported as HTML comments (RULES.md, HTML comments row). The HTML comment error now quotes the first comment it found.
34
+ - Checker: JSON-LD between the `jekyll-seo-tag` comments (sites with `seo: false` that bring the plugin) is checked only for valid JSON and reported as a warning, so its `WebPage` without `name` is no longer an error; the kit's own block is still checked in full and not counted as a second block (Structured data).
35
+
9
36
  ## [1.1.0] - 2026-10-31
10
37
 
11
38
  ### Added
@@ -200,6 +227,7 @@ Internal development milestones, never released. They are all part of 1.0.0.
200
227
  - Example: `sass.silence_deprecations: [import]` for Dart Sass; `llms.txt` per language generated instead of hand-written.
201
228
  - Raw `<name>.md` copies in `_site` on GitHub Pages: caused by content files without front matter (rendered by `jekyll-optional-front-matter` and also copied). Front matter is now required and checked.
202
229
 
203
- [Unreleased]: https://github.com/modootoday/jekyll-theme-modootoday/compare/v1.1.0...HEAD
230
+ [Unreleased]: https://github.com/modootoday/jekyll-theme-modootoday/compare/v1.1.1...HEAD
231
+ [1.1.1]: https://github.com/modootoday/jekyll-theme-modootoday/releases/tag/v1.1.1
204
232
  [1.1.0]: https://github.com/modootoday/jekyll-theme-modootoday/releases/tag/v1.1.0
205
233
  [1.0.0]: https://github.com/modootoday/jekyll-theme-modootoday/releases/tag/v1.0.0
data/README.ko.md CHANGED
@@ -38,7 +38,7 @@ Gemfile 없이 GitHub Pages에서(`remote_theme:`):
38
38
 
39
39
  ```yaml
40
40
  # _config.yml
41
- remote_theme: modootoday/jekyll-theme-modootoday@v1.1.0
41
+ remote_theme: modootoday/jekyll-theme-modootoday@v1.1.1
42
42
  plugins:
43
43
  - jekyll-remote-theme
44
44
  ```
@@ -79,6 +79,7 @@ bundle exec modootoday-check . _site
79
79
  | `--schema-recommended` | also warn about schema.org properties Google Search recommends (author, image, dates, ...) that a JSON-LD node lacks |
80
80
  | `--strict` | warnings fail the run |
81
81
  | `--i18n` | `_data/i18n/<lang>.yml` against the theme registry (`i18n/en.yml` in the gem): unknown keys, `%{name}` placeholder mismatches, and pages marked `translation: pending` |
82
+ | `--i18n-pages` | warn about default-language pages with no translation in a `modootoday.full_languages` language (the page with the same `ref:`, else the same path under `<lang>/`); with `--strict` they fail |
82
83
  | `--i18n-init=LANG,...` | copy the theme's catalogs (`i18n/<lang>.yml`, 23 languages) into `SRC/_data/i18n/` as a starting point for overrides (optional; the theme already uses them), and list keys an existing catalog lacks |
83
84
  | `--gh` | also render each page with `gh api markdown` (network, `gh` login) |
84
85
  | `--help, -h` | print usage and options (`--help=markdown` prints the README table) |
data/README.md CHANGED
@@ -35,7 +35,7 @@ On GitHub Pages without a Gemfile (`remote_theme:`):
35
35
 
36
36
  ```yaml
37
37
  # _config.yml
38
- remote_theme: modootoday/jekyll-theme-modootoday@v1.1.0
38
+ remote_theme: modootoday/jekyll-theme-modootoday@v1.1.1
39
39
  plugins:
40
40
  - jekyll-remote-theme
41
41
  ```
@@ -76,6 +76,7 @@ Walkthrough: [Write your first page](https://modootoday.github.io/jekyll-theme-m
76
76
  | `--schema-recommended` | also warn about schema.org properties Google Search recommends (author, image, dates, ...) that a JSON-LD node lacks |
77
77
  | `--strict` | warnings fail the run |
78
78
  | `--i18n` | `_data/i18n/<lang>.yml` against the theme registry (`i18n/en.yml` in the gem): unknown keys, `%{name}` placeholder mismatches, and pages marked `translation: pending` |
79
+ | `--i18n-pages` | warn about default-language pages with no translation in a `modootoday.full_languages` language (the page with the same `ref:`, else the same path under `<lang>/`); with `--strict` they fail |
79
80
  | `--i18n-init=LANG,...` | copy the theme's catalogs (`i18n/<lang>.yml`, 23 languages) into `SRC/_data/i18n/` as a starting point for overrides (optional; the theme already uses them), and list keys an existing catalog lacks |
80
81
  | `--gh` | also render each page with `gh api markdown` (network, `gh` login) |
81
82
  | `--help, -h` | print usage and options (`--help=markdown` prints the README table) |
data/RULES.md CHANGED
@@ -14,6 +14,10 @@ Every content page starts with front matter; an empty block (`---` then `---`) i
14
14
  |---|---|---|---|
15
15
  | Alerts (5) | top-level quote whose first line is exactly `> [!NOTE]` (TIP/IMPORTANT/WARNING/CAUTION; any case), body on the following `>` lines. The body may start with a paragraph, a blank `>` line, a list, a code block or a `###` heading. Alerts are a **GitHub extension outside the GFM spec**; other parsers show a plain quote | `_includes/modootoday/markdown/components.html` converts kramdown's `<p>[!TYPE]` + body, standalone `<p>[!TYPE]</p>` and `[!TYPE]<br />` (trailing spaces) into `blockquote.mt-callout.mt-callout-<type>[role=note]` with a localised title | quote / GitHub alert / source |
16
16
  | Sections | `# Title`, lead paragraph, link-only paragraph (CTA), then `## Section`; front matter `sections:` | `doc` layout → `_includes/modootoday/markdown/sections.html`: with `sections:` the part before the first `##` is `section.mt-hero` and item *i* wraps the *i*-th **top-level** `##` in `section.mt-section` (an `h2` nested in a quote, list or alert never splits); without it, one `div.mt-prose` | plain document |
17
+ | Section decor | front matter `decor: <include>` on `hero:` or a section item | the include inside `div.mt-section-decor[aria-hidden=true]` at the end of the section, which gets `.mt-has-decor` | not shown |
18
+ | List styles (optional) | `modootoday.lists: {link_chips, titled_items}`; `hero: {checks: true}` | plain sections and pages: link-only list → `ul.mt-chips`, `- **Title**: text` → `strong.mt-item-title` + `span.mt-item-sep`; hero: `.mt-hero-checks`; first `p` after the `h1` → `p.mt-hero-lead` | plain lists |
19
+ | Scroll reveal | front matter `reveal: true` on `hero:` or a section item | `.mt-reveal` on the section; CSS-only fade-in on scroll inside `@supports (animation-timeline: view())` and `prefers-reduced-motion: no-preference` | shown as is |
20
+ | Card badge at the bottom | `badge: bottom` on a `c: features` item | `.mt-badge-bottom` on the section; a `.mt-badge` that is a direct child of a card sits at its bottom edge | badge after the text |
17
21
  | Section mapping | each `sections:` item is a string `name` or `{c: component, name: key, ...params}`. No implicit mapping. Item count ≠ top-level `## ` count is a checker error; setext `---` headings, `##` in quotes/alerts/list items, `##` indented 1–3 spaces and `##` inside HTML comments are checker errors | `data-section`=name, `data-c`=c, class `mt-c-<c>` | none |
18
22
  | Card grid | under `##`, list items `- **Title** — text` or `- **Title**: text` | CSS only | list |
19
23
  | CTA buttons | every link-only paragraph in `section.mt-hero` and in `c: cta` sections; the first link is primary. Separator text such as `·` between links is a text node, so the paragraph still counts as link-only | CSS `p:has(> a):not(:has(> :not(a)))` | links |
@@ -39,7 +43,7 @@ Every content page starts with front matter; an empty block (`---` then `---`) i
39
43
  | Progress | `` `progress:60` `` (whole number 0–100) | `span.mt-progress` > `progress` + value text (i18n `progress.label`) | out of range is a checker error |
40
44
  | Embed | a paragraph holding only `` `embed:https://… Title` `` | `div.mt-embed` > lazy `iframe[title]` (16:9 reserved) | non-https is a checker error |
41
45
  | Loading / skeleton | `` `loading:spinner` ``, `` `loading:bar` ``, `` `skeleton:text` `` / `card` / `image` / `avatar` / `row` | `span.mt-spinner`, `span.mt-topbar`, `span.mt-skeleton` with `role=status`, `aria-busy` and i18n `loading.label`; optional `loading` sheet; reduced motion, forced colors and RTL | unknown variants are checker errors |
42
- | File tree / Details / FAQ / Params / Link cards / Quote / Gallery / Pricing / Subscribe | `c: filetree` (nested list), `c: details` and `c: faq` (`###` per item), `c: params` (table), `c: linkcards` (link-first list), `c: quote` (quote ending in `— name`), `c: gallery` (tight image list), `c: pricing` (`###` per plan, param `recommended`), `c: subscribe` (text; form posts to `modootoday.subscribe.action`) | per-component markup listed in docs api.md; each has its own sheet | gallery images need alt; `recommended` must be in range; subscribe action must be https |
46
+ | File tree / Details / FAQ / Params / Link cards / Quote / Gallery / Pricing / Subscribe / Split | `c: filetree` (nested list), `c: details` and `c: faq` (`###` per item), `c: params` (table), `c: linkcards` (link-first list), `c: quote` (quote ending in `— name`), `c: gallery` (tight image list), `c: pricing` (`###` per plan, param `recommended`), `c: subscribe` (text; form posts to `modootoday.subscribe.action`), `c: split` (text beside its `visual:`, params `side`, `ratio`; the only section with inner wrappers) | per-component markup listed in docs api.md; each has its own sheet | gallery images need alt; `recommended` must be in range; subscribe action must be https; split needs `visual:` |
43
47
  | Component registry | optional `_data/components.yml`: `name: {params: {key: {allowed: [...], required: bool}}, i18n: [keys...]}` | when present the checker validates `c`, params and i18n keys | — |
44
48
 
45
49
  ## 3. Localisation
@@ -55,7 +59,7 @@ The bans are about **portability**: the same source must look the same on github
55
59
  | Forbidden | Why | Check |
56
60
  |---|---|---|
57
61
  | raw HTML (`<div>`, `<details>`, `<kbd>` …) | not portable: GitHub renders some tags (`details`, `kbd`) and strips or escapes others (`script`, `iframe`), kramdown passes everything through; keeps twins readable | checker |
58
- | HTML comments `<!-- -->` | hidden on GitHub but emitted by kramdown; a `##` inside one shifts section counts. Exceptions: a tool region marker alone on its line, `<!-- tool:name:begin -->` / `<!-- tool:name:end -->` (lowercase, ends in `:begin` or `:end`); a one-line specfold value marker, own-line or inline, `<!-- spec:name -->` / `<!-- /spec:name -->` (lowercase letters, digits, `.`, `_`, `-`, `:`-separated) | checker |
62
+ | HTML comments `<!-- -->` | hidden on GitHub but emitted by kramdown; a `##` inside one shifts section counts. Exceptions: a tool region marker alone on its line, `<!-- tool:name:begin -->` / `<!-- tool:name:end -->` (lowercase, ends in `:begin` or `:end`); a one-line specfold value marker, own-line or inline, `<!-- spec:name -->` / `<!-- /spec:name -->` (lowercase letters, digits, `.`, `_`, `-`, `:`-separated), for example `<!-- spec:count -->12<!-- /spec:count -->`; arguments may follow the name after a space, `<!-- spec:count rows="All commands" -->12<!-- /spec:count -->`, on the same line and without `>`, `--` or `##` | checker |
59
63
  | Liquid `{% %}` `{{ }}` | breaks on GitHub and in twins | checker |
60
64
  | kramdown IAL/ALD `{: .x}`, `{:toc}` | kramdown-only | checker |
61
65
  | header ids `{#id}` | kramdown-only (literal text on GitHub) | checker |
@@ -114,6 +118,7 @@ Stub and generator details:
114
118
  - Liquid inside code (`{{ }}`) is still processed by Jekyll before kramdown; such pages need `{% raw %}` on the Jekyll side, which itself shows on GitHub.
115
119
  - The stylesheet uses Sass `@import` so it compiles with both Ruby Sass (GitHub Pages) and Dart Sass (Jekyll 4); Dart Sass prints deprecation warnings unless the site sets `sass: {silence_deprecations: [import, global-builtin]}` (jekyll-sass-converter ≥ 3.1). Custom properties holding Sass values must interpolate (`--x: #{$v}`); Ruby Sass prints `$v` literally.
116
120
  - Component classes are `mt-*`.
121
+ - Lists without a class get `margin-top: .3em` between items (`:where(:not([class])) > li + li`). A layout or include that turns an unclassed Markdown list into a grid or flex row (for example `.c-body > ul`) puts `mt-flat-list` on an ancestor to drop that gap; a list with any class of its own is already exempt.
117
122
  - CTA buttons need `:has()`; without it they stay links.
118
123
 
119
124
  ## 7. Design tokens artifact
@@ -7,7 +7,7 @@ The home crumb uses the home page short_title, then its title. Hidden on the hom
7
7
  {%- if page.url != _home and page.url != "/" -%}
8
8
  {%- assign _hp = site.pages | where: "url", _home | first -%}
9
9
  <nav class="mt-breadcrumb" aria-label="{% include modootoday/i18n.html lang=_lang %}{{ mt_t_breadcrumb_label }}"><ol>
10
- <li><a href="{{ _home | relative_url }}">{{ _hp.short_title | default: _hp.title | default: site.title | default: "Home" }}</a></li>
10
+ <li><a href="{{ _home | relative_url }}">{{ _hp.short_title | default: _hp.title | default: mt_site_title | default: site.title | default: "Home" }}</a></li>
11
11
  {%- assign _acc = _home -%}
12
12
  {%- for _s in _segs offset: 1 -%}{%- if forloop.index > _start and _s != "" and _s != "index.html" -%}
13
13
  {%- assign _acc = _acc | append: _s | append: "/" -%}
@@ -10,15 +10,16 @@ Jekyll 3.10 / GitHub Pages, so every default lives here (Liquid). Sets mt_* vari
10
10
  site.modootoday.tokens / tokens_dark semantic token overrides, inline :root CSS
11
11
  site.modootoday.sidebar auto | true | false: doc-page sidebar from the nav (default auto: on when the nav has a group)
12
12
  site.modootoday.badges false disables status badges (default true)
13
+ site.modootoday.lists {link_chips: true, titled_items: true}: optional list markup (markdown/lists.html, lists sheet)
13
14
  {%- endcomment -%}
14
15
  {%- assign mt = site.modootoday -%}
15
- {%- assign mt_all_components = "callout,badge,hero,cta,card,tabs,code-copy,nav,sidebar,page-actions,pagination,theme-toggle,breadcrumb,toc,accordion,steps,figure,timeline,kbd,search,code,footnotes,progress,embed,banner,filetree,details,faq,params,linkcards,quote,gallery,pricing,subscribe,loading,figure-svg" | split: "," -%}
16
+ {%- assign mt_all_components = "callout,badge,hero,cta,card,tabs,code-copy,nav,sidebar,page-actions,pagination,theme-toggle,breadcrumb,toc,accordion,steps,figure,timeline,kbd,search,code,footnotes,progress,embed,banner,filetree,details,faq,params,linkcards,quote,gallery,pricing,subscribe,split,loading,figure-svg,lists,reveal" | split: "," -%}
16
17
  {%- comment -%}Sass partials by context (components/<group>/_<name>.scss). Core: always in assets/css/modootoday.css.
17
18
  Section sheets: assets/css/modootoday/<group>/<name>.css, linked when a page's sections: use c (c:sheet).
18
19
  Context sheets: assets/css/modootoday/<name>.css, linked by layout front matter mt_sheets (doc; blog layouts also get assets/css/modootoday-blog.css).{%- endcomment -%}
19
- {%- assign mt_core_partials = "core/inline/callout,core/inline/badge,core/inline/kbd,core/inline/figure,core/inline/code-copy,core/inline/hero,core/inline/cta,core/inline/code,core/inline/footnotes,core/inline/progress,core/inline/embed,core/chrome/nav,core/chrome/theme-toggle,core/chrome/breadcrumb,core/chrome/pagination,core/chrome/banner" | split: "," -%}
20
- {%- assign mt_section_sheets = "cta:showcase/cta,features:showcase/card,linkcards:showcase/linkcards,quote:showcase/quote,gallery:showcase/gallery,pricing:showcase/pricing,subscribe:showcase/subscribe,tabs:structure/tabs,accordion:structure/accordion,steps:structure/steps,timeline:structure/timeline,filetree:structure/filetree,details:structure/details,faq:structure/faq,params:structure/params" | split: "," -%}
21
- {%- assign mt_context_sheets = "doc:sidebar|toc|page-actions,search:search,loading:loading,figure:figure-svg" | split: "," -%}
20
+ {%- assign mt_core_partials = "core/inline/callout,core/inline/badge,core/inline/kbd,core/inline/figure,core/inline/code-copy,core/inline/hero,core/inline/reveal,core/inline/cta,core/inline/code,core/inline/footnotes,core/inline/progress,core/inline/embed,core/chrome/nav,core/chrome/theme-toggle,core/chrome/breadcrumb,core/chrome/pagination,core/chrome/banner" | split: "," -%}
21
+ {%- assign mt_section_sheets = "cta:showcase/cta,features:showcase/card,linkcards:showcase/linkcards,quote:showcase/quote,gallery:showcase/gallery,pricing:showcase/pricing,subscribe:showcase/subscribe,tabs:structure/tabs,accordion:structure/accordion,steps:structure/steps,timeline:structure/timeline,filetree:structure/filetree,details:structure/details,faq:structure/faq,params:structure/params,split:structure/split" | split: "," -%}
22
+ {%- assign mt_context_sheets = "doc:sidebar|toc|page-actions,search:search,loading:loading,figure:figure-svg,lists:lists" | split: "," -%}
22
23
  {%- assign mt_components = mt_all_components -%}
23
24
  {%- if mt.components -%}{%- assign mt_components = mt.components -%}{%- endif -%}
24
25
  {%- assign mt_color_scheme = "auto" -%}
@@ -3,6 +3,7 @@ SEO and theme-init head tags (needs modootoday/config.html first). Liquid only,
3
3
  site.modootoday.seo false drops canonical, hreflang and Open Graph (use jekyll-seo-tag instead)
4
4
  site.modootoday.hreflang true keeps the hreflang alternates when seo is false (default: follows seo)
5
5
  site.modootoday.languages list of language codes for hreflang, e.g. [en, ko]; site.lang is unprefixed, others live under /<lang>/
6
+ (a page with front matter ref: links the pages sharing that ref instead, whatever their paths)
6
7
  site.modootoday.feed true adds the Atom link to /feed.xml, or /<lang>/feed.xml when that page exists (jekyll-feed or layout: feed)
7
8
  site.modootoday.csp strict loads the pre-paint theme script from a file instead of inline
8
9
  site.modootoday.favicon brand links the theme's modootoday mark (SVG, 32px PNG, apple-touch); a path links your own icon
@@ -39,7 +40,8 @@ SEO and theme-init head tags (needs modootoday/config.html first). Liquid only,
39
40
  {%- if _mt_plang != _mt_def -%}{%- assign _mt_pre = "/" | append: _mt_plang -%}{%- assign _mt_base = page.url | remove_first: _mt_pre -%}{%- endif -%}
40
41
  {%- unless mt_page_urls -%}{%- assign mt_page_urls = site.pages | map: "url" | join: "|" | prepend: "|" | append: "|" -%}{%- endunless -%}
41
42
  {%- for _l in _mt_langs -%}
42
- {%- if _l == _mt_def -%}{%- assign _mt_alt = _mt_base -%}{%- else -%}{%- assign _mt_alt = "/" | append: _l | append: _mt_base -%}{%- endif -%}
43
+ {%- if page.ref -%}{%- include modootoday/ref-url.html ref=page.ref lang=_l exact=true -%}{%- assign _mt_alt = mt_ref_url | default: "/-none-/" -%}
44
+ {%- elsif _l == _mt_def -%}{%- assign _mt_alt = _mt_base -%}{%- else -%}{%- assign _mt_alt = "/" | append: _l | append: _mt_base -%}{%- endif -%}
43
45
  {%- assign _mt_key = "|" | append: _mt_alt | append: "|" -%}
44
46
  {%- if mt_page_urls contains _mt_key %}
45
47
  <link rel="alternate" hreflang="{{ _l }}" href="{{ _mt_alt | absolute_url }}">
@@ -68,7 +70,7 @@ SEO and theme-init head tags (needs modootoday/config.html first). Liquid only,
68
70
  <meta property="og:description" content="{{ page.description | default: site.description | strip_html | normalize_whitespace | escape }}">
69
71
  {%- endif -%}
70
72
  {%- if site.title %}
71
- <meta property="og:site_name" content="{{ site.title | escape }}">
73
+ <meta property="og:site_name" content="{{ mt_site_title | default: site.title | escape }}">
72
74
  {%- endif -%}
73
75
  {%- if site.url %}
74
76
  <meta property="og:url" content="{{ _mt_url }}">
@@ -4,7 +4,7 @@ is a page, then the page. Not printed on the home page or a /<lang>/ home.{%- en
4
4
  {%- if _ld_segs[1] == _ld_lang and _ld_lang != site.lang -%}{%- assign _ld_home = "/" | append: _ld_lang | append: "/" -%}{%- assign _ld_start = 1 -%}{%- endif -%}
5
5
  {%- if page.url != _ld_home and page.url != "/" -%}
6
6
  {%- assign _ld_hp = site.pages | where: "url", _ld_home | first -%}{%- assign _ld_n = 1 -%}
7
- ,{"@type":"BreadcrumbList","@id":{{ _ld_url | append: "#breadcrumb" | jsonify }},"itemListElement":[{"@type":"ListItem","position":1,"name":{{ _ld_hp.short_title | default: _ld_hp.title | default: site.title | default: "Home" | strip_html | jsonify }},"item":{{ _ld_home | absolute_url | jsonify }}}
7
+ ,{"@type":"BreadcrumbList","@id":{{ _ld_url | append: "#breadcrumb" | jsonify }},"itemListElement":[{"@type":"ListItem","position":1,"name":{{ _ld_hp.short_title | default: _ld_hp.title | default: mt_site_title | default: site.title | default: "Home" | strip_html | jsonify }},"item":{{ _ld_home | absolute_url | jsonify }}}
8
8
  {%- assign _ld_acc = _ld_home -%}
9
9
  {%- for _s in _ld_segs offset: 1 -%}{%- if forloop.index > _ld_start and _s != "" and _s != "index.html" -%}
10
10
  {%- assign _ld_acc = _ld_acc | append: _s | append: "/" -%}
@@ -3,7 +3,7 @@ Defaults: Organization named site.title; logo from publisher.logo, or the brand
3
3
  {%- assign _ld_p = _ld.publisher -%}
4
4
  {%- assign _ld_pt = _ld_p.type | default: "Organization" -%}
5
5
  {%- assign _ld_pl = _ld_p.logo -%}{%- if _ld_pl == nil and site.modootoday.logo == "brand" -%}{%- assign _ld_pl = "/assets/modootoday/apple-touch-icon.png" -%}{%- elsif _ld_pl == nil -%}{%- assign _ld_pl = site.modootoday.logo -%}{%- endif -%}
6
- ,{"@type":{{ _ld_pt | jsonify }},"@id":{{ _ld_root | append: "#publisher" | jsonify }},"name":{{ _ld_p.name | default: site.title | default: _ld_title | jsonify }},"url":{{ _ld_p.url | default: "/" | absolute_url | jsonify }}
6
+ ,{"@type":{{ _ld_pt | jsonify }},"@id":{{ _ld_root | append: "#publisher" | jsonify }},"name":{{ _ld_p.name | default: mt_site_title | default: site.title | default: _ld_title | jsonify }},"url":{{ _ld_p.url | default: "/" | absolute_url | jsonify }}
7
7
  {%- if _ld_pl -%}{%- assign _ld_plu = _ld_pl | absolute_url -%}
8
8
  {%- if _ld_pt == "Person" %},"image":{{ _ld_plu | jsonify }}{% else %},"logo":{"@type":"ImageObject","url":{{ _ld_plu | jsonify }}}{% endif -%}
9
9
  {%- endif -%}
@@ -1,5 +1,5 @@
1
1
  {%- comment -%}WebSite node (#website). site.modootoday.schema.search_url (a URL with {search_term_string}) adds a SearchAction.{%- endcomment -%}
2
- {"@type":"WebSite","@id":{{ _ld_root | append: "#website" | jsonify }},"url":{{ _ld_root | jsonify }},"name":{{ site.title | default: _ld_title | jsonify }}
2
+ {"@type":"WebSite","@id":{{ _ld_root | append: "#website" | jsonify }},"url":{{ _ld_root | jsonify }},"name":{{ mt_site_title | default: site.title | default: _ld_title | jsonify }}
3
3
  {%- if site.description %},"description":{{ site.description | strip_html | normalize_whitespace | jsonify }}{% endif -%}
4
4
  ,"inLanguage":{{ site.lang | default: "en" | jsonify }},"publisher":{"@id":{{ _ld_root | append: "#publisher" | jsonify }}}
5
5
  {%- if _ld.search_url -%}
@@ -1,5 +1,6 @@
1
1
  {%- comment -%}
2
- Language switcher, built from modootoday.languages (the same alternates as the hreflang links).
2
+ Language switcher, built from modootoday.languages (the same alternates as the hreflang links; pages with ref: use the
3
+ page sharing that ref in each language).
3
4
  Each language links to this page's translation when it exists, else to that language's home page.
4
5
  Opt in with modootoday.language_switcher: header | footer, or include it in your own footer-custom.html.
5
6
  Label text: language (the language's own name) and languages.label (modootoday/i18n.html: site _data/i18n, theme catalog, English).
@@ -16,7 +17,8 @@
16
17
  {%- unless mt_page_urls -%}{%- assign mt_page_urls = site.pages | map: "url" | join: "|" | prepend: "|" | append: "|" -%}{%- endunless -%}
17
18
  {%- capture _ls_items -%}
18
19
  {%- for _l in site.modootoday.languages -%}
19
- {%- if _l == _ls_def -%}{%- assign _ls_alt = _ls_base -%}{%- else -%}{%- assign _ls_alt = "/" | append: _l | append: _ls_base -%}{%- endif -%}
20
+ {%- if page.ref -%}{%- include modootoday/ref-url.html ref=page.ref lang=_l exact=true -%}{%- assign _ls_alt = mt_ref_url | default: "/-none-/" -%}
21
+ {%- elsif _l == _ls_def -%}{%- assign _ls_alt = _ls_base -%}{%- else -%}{%- assign _ls_alt = "/" | append: _l | append: _ls_base -%}{%- endif -%}
20
22
  {%- assign _ls_key = "|" | append: _ls_alt | append: "|" -%}
21
23
  {%- unless mt_page_urls contains _ls_key -%}{%- if _l == _ls_def -%}{%- assign _ls_alt = "/" -%}{%- else -%}{%- assign _ls_alt = "/" | append: _l | append: "/" -%}{%- endif -%}{%- endunless -%}
22
24
  {%- capture _ls_name -%}{%- include modootoday/i18n/name.html code=_l -%}{%- endcapture -%}{%- assign _ls_name = site.data.i18n[_l].language | default: _ls_name -%}
@@ -1,3 +1,6 @@
1
1
  {%- comment -%}URL for include.url: full URLs as they are, site paths through relative_url (absolute=true: absolute_url).
2
- A bare "#" sample link stays "#" (absolute=true: this page's absolute URL, for JSON-LD).{%- endcomment -%}
3
- {%- if include.url == "#" and include.absolute -%}{{ page.url | absolute_url }}{%- elsif include.url == "#" or include.url contains "://" or include.url contains "mailto:" or include.url contains "tel:" -%}{{ include.url }}{%- elsif include.absolute -%}{{ include.url | absolute_url }}{%- else -%}{{ include.url | relative_url }}{%- endif -%}
2
+ A bare "#" sample link stays "#" (absolute=true: this page's absolute URL, for JSON-LD).
3
+ include.ref instead of url: the page with that front matter ref: in this page's language (lang= to choose another),
4
+ else the default-language one (modootoday/ref-url.html); modootoday-check rejects a ref no page defines.{%- endcomment -%}
5
+ {%- if include.ref -%}{%- include modootoday/ref-url.html ref=include.ref lang=include.lang -%}{%- if include.absolute -%}{{ mt_ref_url | absolute_url }}{%- else -%}{{ mt_ref_url | relative_url }}{%- endif -%}
6
+ {%- elsif include.url == "#" and include.absolute -%}{{ page.url | absolute_url }}{%- elsif include.url == "#" or include.url contains "://" or include.url contains "mailto:" or include.url contains "tel:" -%}{{ include.url }}{%- elsif include.absolute -%}{{ include.url | absolute_url }}{%- else -%}{{ include.url | relative_url }}{%- endif -%}
@@ -0,0 +1,26 @@
1
+ {%- comment -%}
2
+ md-components v1 — optional list styles (site.modootoday.lists; the lists sheet styles them). Prints include.html with:
3
+ link_chips: true a list whose every item is one link and nothing else → ul.mt-chips (a row of chips)
4
+ titled_items: true an item that starts with **Title**: → li > strong.mt-item-title + span.mt-item-sep, then the text
5
+ Applied to the hero, to sections without c: and to pages without sections; component sections keep their own lists.
6
+ {%- endcomment -%}
7
+ {%- assign _lh = include.html -%}
8
+ {%- if site.modootoday.lists.titled_items == true and _lh contains "<li><strong>" -%}
9
+ {%- assign _lp = _lh | split: "<li><strong>" -%}
10
+ {%- capture _lh -%}{{ _lp[0] }}{%- for _x in _lp offset:1 -%}
11
+ {%- assign _t = _x | split: "</strong>: " -%}{%- assign _tt = _t[0] -%}
12
+ {%- if _t.size > 1 -%}{%- unless _tt contains "</li>" or _tt contains "<li" -%}{%- assign _ok = true -%}{%- else -%}{%- assign _ok = false -%}{%- endunless -%}{%- else -%}{%- assign _ok = false -%}{%- endif -%}
13
+ {%- if _ok -%}<li><strong class="mt-item-title">{{ _tt }}</strong><span class="mt-item-sep">: </span>{{ _x | remove_first: _tt | remove_first: "</strong>: " }}{%- else -%}<li><strong>{{ _x }}{%- endif -%}
14
+ {%- endfor -%}{%- endcapture -%}
15
+ {%- endif -%}
16
+ {%- if site.modootoday.lists.link_chips == true and _lh contains "<ul>" -%}
17
+ {%- assign _nl = "
18
+ " -%}{%- assign _us = "<ul>" | append: _nl -%}
19
+ {%- assign _lp = _lh | split: _us -%}
20
+ {%- capture _lh -%}{{ _lp[0] }}{%- for _x in _lp offset:1 -%}
21
+ {%- assign _in = _x | split: "</ul>" | first -%}
22
+ {%- assign _n1 = _in | split: "<li>" | size -%}{%- assign _n2 = _in | split: "<li><a " | size -%}{%- assign _n3 = _in | split: "</a></li>" | size -%}{%- assign _n4 = _in | split: "<a " | size -%}
23
+ {%- if _n1 > 1 and _n1 == _n2 and _n1 == _n3 and _n1 == _n4 -%}<ul class="mt-chips">{{ _nl }}{%- else -%}{{ _us }}{%- endif -%}{{ _x }}
24
+ {%- endfor -%}{%- endcapture -%}
25
+ {%- endif -%}
26
+ {{- _lh -}}
@@ -7,7 +7,15 @@ Only top-level h2 split: kramdown prints them at column 0 ("\n<h2 "), nested one
7
7
  alert) are indented and stay inside their section. modootoday-check rejects nested/setext/indented ##.
8
8
  Item: "name" or {c: component, name: key, ...params}. c: tabs → data-tabs, c: cta → (on the section, or on its [data-mt-result] element when it holds one, so a showcase box only frames the result).
9
9
  visual: <name> on an item (or include.hero.visual for the hero) appends the site's
10
- _includes/sections/<name>.html at the end of that section, called with section=<item> (hero=<hero>).
10
+ _includes/sections/<name>.html at the end of that section, called with section=<item> (hero=<hero>); every other key
11
+ on the item (e.g. scene: 2) reaches the include as include.section.<key>.
12
+ c: split wraps the section in div.mt-section-in > div.mt-section-body (h2 and text) + div.mt-section-visual (the visual
13
+ include): two columns from 48rem (side: start|end puts the visual first or last, ratio: "1:1"|"5:7" text:visual), one below.
14
+ Only split gets the wrappers, so child selectors of the other components keep working.
15
+ decor: <name> (item or hero) adds _includes/sections/<name>.html in div.mt-section-decor[aria-hidden=true], placed in
16
+ the section's top end corner (section gets .mt-has-decor); for ornaments only, never content.
17
+ The hero's first p after the h1 gets .mt-hero-lead; hero checks: true adds .mt-hero-checks (its first ul is a ✓ row, lists sheet).
18
+ site.modootoday.lists (link_chips, titled_items) runs markdown/lists.html on the hero, plain sections and section-less pages.
11
19
  modootoday-check fails when the item count differs from the h2 count.
12
20
  {%- endcomment -%}
13
21
  {%- assign _lang = include.lang | default: site.lang | default: "en" -%}
@@ -23,21 +31,27 @@ modootoday-check fails when the item count differs from the h2 count.
23
31
  {%- if _fparts.size > 1 -%}{%- assign _notes = include.content | remove_first: _main | strip -%}{%- endif -%}
24
32
  {%- assign _parts = _main | prepend: _nl | split: _sep -%}
25
33
  {%- assign mt_c_transforms = "|filetree|linkcards|details|faq|params|quote|gallery|pricing|subscribe|" -%}
26
- {%- assign _p0 = _parts[0] -%}{%- capture _hero %}{% include modootoday/actions.html html=_p0 %}{% endcapture -%}
27
- <section class="mt-hero">{{ _hero }}{% if include.hero.visual %}{% include sections/{{ include.hero.visual }}.html section=include.hero %}{% endif %}</section>
34
+ {%- assign _p0 = _parts[0] -%}{%- if site.modootoday.lists -%}{%- capture _p0 %}{% include modootoday/markdown/lists.html html=_p0 %}{% endcapture -%}{%- endif -%}
35
+ {%- capture _hero %}{% include modootoday/actions.html html=_p0 %}{% endcapture -%}{%- assign _hl = "</h1>" | append: _nl | append: _nl -%}{%- assign _hlp = _hl | append: "<p>" -%}{%- assign _hlc = _hl | append: '<p class="mt-hero-lead">' -%}{%- assign _hero = _hero | replace_first: _hlp, _hlc -%}
36
+ <section class="mt-hero{% if include.hero.checks == true %} mt-hero-checks{% endif %}{% if include.hero.decor %} mt-has-decor{% endif %}{% if include.hero.reveal == true %} mt-reveal{% endif %}">{{ _hero }}{% if include.hero.visual %}{% include sections/{{ include.hero.visual }}.html section=include.hero %}{% endif %}{% if include.hero.decor %}<div class="mt-section-decor" aria-hidden="true">{% include sections/{{ include.hero.decor }}.html section=include.hero %}</div>{% endif %}</section>
28
37
  {%- for _p in _parts offset:1 -%}
29
38
  {%- assign _s = include.sections[forloop.index0] -%}
30
39
  {%- if _s.name or _s.c -%}{%- assign _n = _s.name -%}{%- assign _cc = _s.c -%}{%- else -%}{%- assign _n = _s -%}{%- assign _cc = nil -%}{%- endif -%}
31
40
  {%- assign _body = _p -%}
41
+ {%- if site.modootoday.lists and _cc == nil -%}{%- capture _body %}{% include modootoday/markdown/lists.html html=_p %}{% endcapture -%}{%- endif -%}
32
42
  {%- if _cc == 'cta' -%}{%- capture _body %}{% include modootoday/actions.html html=_p %}{% endcapture -%}{%- endif -%}
33
43
  {%- if _cc == 'features' -%}{%- assign _body = _body | replace: "</strong> — ", "</strong> " | replace: "</strong> – ", "</strong> " | replace: "</strong> - ", "</strong> " -%}{%- endif -%}
34
44
  {%- if _cc and mt_c_transforms contains _cc -%}{%- capture _body %}{% include modootoday/markdown/c/{{ _cc }}.html html=_body s=_s i=forloop.index id=_n %}{% endcapture -%}{%- endif -%}
35
- <section class="mt-section{% if _cc %} mt-c-{{ _cc }}{% endif %}{% if _cc == 'cta' %}{% unless _body contains 'data-mt-result' %} mt-cta{% endunless %}{% endif %}{% if _s.cols %} mt-cols-{{ _s.cols }}{% endif %}" data-section="{{ _n | default: forloop.index0 }}"{% if _cc %} data-c="{{ _cc }}"{% endif %}{% if _cc == 'tabs' %} data-tabs data-tabs-label="{{ _tl }}"{% endif %}>
36
- <h2 {{ _body }}{% if _s.visual %}{% include sections/{{ _s.visual }}.html section=_s %}{% endif %}</section>
45
+ <section class="mt-section{% if _cc %} mt-c-{{ _cc }}{% endif %}{% if _cc == 'cta' %}{% unless _body contains 'data-mt-result' %} mt-cta{% endunless %}{% endif %}{% if _s.cols %} mt-cols-{{ _s.cols }}{% endif %}{% if _s.decor %} mt-has-decor{% endif %}{% if _s.reveal == true %} mt-reveal{% endif %}{% if _cc == 'features' and _s.badge == 'bottom' %} mt-badge-bottom{% endif %}{% if _cc == 'split' %}{% if _s.side == 'start' %} mt-split-start{% endif %}{% if _s.ratio == '5:7' %} mt-split-5-7{% endif %}{% endif %}" data-section="{{ _n | default: forloop.index0 }}"{% if _cc %} data-c="{{ _cc }}"{% endif %}{% if _cc == 'tabs' %} data-tabs data-tabs-label="{{ _tl }}"{% endif %}>
46
+ {%- if _cc == 'split' %}
47
+ <div class="mt-section-in"><div class="mt-section-body"><h2 {{ _body }}</div>{% if _s.visual %}<div class="mt-section-visual">{% include sections/{{ _s.visual }}.html section=_s %}</div>{% endif %}</div>{% if _s.decor %}<div class="mt-section-decor" aria-hidden="true">{% include sections/{{ _s.decor }}.html section=_s %}</div>{% endif %}</section>
48
+ {%- else %}
49
+ <h2 {{ _body }}{% if _s.visual %}{% include sections/{{ _s.visual }}.html section=_s %}{% endif %}{% if _s.decor %}<div class="mt-section-decor" aria-hidden="true">{% include sections/{{ _s.decor }}.html section=_s %}</div>{% endif %}</section>
50
+ {%- endif -%}
37
51
  {%- endfor -%}
38
52
  {%- if _notes != "" %}
39
53
  <div class="mt-endnotes">{{ _notes }}</div>
40
54
  {%- endif -%}
41
55
  {%- else -%}
42
- <div class="mt-prose">{{ include.content }}</div>
56
+ {%- if site.modootoday.lists -%}<div class="mt-prose">{% include modootoday/markdown/lists.html html=include.content %}</div>{%- else -%}<div class="mt-prose">{{ include.content }}</div>{%- endif -%}
43
57
  {%- endif -%}
@@ -0,0 +1,12 @@
1
+ {%- comment -%}Sets mt_ref_url to the URL (site path, before relative_url) of the page whose front matter ref: is include.ref,
2
+ in include.lang (default: this page's lang, then site.lang). Without one in that language it falls back to the
3
+ default-language page, unless include.exact is true; mt_ref_url is nil when nothing matches.
4
+ A page's language is its lang: (front matter or defaults), else site.lang.{%- endcomment -%}
5
+ {%- assign mt_ref_url = nil -%}
6
+ {%- assign _rf_def = site.lang | default: "en" -%}
7
+ {%- assign _rf_lang = include.lang | default: page.lang | default: _rf_def -%}
8
+ {%- assign _rf_pages = site.pages | where: "ref", include.ref -%}
9
+ {%- for _rf_p in _rf_pages -%}{%- assign _rf_l = _rf_p.lang | default: _rf_def -%}{%- if _rf_l == _rf_lang -%}{%- assign mt_ref_url = _rf_p.url -%}{%- break -%}{%- endif -%}{%- endfor -%}
10
+ {%- unless mt_ref_url or include.exact -%}
11
+ {%- for _rf_p in _rf_pages -%}{%- assign _rf_l = _rf_p.lang | default: _rf_def -%}{%- if _rf_l == _rf_def -%}{%- assign mt_ref_url = _rf_p.url -%}{%- break -%}{%- endif -%}{%- endfor -%}
12
+ {%- endunless -%}
@@ -10,6 +10,7 @@ Stylesheet links (needs modootoday/config.html first). Sets mt_page_c ("|tabs|fa
10
10
  {%- if mt_components contains "search" and mt_search_index -%}{%- assign mt_page_c = mt_page_c | append: "search|" -%}{%- endif -%}
11
11
  {%- if site.modootoday.loading or content contains 'class="mt-spinner' or content contains 'class="mt-skeleton' or content contains 'class="mt-topbar' -%}{%- assign mt_page_c = mt_page_c | append: "loading|" -%}{%- endif -%}
12
12
  {%- if content contains 'class="mt-figure mt-figure-svg' -%}{%- assign mt_page_c = mt_page_c | append: "figure|" -%}{%- endif -%}
13
+ {%- if content contains 'class="mt-hero mt-hero-checks' or content contains 'class="mt-chips"' or content contains 'class="mt-item-title"' -%}{%- assign mt_page_c = mt_page_c | append: "lists|" -%}{%- endif -%}
13
14
  {%- for _x in layout.mt_css -%}{%- assign mt_page_c = mt_page_c | append: _x | append: "|" -%}{%- endfor -%}
14
15
  {%- for _x in page.css -%}{%- assign mt_page_c = mt_page_c | append: _x | append: "|" -%}{%- endfor %}
15
16
  {%- comment -%}Blog, news and archive layouts add the separate blog sheet (both modes); front matter blog_css: true opts any page in.{%- endcomment -%}
@@ -3,6 +3,8 @@
3
3
  {%- assign _lang = page.lang | default: site.lang | default: "en" -%}
4
4
  {%- include modootoday/config.html -%}
5
5
  {%- include modootoday/i18n.html lang=_lang -%}
6
+ {%- comment -%}mt_site_title: the brand name shown in the header, <title>, og:site_name and JSON-LD; site.data.i18n[lang].site_title localises it, else site.title.{%- endcomment -%}
7
+ {%- assign mt_site_title = site.data.i18n[_lang].site_title | default: site.title -%}
6
8
  {%- comment -%}mt_home: this language's home (/<lang>/ when that page exists), else the site root; used by the brand link and footers.{%- endcomment -%}
7
9
  {%- assign mt_home = "/" -%}{%- assign _def_lang = site.lang | default: "en" -%}
8
10
  {%- if _lang != _def_lang -%}{%- assign _lh = "/" | append: _lang | append: "/" -%}{%- assign _lhp = site.pages | where: "url", _lh | first -%}{%- if _lhp -%}{%- assign mt_home = _lh -%}{%- endif -%}{%- endif -%}
@@ -14,7 +16,7 @@
14
16
  <head>
15
17
  <meta charset="utf-8">
16
18
  <meta name="viewport" content="width=device-width, initial-scale=1{% unless site.modootoday.viewport_fit == "auto" %}, viewport-fit=cover{% endunless %}">
17
- <title>{% if page.title %}{{ page.title }}{% if site.title %} · {{ site.title }}{% endif %}{% else %}{{ site.title }}{% endif %}</title>
19
+ <title>{% if page.title %}{{ page.title }}{% if site.title %} · {{ mt_site_title }}{% endif %}{% else %}{{ mt_site_title }}{% endif %}</title>
18
20
  {%- if page.description or site.description %}
19
21
  <meta name="description" content="{{ page.description | default: site.description | strip_html | normalize_whitespace | escape }}">
20
22
  {%- endif %}
@@ -45,7 +47,7 @@
45
47
  {%- endif %}
46
48
  {%- assign _chrome = true -%}{%- if layout.mt_chrome == false or page.chrome == false -%}{%- assign _chrome = false -%}{%- endif -%}
47
49
  {%- if site.title and _chrome %}
48
- <header class="mt-header"><div class="mt-header-inner"><a class="mt-site-title{% if site.modootoday.logo %} mt-has-logo{% endif %}" href="{{ mt_home | relative_url }}">{% include modootoday/brand/logo.html %}{{ site.title }}</a>{% include modootoday/nav.html %}{% if mt_components contains "search" and mt_search_index %}<div class="mt-search" role="search" data-index="{{ '/search.json' | relative_url }}" data-lang="{{ site.lang | default: 'en' }}" hidden>{% include modootoday/brand/icon.html name="search" size=16 %}<input type="search" aria-keyshortcuts="/ Control+K Meta+K" aria-controls="mt-search-results" aria-label="{{ mt_t_search_label }}" placeholder="{{ mt_t_search_label }}"><ul class="mt-search-results" id="mt-search-results" aria-live="polite"></ul></div>{% endif %}{% if site.modootoday.language_switcher == "header" %}{% include modootoday/lang-switch.html style="menu" %}{% endif %}{% if mt_components contains "theme-toggle" and mt_theme_toggle %}<button class="mt-theme-toggle" type="button" aria-pressed="false" aria-label="{{ mt_t_theme_toggle }}" title="{{ mt_t_theme_toggle }}" hidden>{% include modootoday/brand/icon.html name="moon" %}{% include modootoday/brand/icon.html name="sun" %}</button>{% endif %}</div></header>
50
+ <header class="mt-header"><div class="mt-header-inner"><a class="mt-site-title{% if site.modootoday.logo %} mt-has-logo{% endif %}" href="{{ mt_home | relative_url }}">{% include modootoday/brand/logo.html %}{{ mt_site_title }}</a>{% include modootoday/nav.html %}{% if mt_components contains "search" and mt_search_index %}<div class="mt-search" role="search" data-index="{{ '/search.json' | relative_url }}" data-lang="{{ site.lang | default: 'en' }}" hidden>{% include modootoday/brand/icon.html name="search" size=16 %}<input type="search" aria-keyshortcuts="/ Control+K Meta+K" aria-controls="mt-search-results" aria-label="{{ mt_t_search_label }}" placeholder="{{ mt_t_search_label }}"><ul class="mt-search-results" id="mt-search-results" aria-live="polite"></ul></div>{% endif %}{% if site.modootoday.language_switcher == "header" %}{% include modootoday/lang-switch.html style="menu" %}{% endif %}{% if mt_components contains "theme-toggle" and mt_theme_toggle %}<button class="mt-theme-toggle" type="button" aria-pressed="false" aria-label="{{ mt_t_theme_toggle }}" title="{{ mt_t_theme_toggle }}" hidden>{% include modootoday/brand/icon.html name="moon" %}{% include modootoday/brand/icon.html name="sun" %}</button>{% endif %}</div></header>
49
51
  {%- endif %}
50
52
  <main id="mt-main" tabindex="-1" class="mt-main{% if page.full_width %} mt-main-full{% endif %}{% if layout.mt_main %} {{ layout.mt_main }}{% endif %}">
51
53
  {% include modootoday/links/content.html html=content %}
@@ -8,6 +8,7 @@
8
8
  h4 { font-size: 1rem; }
9
9
  p, ul, ol, dl { margin: 0 0 1em; }
10
10
  :where(:not([class])) > li + li { margin-top: .3em; } // flow lists only: classed grid/flex lists keep item 1 aligned
11
+ .mt-flat-list li + li { margin-top: 0; } // opt-out on an ancestor, for unclassed Markdown lists made grid/flex (.c-body > ul)
11
12
  li > ul, li > ol { margin: .3em 0 0; }
12
13
  blockquote { margin: 1.25rem 0; padding: .25rem 1rem; color: mt(muted-foreground); border-inline-start: 3px solid mt(border); }
13
14
  blockquote > :last-child { margin-bottom: 0; }
@@ -6,4 +6,7 @@
6
6
  :root[data-theme="dark"] .mt-figure img { filter: brightness(.85); }
7
7
  @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .mt-figure img { filter: brightness(.85); } }
8
8
  .mt-figure figcaption { margin-top: .6rem; color: mt(muted-foreground); font-size: .875rem; text-align: center; }
9
+ // .mt-illustration: figure[role=img][aria-label] for drawn scenes; .mt-illustration-tag marks it (e.g. "Illustration").
10
+ .mt-illustration { position: relative; margin: 0 0 1.5rem; }
11
+ .mt-illustration-tag { position: absolute; inset-block-start: .5rem; inset-inline-start: .5rem; padding: .125rem .5rem; border: 1px solid mt(border); border-radius: 999px; background: mt(background); color: mt(foreground); font-size: .75rem; }
9
12
  }
@@ -5,4 +5,7 @@
5
5
  .mt-hero > p:first-of-type { font-size: 1.2rem; line-height: 1.6; color: mt(muted-foreground); max-width: 40rem; }
6
6
  .mt-section { padding: mt(space-section) 0 0; }
7
7
  .mt-section > h2:first-child { margin-top: 0; }
8
+ // decor: an aria-hidden ornament in the top end corner; it never takes clicks.
9
+ .mt-has-decor { position: relative; }
10
+ .mt-section-decor { position: absolute; inset-block-start: 0; inset-inline-end: 0; pointer-events: none; }
8
11
  }
@@ -0,0 +1,10 @@
1
+ // Scroll reveal: .mt-reveal (reveal: true on a section or hero:). CSS only, no script.
2
+ // It runs only where scroll-driven animations exist and the reader allows motion; otherwise content is shown as is.
3
+ @include mt-layer(components) {
4
+ @supports (animation-timeline: view()) {
5
+ @media screen and (prefers-reduced-motion: no-preference) {
6
+ @keyframes mt-reveal { from { opacity: 0; translate: 0 1.5rem; } to { opacity: 1; translate: none; } }
7
+ .mt-reveal { animation: mt-reveal linear both; animation-timeline: view(); animation-range: entry 0% entry 60%; }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,16 @@
1
+ // Optional list styles (lists sheet): linked only by pages with .mt-hero-checks, ul.mt-chips or .mt-item-title.
2
+ // .mt-hero-checks: the hero's first ul is a row of ✓ chips in --mt-ok (default: the status-available colour; a site may set its own). ul.mt-chips: link-only items as 44px chips with →.
3
+ // .mt-item-title: **Title**: items get a bold title and a muted separator.
4
+ @include mt-layer(components) {
5
+ :where(:root) { --mt-ok: #{mt(status-available)}; }
6
+ .mt-hero-checks > ul:first-of-type, ul.mt-chips { display: flex; flex-wrap: wrap; gap: .5rem; padding: 0; list-style: none; }
7
+ .mt-hero-checks > ul:first-of-type > li { display: inline-flex; align-items: center; gap: .375rem; margin: 0; padding: .25rem .75rem; border: 1px solid mt(border); border-radius: 999px; }
8
+ .mt-hero-checks > ul:first-of-type > li::before { content: "✓" / ""; color: var(--mt-ok); font-weight: 700; }
9
+ ul.mt-chips > li { margin: 0; }
10
+ ul.mt-chips a { display: inline-flex; align-items: center; gap: .375rem; min-height: 44px; padding: 0 1rem; border: 1px solid mt(border); border-radius: 999px; color: mt(foreground); text-decoration: none; }
11
+ ul.mt-chips a::after { content: "→" / ""; color: mt(muted-foreground); }
12
+ :dir(rtl) ul.mt-chips a::after, [dir="rtl"] ul.mt-chips a::after { content: "←" / ""; }
13
+ ul.mt-chips a:hover { border-color: mt(primary); }
14
+ .mt-item-title { font-weight: 700; }
15
+ .mt-item-sep { color: mt(muted-foreground); }
16
+ }
@@ -8,6 +8,9 @@
8
8
  .mt-c-features > ul > li code { white-space: nowrap; }
9
9
  .mt-c-features > ul > li:hover { border-color: color-mix(in srgb, #{mt(primary)} 40%, #{mt(border)}); box-shadow: mt(shadow); }
10
10
  .mt-c-features > ul > li > strong:first-child { display: block; margin-bottom: .35rem; color: mt(card-foreground); font-size: 1rem; }
11
+ // badge: bottom: a status badge (.mt-badge) written as a direct child of the item sits at the card's bottom edge.
12
+ .mt-badge-bottom > ul > li:has(> .mt-badge) { position: relative; padding-block-end: calc(var(--mt-card-padding) + 2rem); }
13
+ .mt-badge-bottom > ul > li > .mt-badge { position: absolute; inset-block-end: var(--mt-card-padding); inset-inline-start: var(--mt-card-padding); }
11
14
  @media (min-width: 48rem) {
12
15
  .mt-cols-2 > ul { grid-template-columns: repeat(2, minmax(0, 1fr)); }
13
16
  .mt-cols-3 > ul { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@@ -0,0 +1,14 @@
1
+ // Split: c: split; text (.mt-section-body) and the visual: include (.mt-section-visual) side by side from 48rem,
2
+ // stacked below. Grid columns follow the writing direction, so start/end flip in RTL. DOM order stays text first.
3
+ @include mt-layer(components) {
4
+ .mt-section-in { display: grid; gap: 2rem; }
5
+ .mt-section-in > * { min-width: 0; }
6
+ .mt-section-body > :last-child, .mt-section-visual > :last-child { margin-bottom: 0; }
7
+ .mt-section-visual img, .mt-section-visual svg { display: block; max-width: 100%; height: auto; }
8
+ @media (min-width: 48rem) {
9
+ .mt-c-split > .mt-section-in { grid-template-columns: var(--mt-split-body, 1fr) var(--mt-split-visual, 1fr); align-items: center; }
10
+ .mt-split-5-7 { --mt-split-body: 5fr; --mt-split-visual: 7fr; }
11
+ .mt-split-start > .mt-section-in { grid-template-columns: var(--mt-split-visual, 1fr) var(--mt-split-body, 1fr); }
12
+ .mt-split-start .mt-section-visual { grid-row: 1; grid-column: 1; }
13
+ }
14
+ }
@@ -7,6 +7,7 @@
7
7
  @import "modootoday/components/core/inline/figure";
8
8
  @import "modootoday/components/core/inline/code-copy";
9
9
  @import "modootoday/components/core/inline/hero";
10
+ @import "modootoday/components/core/inline/reveal";
10
11
  @import "modootoday/components/core/inline/cta";
11
12
  @import "modootoday/components/section/showcase/cta";
12
13
  @import "modootoday/components/core/inline/code";
@@ -37,4 +38,5 @@
37
38
  @import "modootoday/components/section/structure/details";
38
39
  @import "modootoday/components/section/structure/faq";
39
40
  @import "modootoday/components/section/structure/params";
41
+ @import "modootoday/components/section/structure/split";
40
42
  @import "modootoday/access";
@@ -0,0 +1,8 @@
1
+ ---
2
+ # md-components v1 — jekyll-theme-modootoday optional sheet (hero check row, link chips, titled items). Linked only by pages that use it.
3
+ ---
4
+ {%- include modootoday/config.html -%}
5
+ $mt-css-layers: {{ mt_css_layers }};
6
+ @import "modootoday/sheet";
7
+ {% if mt_components contains "lists" %}@import "modootoday/components/lists/lists";
8
+ {% endif %}
@@ -0,0 +1,7 @@
1
+ ---
2
+ # md-components v1 — jekyll-theme-modootoday section sheet (c: split). Linked only by pages that use it.
3
+ ---
4
+ {%- include modootoday/config.html -%}
5
+ $mt-css-layers: {{ mt_css_layers }};
6
+ @import "modootoday/sheet";
7
+ @import "modootoday/components/section/structure/split";
data/i18n/glossary.yml CHANGED
@@ -6,7 +6,10 @@ register:
6
6
  ko: {docs: 합니다체, ui: 해요체, labels: noun phrase}
7
7
  ja: {docs: です・ます, ui: です・ます, labels: 体言止め, table_cells: 体言止め or 常体}
8
8
  # Pages whose front matter has `noindex: true` are sample content (demo posts) and keep their genre voice.
9
+ # Brand: the theme's visible name always carries the word for theme (the plain name is a separate service).
10
+ # Gem, repository and code names (jekyll-theme-modootoday, modootoday-check) are not translated.
9
11
  terms:
12
+ - {en: modoo.today theme (brand name), ko: 모두의오늘 테마, ja: modoo.today テーマ}
10
13
  - {en: Markdown twin, ko: Markdown 버전, ja: Markdown 版, avoid: {ko: [쌍둥이, 트윈], ja: [ツイン, 双子]}}
11
14
  - {en: front matter, ko: front matter, ja: front matter, avoid: {ko: [머리말, 프런트 매터, 프론트 매터], ja: [フロントマター]}}
12
15
  - {en: breadcrumb, ko: 이동 경로, ja: パンくずリスト, avoid: {ko: [브레드크럼, 빵 부스러기], ja: [ブレッドクラム]}}
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Modootoday
4
+ module Check
5
+ # .mt-illustration (WCAG 1.1.1): a drawn scene is one image to assistive technology, so it must be
6
+ # <figure role="img"> with a non-empty aria-label; its children (and any .mt-illustration-tag) are not read.
7
+ module Illustration
8
+ OPEN = /<([a-z][a-z0-9]*)\b[^>]*\bclass="[^"]*\bmt-illustration\b(?!-)[^"]*"[^>]*>/i
9
+
10
+ module_function
11
+
12
+ def call(ctx)
13
+ Dir.glob("#{ctx.site}/**/*.html").each do |f|
14
+ File.read(f).gsub(%r{<pre\b.*?</pre>}m, "").scan(OPEN) do
15
+ tag, el = Regexp.last_match(1), Regexp.last_match(0)
16
+ label = el[/\baria-label="([^"]*)"/, 1].to_s.strip
17
+ next if tag.casecmp?("figure") && el =~ /\brole="img"/ && !label.empty?
18
+ ctx.errs << "#{f}: .mt-illustration must be <figure role=\"img\" aria-label=\"…\"> with a non-empty label (got #{el[0, 120]})"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -4,7 +4,7 @@ module Modootoday
4
4
  module Check
5
5
  # Command line: usage, --help/--version, running the rule groups in order, reporting.
6
6
  module CLI
7
- USAGE = "usage: modootoday-check SRC SITE [--twins] [--github-pages[=VERSION]] [--gh] [--strict] [--schema-recommended] [--i18n] [--i18n-init=LANG,...]"
7
+ USAGE = "usage: modootoday-check SRC SITE [--twins] [--github-pages[=VERSION]] [--gh] [--strict] [--schema-recommended] [--i18n] [--i18n-pages] [--i18n-init=LANG,...]"
8
8
  # Single source for --help and the README "Commands" table (script/readme-commands, test/project/readme_test.rb).
9
9
  OPTIONS = [
10
10
  ["SRC SITE", "always: front matter, forbidden syntax, alerts, fence info strings, `status:`/`tag:`/`kbd:` values (`since:` is rejected), `sections:` count, `_data/i18n/<lang>.yml` keys against the default language and translated pages against default-language pages, `_data/components.yml` (keys, slot contract counts per section, `parts` against `api.md` and the built HTML), Sass/CSS rules, unconverted markers in the build, schema.org JSON-LD in the build (valid JSON, vocabulary, required properties per type, absolute URLs, ISO 8601 dates, one graph per page, agreement with Open Graph)"],
@@ -13,6 +13,7 @@ module Modootoday
13
13
  ["--schema-recommended", "also warn about schema.org properties Google Search recommends (author, image, dates, ...) that a JSON-LD node lacks"],
14
14
  ["--strict", "warnings fail the run"],
15
15
  ["--i18n", "`_data/i18n/<lang>.yml` against the theme registry (`i18n/en.yml` in the gem): unknown keys, `%{name}` placeholder mismatches, and pages marked `translation: pending`"],
16
+ ["--i18n-pages", "warn about default-language pages with no translation in a `modootoday.full_languages` language (the page with the same `ref:`, else the same path under `<lang>/`); with `--strict` they fail"],
16
17
  ["--i18n-init=LANG,...", "copy the theme's catalogs (`i18n/<lang>.yml`, 23 languages) into `SRC/_data/i18n/` as a starting point for overrides (optional; the theme already uses them), and list keys an existing catalog lacks"],
17
18
  ["--gh", "also render each page with `gh api markdown` (network, `gh` login)"],
18
19
  ["--help, -h", "print usage and options (`--help=markdown` prints the README table)"],
@@ -20,7 +21,7 @@ module Modootoday
20
21
  ].freeze
21
22
  # Rule groups in report order.
22
23
  RULES = [Registry, SiteConfig.method(:source_mode), Pages, Layouts, I18n, Registry.method(:i18n_keys), BuiltParts,
23
- BuildOutput.method(:residue), NewWindow.method(:built), StructuredData, Stylesheets, BuildOutput.method(:css), SiteConfig, Renderers, Blog, GithubPages, Twins, GhRender].freeze
24
+ BuildOutput.method(:residue), NewWindow.method(:built), Illustration, StructuredData, Stylesheets, BuildOutput.method(:css), SiteConfig, Renderers, Blog, GithubPages, Twins, GhRender].freeze
24
25
 
25
26
  module_function
26
27
 
@@ -3,7 +3,7 @@
3
3
  module Modootoday
4
4
  module Check
5
5
  # Values the transforms need (Rule 2b): inline `file:`, `progress:`, `embed:` code spans, and per-section
6
- # content rules for c: gallery, quote, pricing and subscribe that slot counts cannot express.
6
+ # content rules for c: gallery, quote, pricing, split and subscribe that slot counts cannot express.
7
7
  module ComponentRules
8
8
  SKELETONS = %w[text card image avatar row].freeze
9
9
 
@@ -43,6 +43,8 @@ module Modootoday
43
43
  n = m.scan(/^###(?: |$)/).size
44
44
  rec = s["recommended"]
45
45
  errs << "#{w}: recommended: must be a plan number from 1 to #{n}" if s.key?("recommended") && !(rec.is_a?(Integer) && rec.between?(1, n))
46
+ when "split"
47
+ errs << "#{w}: needs visual: <name> (the _includes/sections/<name>.html shown beside the text)" unless s["visual"].is_a?(String) && !s["visual"].strip.empty?
46
48
  when "subscribe"
47
49
  act = ctx.mtc.is_a?(Hash) && ctx.mtc.dig("subscribe", "action")
48
50
  ctx.warns << "#{w}: no form is shown until _config.yml sets modootoday.subscribe.action" unless act
@@ -32,6 +32,49 @@ module Modootoday
32
32
  init(ctx) if ctx.argv.any? { |a| a.start_with?("--i18n-init") }
33
33
  follow_default(ctx)
34
34
  registry(ctx) if ctx.option?("--i18n") && File.exist?("#{THEME}/en.yml")
35
+ refs(ctx)
36
+ pages(ctx) if ctx.option?("--i18n-pages")
37
+ end
38
+
39
+ # Each page as [rel path, front matter, language]: lang: from front matter, else the /<lang>/ folder
40
+ # when that code is in modootoday.languages, else the default language (site lang).
41
+ def page_index(ctx)
42
+ langs = Array(ctx.mtc["languages"]).map(&:to_s)
43
+ default = ctx.site_cfg["lang"].to_s.empty? ? "en" : ctx.site_cfg["lang"].to_s
44
+ ctx.mds.map do |f|
45
+ r = ctx.rel(f)
46
+ fm = (Markdown.fm(File.read(f)) rescue {}) || {}
47
+ dir = r.split("/").first
48
+ [r, fm, (fm["lang"] || (langs.include?(dir) && r.include?("/") ? dir : default)).to_s]
49
+ end
50
+ end
51
+
52
+ # {% include modootoday/link.html ref="x" %} needs a page with front matter ref: x.
53
+ def refs(ctx)
54
+ files = (ctx.mds + Dir.glob("#{ctx.src}/{_includes,_layouts}/**/*.html")).reject { |f| f.include?("/_site/") }
55
+ uses = files.flat_map { |f| Markdown.strip_fences(File.read(f)).gsub(/`[^`\n]*`/, "").scan(/\{%-?\s*include\s+modootoday\/link\.html\b[^%]*\bref=["']([^"']+)["']/).flatten.map { |x| [ctx.rel(f), x] } }
56
+ return if uses.empty?
57
+ known = page_index(ctx).map { |_, fm, _| fm["ref"].to_s }.reject(&:empty?)
58
+ uses.each { |r, x| ctx.errs << "#{r}: link.html ref=\"#{x}\": no page has front matter ref: #{x}" unless known.include?(x) }
59
+ end
60
+
61
+ # --i18n-pages: every default-language page has a translation in each modootoday.full_languages language
62
+ # (the page with the same ref:, or without ref: the same path under <lang>/). Warnings; --strict fails.
63
+ def pages(ctx)
64
+ full = Array(ctx.mtc["full_languages"]).map(&:to_s)
65
+ default = ctx.site_cfg["lang"].to_s.empty? ? "en" : ctx.site_cfg["lang"].to_s
66
+ return ctx.warns << "--i18n-pages: set modootoday.full_languages to the languages that translate every page" if full.empty?
67
+ idx = page_index(ctx)
68
+ have = idx.to_h { |r, fm, l| [[l, fm["ref"] ? "ref:#{fm['ref']}" : r], true] }
69
+ idx.each do |r, fm, l|
70
+ next unless l == default && fm["layout"] != "twin"
71
+ full.each do |fl|
72
+ next if fl == default
73
+ key = fm["ref"] ? "ref:#{fm['ref']}" : "#{fl}/#{r}"
74
+ next if have[[fl, key]]
75
+ ctx.warns << "#{r}: no #{fl} page (#{fm['ref'] ? "ref: #{fm['ref']}" : "#{fl}/#{r}"}); #{fl} is in modootoday.full_languages"
76
+ end
77
+ end
35
78
  end
36
79
 
37
80
  def init(ctx)
@@ -22,18 +22,23 @@ module Modootoday
22
22
  components(ctx, r, secs, sec_md) if ctx.comps
23
23
  hero = meta["hero"]
24
24
  errs << "#{r}: hero: must be a map such as {visual: name}" if hero && !hero.is_a?(Hash)
25
+ errs << "#{r}: hero.checks must be true or false" if hero.is_a?(Hash) && hero.key?("checks") && ![true, false].include?(hero["checks"])
25
26
  visuals(ctx, r, secs, hero.is_a?(Hash) ? hero : nil)
26
27
  end
27
28
 
28
- # visual: <name> on a section item or on hero: needs _includes/sections/<name>.html in the site or the theme (showcase).
29
+ # reveal: is a boolean. visual: <name> and decor: <name> on a section item or on hero: needs _includes/sections/<name>.html in the site or the theme (showcase).
29
30
  def visuals(ctx, r, secs, hero)
30
31
  items = secs.each_with_index.map { |s, i| [s, "sections[#{i}]"] }
31
32
  items << [hero, "hero"] if hero
32
33
  items.each do |s, where|
33
- next unless s.is_a?(Hash) && s.key?("visual")
34
- v = s["visual"].to_s
35
- next ctx.errs << "#{r}: #{where}.visual must be an include name [a-z0-9_-] (got #{s['visual'].inspect})" unless v =~ /\A[a-z0-9][a-z0-9_-]*\z/
36
- ctx.errs << "#{r}: #{where}.visual: _includes/sections/#{v}.html not found" unless [ctx.src, THEME].any? { |d| File.exist?(File.join(d, "_includes/sections/#{v}.html")) }
34
+ next unless s.is_a?(Hash)
35
+ ctx.errs << "#{r}: #{where}.reveal must be true or false" if s.key?("reveal") && ![true, false].include?(s["reveal"])
36
+ %w[visual decor].each do |key|
37
+ next unless s.key?(key)
38
+ v = s[key].to_s
39
+ next ctx.errs << "#{r}: #{where}.#{key} must be an include name [a-z0-9_-] (got #{s[key].inspect})" unless v =~ /\A[a-z0-9][a-z0-9_-]*\z/
40
+ ctx.errs << "#{r}: #{where}.#{key}: _includes/sections/#{v}.html not found" unless [ctx.src, THEME].any? { |d| File.exist?(File.join(d, "_includes/sections/#{v}.html")) }
41
+ end
37
42
  end
38
43
  end
39
44
 
@@ -57,7 +62,9 @@ module Modootoday
57
62
  params = spec["params"] || {}
58
63
  Slots.check_section(errs, r, i, s["c"], spec["slots"], sec_md[i])
59
64
  (ctx.part_uses[s["c"]] ||= []) << r if spec.is_a?(Hash) && spec["parts"].is_a?(Array)
60
- (s.keys - %w[c name visual]).each do |k|
65
+ (s.keys - %w[c name visual decor reveal]).each do |k|
66
+ # with visual:, other keys are the visual include's own params (include.section.<key>)
67
+ next if !params.key?(k) && s.key?("visual")
61
68
  next errs << "#{r}: sections[#{i}] #{s['c']}: unknown param '#{k}'" unless params.key?(k)
62
69
  allowed = params[k].is_a?(Hash) && params[k]["allowed"]
63
70
  errs << "#{r}: sections[#{i}] #{s['c']}.#{k}=#{s[k].inspect} not in #{allowed.inspect}" if allowed && !allowed.include?(s[k])
@@ -52,13 +52,16 @@ module Modootoday
52
52
 
53
53
  # Tool-generated region markers on their own line, e.g. <!-- spec:toc:begin --> / <!-- spec:toc:end -->
54
54
  MARKER = /^[ \t]*<!--[ \t]*[a-z][a-z0-9-]*(?::[a-z0-9._-]+)*:(?:begin|end)[ \t]*-->[ \t]*$/
55
- # specfold generated values, own-line or inline, one line each: <!-- spec:count -->12<!-- /spec:count -->
56
- SPEC = %r{<!--[ \t]*/?spec:[a-z0-9._-]+(?::[a-z0-9._-]+)*[ \t]*-->}
55
+ # specfold generated values, own-line or inline, one line each: <!-- spec:count -->12<!-- /spec:count -->.
56
+ # Arguments may follow the name after a space (<!-- spec:count rows="All commands" -->) up to the closing -->;
57
+ # they stay on one line and contain no `>`, `--` or `##` (also inside quotes).
58
+ SPEC = %r{<!--[ \t]*/?spec:[a-z0-9._-]+(?::[a-z0-9._-]+)*(?:[ \t]+(?:(?!--|\#\#)[^>\n])*)?-->}
57
59
 
58
60
  # 1c. forbidden syntax outside code
59
61
  def forbidden(ctx, f, code, html_free)
60
62
  r = ctx.rel(f)
61
- ctx.errs << "#{r}: HTML comment <!-- --> (hidden on GitHub, emitted by kramdown); remove it" if code.gsub(MARKER, "").gsub(SPEC, "").include?("<!--")
63
+ left = code.gsub(MARKER, "").gsub(SPEC, "")[/<!--[^\n]{0,60}/]
64
+ ctx.errs << "#{r}: HTML comment <!-- --> (hidden on GitHub, emitted by kramdown); remove it (first: `#{left.strip}`)" if left
62
65
  html_free.each_line.with_index(1) do |l, n|
63
66
  linkless = l.gsub(/\]\([^)]*\)/, "]()").gsub(/<(https?:|mailto:)[^>]*>|<[^@\s>]+@[^>\s]+>/, "<>").sub(/^ {0,3}\[[^\]]+\]:\s*\S+/, "")
64
67
  why = case l
@@ -6,6 +6,7 @@ require_relative "component_rules"
6
6
  require_relative "page_sections"
7
7
  require_relative "a11y/content"
8
8
  require_relative "a11y/new_window"
9
+ require_relative "a11y/illustration"
9
10
 
10
11
  module Modootoday
11
12
  module Check
@@ -4,7 +4,7 @@ module Modootoday
4
4
  module Check
5
5
  # The modootoday: settings in the site's _config.yml.
6
6
  module SiteConfig
7
- COMPONENTS = %w[callout badge hero cta card tabs code-copy nav sidebar page-actions pagination theme-toggle breadcrumb toc accordion steps figure timeline kbd search code footnotes progress embed banner filetree details faq params linkcards quote gallery pricing subscribe loading].freeze
7
+ COMPONENTS = %w[callout badge hero cta card tabs code-copy nav sidebar page-actions pagination theme-toggle breadcrumb toc accordion steps figure timeline kbd search code footnotes progress embed banner filetree details faq params linkcards quote gallery pricing subscribe split loading lists reveal].freeze
8
8
  SEMANTIC = %w[background foreground muted muted-foreground card card-foreground primary primary-foreground brand accent
9
9
  accent-foreground border ring note tip important warning caution status-available status-partial
10
10
  status-planned font-sans font-mono font-size line-height measure space radius].freeze
@@ -25,6 +25,12 @@ module Modootoday
25
25
  return errs << "_config.yml: modootoday: must be a map" if mtc && !mtc.is_a?(Hash)
26
26
  return unless mtc.is_a?(Hash)
27
27
  enums(mtc, errs)
28
+ full_languages(mtc, errs)
29
+ if mtc.key?("lists")
30
+ l = mtc["lists"]
31
+ ok = l.is_a?(Hash) && (l.keys - %w[link_chips titled_items]).empty? && l.values.all? { |v| [true, false].include?(v) }
32
+ errs << "_config.yml: modootoday.lists must be a map of link_chips and titled_items set to true or false" unless ok
33
+ end
28
34
  if mtc.key?("archive")
29
35
  a = mtc["archive"]
30
36
  if !a.is_a?(Hash) || !(a.keys - %w[tags categories]).empty?
@@ -53,6 +59,14 @@ module Modootoday
53
59
  errs << "_config.yml: modootoday.languages must be a list of language codes such as [en, ko]" if mtc.key?("languages") && !(mtc["languages"].is_a?(Array) && mtc["languages"].all? { |l| l.to_s =~ /\A[a-z]{2,3}(-[A-Za-z0-9]+)*\z/ })
54
60
  end
55
61
 
62
+ # full_languages: the languages that must translate every default-language page (modootoday-check --i18n-pages).
63
+ def full_languages(mtc, errs)
64
+ return unless mtc.key?("full_languages")
65
+ fl, langs = mtc["full_languages"], Array(mtc["languages"]).map(&:to_s)
66
+ return errs << "_config.yml: modootoday.full_languages must be a list of codes from modootoday.languages" unless fl.is_a?(Array)
67
+ (fl.map(&:to_s) - langs).each { |l| errs << "_config.yml: modootoday.full_languages: '#{l}' is not in modootoday.languages" }
68
+ end
69
+
56
70
  def tokens(ctx)
57
71
  mtc, errs = ctx.mtc, ctx.errs
58
72
  %w[tokens tokens_dark].each do |k|
@@ -12,6 +12,10 @@ module Modootoday
12
12
  module StructuredData
13
13
  BLOCK = %r{<script type="application/ld\+json">(.*?)</script>}m
14
14
  CONTEXT = %r{\Ahttps?://schema\.org/?\z}
15
+ # jekyll-seo-tag output (sites with seo: false that bring the plugin). Its JSON-LD is third-party markup the kit
16
+ # does not write: it is checked only for valid JSON and noted as a warning, not held to the kit's rules
17
+ # (for example WebPage with headline but no name). schema.org itself marks no property as required.
18
+ SEO_TAG = /<!-- Begin Jekyll SEO tag.*?<!-- End Jekyll SEO tag -->/m
15
19
 
16
20
  module_function
17
21
 
@@ -20,6 +24,15 @@ module Modootoday
20
24
  end
21
25
 
22
26
  def page(ctx, f, html)
27
+ html.scan(SEO_TAG).each do |seo|
28
+ seo.scan(BLOCK).flatten.each do |b|
29
+ JSON.parse(b)
30
+ ctx.warns << "#{f}: JSON-LD from jekyll-seo-tag not checked against the kit's schema rules (third-party markup)"
31
+ rescue JSON::ParserError => e
32
+ ctx.errs << "#{f}: jekyll-seo-tag JSON-LD is not valid JSON (#{e.message[0, 80]})"
33
+ end
34
+ end
35
+ html = html.gsub(SEO_TAG, "")
23
36
  blocks = html.scan(BLOCK).flatten
24
37
  return if blocks.empty?
25
38
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-modootoday
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - modootoday
@@ -129,6 +129,7 @@ files:
129
129
  - _includes/modootoday/markdown/c/subscribe.html
130
130
  - _includes/modootoday/markdown/components.html
131
131
  - _includes/modootoday/markdown/inline.html
132
+ - _includes/modootoday/markdown/lists.html
132
133
  - _includes/modootoday/markdown/llms-pages.html
133
134
  - _includes/modootoday/markdown/raw-body.html
134
135
  - _includes/modootoday/markdown/sections.html
@@ -148,6 +149,7 @@ files:
148
149
  - _includes/modootoday/pages/hero/scene.html
149
150
  - _includes/modootoday/pages/hero/sparks.html
150
151
  - _includes/modootoday/pages/search-form.html
152
+ - _includes/modootoday/ref-url.html
151
153
  - _includes/modootoday/render/asset.html
152
154
  - _includes/modootoday/render/math.html
153
155
  - _includes/modootoday/render/mermaid.html
@@ -236,10 +238,12 @@ files:
236
238
  - _sass/modootoday/components/core/inline/_hero.scss
237
239
  - _sass/modootoday/components/core/inline/_kbd.scss
238
240
  - _sass/modootoday/components/core/inline/_progress.scss
241
+ - _sass/modootoday/components/core/inline/_reveal.scss
239
242
  - _sass/modootoday/components/doc/_page-actions.scss
240
243
  - _sass/modootoday/components/doc/_sidebar.scss
241
244
  - _sass/modootoday/components/doc/_toc.scss
242
245
  - _sass/modootoday/components/figure/_figure-svg.scss
246
+ - _sass/modootoday/components/lists/_lists.scss
243
247
  - _sass/modootoday/components/loading/_loading.scss
244
248
  - _sass/modootoday/components/search/_search.scss
245
249
  - _sass/modootoday/components/section/showcase/_card.scss
@@ -254,6 +258,7 @@ files:
254
258
  - _sass/modootoday/components/section/structure/_faq.scss
255
259
  - _sass/modootoday/components/section/structure/_filetree.scss
256
260
  - _sass/modootoday/components/section/structure/_params.scss
261
+ - _sass/modootoday/components/section/structure/_split.scss
257
262
  - _sass/modootoday/components/section/structure/_steps.scss
258
263
  - _sass/modootoday/components/section/structure/_tabs.scss
259
264
  - _sass/modootoday/components/section/structure/_timeline.scss
@@ -301,6 +306,7 @@ files:
301
306
  - assets/css/modootoday.scss
302
307
  - assets/css/modootoday/doc.scss
303
308
  - assets/css/modootoday/figure.scss
309
+ - assets/css/modootoday/lists.scss
304
310
  - assets/css/modootoday/loading.scss
305
311
  - assets/css/modootoday/search.scss
306
312
  - assets/css/modootoday/showcase/card.scss
@@ -315,6 +321,7 @@ files:
315
321
  - assets/css/modootoday/structure/faq.scss
316
322
  - assets/css/modootoday/structure/filetree.scss
317
323
  - assets/css/modootoday/structure/params.scss
324
+ - assets/css/modootoday/structure/split.scss
318
325
  - assets/css/modootoday/structure/steps.scss
319
326
  - assets/css/modootoday/structure/tabs.scss
320
327
  - assets/css/modootoday/structure/timeline.scss
@@ -365,6 +372,7 @@ files:
365
372
  - lib/jekyll-theme-modootoday.rb
366
373
  - lib/modootoday/check.rb
367
374
  - lib/modootoday/check/a11y/content.rb
375
+ - lib/modootoday/check/a11y/illustration.rb
368
376
  - lib/modootoday/check/a11y/new_window.rb
369
377
  - lib/modootoday/check/blog.rb
370
378
  - lib/modootoday/check/build_output.rb