jekyll-theme-centos 2.52.0.beta.104 → 2.52.0.beta.106

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/_config.yml +2 -2
  3. data/_data/base/accordion_schema.yml +6 -0
  4. data/_data/base/announcement_schema.yml +1 -1
  5. data/_data/base/artwork_schema.yml +17 -3
  6. data/_data/base/bits_schema.yml +9 -4
  7. data/_data/base/breadcrumb_schema.yml +14 -0
  8. data/_data/base/breakingnews_schema.yml +1 -1
  9. data/_data/base/calendar_schema.yml +17 -2
  10. data/_data/base/card_schema.yml +2 -2
  11. data/_data/base/copyright_schema.yml +7 -17
  12. data/_data/base/copyvalue_schema.yml +4 -1
  13. data/_data/base/datatable_schema.yml +3 -3
  14. data/_data/base/download_schema.yml +42 -16
  15. data/_data/base/event_schema.yml +1 -1
  16. data/_data/base/footer_schema.yml +7 -6
  17. data/_data/base/highlight_schema.yml +7 -5
  18. data/_data/base/image_schema.yml +1 -1
  19. data/_data/base/link_schema.yml +13 -19
  20. data/_data/base/navbar_schema.yml +6 -0
  21. data/_data/base/preview_schema.yml +8 -4
  22. data/_data/base/project_schema.yml +4 -2
  23. data/_data/base/script_schema.yml +4 -4
  24. data/_data/base/shortcut_schema.yml +2 -2
  25. data/_data/base/social_schema.yml +2 -2
  26. data/_data/base/sponsors.yml +3 -2
  27. data/_data/base/sponsors_schema.yml +20 -0
  28. data/_data/base/title_schema.yml +14 -0
  29. data/_data/base/toc_schema.yml +16 -2
  30. data/_includes/base/accordion-item.html.liquid +11 -32
  31. data/_includes/base/accordion.html.liquid +3 -29
  32. data/_includes/base/announcement.html.liquid +8 -35
  33. data/_includes/base/artwork.html.liquid +5 -38
  34. data/_includes/base/backtotop.html.liquid +3 -42
  35. data/_includes/base/bits.html.liquid +26 -74
  36. data/_includes/base/breadcrumb.html.liquid +15 -49
  37. data/_includes/base/breakingnews.html.liquid +6 -43
  38. data/_includes/base/calendar-events.html.liquid +3 -37
  39. data/_includes/base/calendar-init.html.liquid +7 -63
  40. data/_includes/base/calendar.html.liquid +3 -34
  41. data/_includes/base/card.html.liquid +4 -44
  42. data/_includes/base/color-table.html.liquid +18 -4
  43. data/_includes/base/configuration_variables-default-cell.html.liquid +6 -50
  44. data/_includes/base/configuration_variables-nested.html.liquid +6 -7
  45. data/_includes/base/configuration_variables.html.liquid +9 -11
  46. data/_includes/base/content.html.liquid +7 -4
  47. data/_includes/base/copyright.html.liquid +8 -39
  48. data/_includes/base/copyvalue.html.liquid +3 -70
  49. data/_includes/base/datatable.html.liquid +4 -40
  50. data/_includes/base/download.html.liquid +12 -66
  51. data/_includes/base/download_architecture.html.liquid +29 -117
  52. data/_includes/base/download_commands.html.liquid +2 -14
  53. data/_includes/base/download_documentation.html.liquid +13 -47
  54. data/_includes/base/download_eol.html.liquid +5 -15
  55. data/_includes/base/download_filevalue.html.liquid +9 -49
  56. data/_includes/base/download_screenshots.html.liquid +10 -65
  57. data/_includes/base/download_sections.html.liquid +2 -19
  58. data/_includes/base/event.html.liquid +8 -63
  59. data/_includes/base/fontawesome.html.liquid +4 -38
  60. data/_includes/base/footer.html.liquid +9 -2
  61. data/_includes/base/head.html.liquid +6 -47
  62. data/_includes/base/highlight.html.liquid +4 -37
  63. data/_includes/base/image.html.liquid +5 -22
  64. data/_includes/base/link.html.liquid +38 -57
  65. data/_includes/base/locales.html.liquid +6 -26
  66. data/_includes/base/navbar.html.liquid +16 -45
  67. data/_includes/base/navindex.html.liquid +40 -111
  68. data/_includes/base/ogp.html.liquid +17 -83
  69. data/_includes/base/preview.html.liquid +18 -57
  70. data/_includes/base/project.html.liquid +4 -29
  71. data/_includes/base/shortcut.html.liquid +7 -37
  72. data/_includes/base/social.html.liquid +4 -42
  73. data/_includes/base/sponsors-cards.html.liquid +4 -55
  74. data/_includes/base/sponsors-carousel.html.liquid +11 -59
  75. data/_includes/base/sponsors-data.html.liquid +32 -0
  76. data/_includes/base/title.html.liquid +5 -42
  77. data/_includes/base/toc-generator.html.liquid +4 -0
  78. data/_includes/base/toc.html.liquid +28 -81
  79. data/_sass/base/_customization.scss +18 -0
  80. data/_sass/base/_preview.scss +5 -9
  81. data/assets/icons/favicon.ico +0 -0
  82. data/assets/js/base/backtotop.js +3 -3
  83. data/assets/js/base/copyvalue.js +15 -11
  84. data/assets/js/base/datatable.js +6 -13
  85. data/assets/js/base/highlight.js +46 -31
  86. metadata +2 -1
@@ -1,27 +1,25 @@
1
1
  {% comment %}
2
- Generates the top-level property table and initiates the recursive generation
3
- of separate, nested DataTables for complex properties.
2
+ configuration_variables: renders the page component's schema properties as a table, then one nested table per object or array-of-objects property.
3
+ Data: include.schema > site.data.<component>_schema > site.data.base.<component>_schema
4
+ Schema: src/YAML/configuration_variables_schema.yml
4
5
  {% endcomment %}
5
6
 
6
7
  {% assign component = page.component.name %}
7
- {% assign property_name = "with_" | append: component %}
8
8
  {% assign property_name_schema = component | append: "_schema" %}
9
9
  {% assign property_schema = include.schema | default: site.data[property_name_schema] | default: site.data.base[property_name_schema] %}
10
10
 
11
- {% assign col_name = include.col_name | default: "Name" %}
12
- {% assign col_type = include.col_type | default: "Type" %}
13
- {% assign col_default = include.col_default | default: "Default" %}
14
- {% assign col_description = include.col_description | default: "Description" %}
11
+ {% assign col_name = include.col_name | default: site.data.base.configuration_variables_schema.properties.col_name.default %}
12
+ {% assign col_type = include.col_type | default: site.data.base.configuration_variables_schema.properties.col_type.default %}
13
+ {% assign col_default = include.col_default | default: site.data.base.configuration_variables_schema.properties.col_default.default %}
14
+ {% assign col_description = include.col_description | default: site.data.base.configuration_variables_schema.properties.col_description.default %}
15
15
 
16
16
  {% assign columns = include.columns | default: property_schema.columns %}
17
17
 
18
18
  {% if property_schema.properties %}
19
19
 
20
- {% assign table_classes = "table dataTable" %}
21
-
22
20
  <p>The <code>{{ page.component.name }}</code> {{ page.component.type }} understands the following configuration variables:</p>
23
21
 
24
- <table class="{{ table_classes }}">
22
+ <table class="table dataTable">
25
23
  <thead>
26
24
  <tr>
27
25
  <th scope="col">{{ col_name }}</th>
@@ -86,5 +84,5 @@ of separate, nested DataTables for complex properties.
86
84
  {% endfor -%}
87
85
 
88
86
  {% else %}
89
- <p>The <code>{{ page.component.name }}</code> {{ page.component.type }} {{ pt }} doesn't have configuration variables.</p>
87
+ <p>The <code>{{ page.component.name }}</code> {{ page.component.type }} doesn't have configuration variables.</p>
90
88
  {% endif %}
@@ -1,3 +1,8 @@
1
+ {% comment %}
2
+ content: renders the main element with the announcement, bits, event, breadcrumb, title, artwork, and toc rows, then the page content.
3
+ Data: include.data > page.with_content_data > site.data.content > site.data.base.content
4
+ Schema: src/YAML/content_schema.yml
5
+ {% endcomment %}
1
6
  {%- assign with_content = include.with_content
2
7
  | default: page.with_content
3
8
  -%}
@@ -21,13 +26,11 @@
21
26
 
22
27
  {%- assign row_class = include.row_class
23
28
  | default: with_content_data.row_class
24
- | default: content_schema.properties.with_content_data.properties.row_class.default
25
- | default: 'row mb-3' -%}
29
+ | default: content_schema.properties.with_content_data.properties.row_class.default -%}
26
30
 
27
31
  {%- assign col_class = include.col_class
28
32
  | default: with_content_data.col_class
29
- | default: content_schema.properties.with_content_data.properties.col_class.default
30
- | default: 'col' -%}
33
+ | default: content_schema.properties.with_content_data.properties.col_class.default -%}
31
34
 
32
35
  {%- if with_content -%}
33
36
  <main class="{{ class }}">
@@ -1,34 +1,7 @@
1
1
  {% comment %}
2
- ================================================================================
3
- Template: Copyright Footer Component
4
- Purpose: Renders copyright notice with author name and optional legal links.
5
- Shows dynamic year, author, and links to Terms of Service, Privacy Policy, etc.
6
- ================================================================================
7
-
8
- Design Logic:
9
- - Dynamic year: automatically uses current year from site.time
10
- - Legal links looping: flexible number of links in footer
11
- - Reusable link component: consistent styling
12
- - Capture-based rendering: builds complex link string before output
13
- - Border separator styling: visual dividers between links
14
-
15
- Required Inputs:
16
- - author: Copyright holder name (e.g., "The CentOS Project")
17
-
18
- Optional Inputs:
19
- - legals: Array of legal link objects
20
- - name: Link text (e.g., "Terms of Service")
21
- - link: URL for legal document
22
- - color: Link color (defaults to light)
23
- - icon: Icon class (optional)
24
- - class: Custom CSS classes (optional)
25
-
26
- Data Source Hierarchy (priority):
27
- 1. include.data
28
- 2. page.with_copyright_data
29
- 3. site.data.copyright
30
- 4. site.data.base.copyright
31
- ================================================================================
2
+ copyright: renders the copyright line with the current year, the author, and the legal links.
3
+ Data: include.data > page.with_copyright_data > site.data.copyright > site.data.base.copyright
4
+ Schema: src/YAML/copyright_schema.yml
32
5
  {% endcomment %}
33
6
 
34
7
  {%- assign with_copyright = include.with_copyright
@@ -44,9 +17,6 @@ Data Source Hierarchy (priority):
44
17
  {%- assign with_copyright_schema = site.data.base.copyright_schema.properties.with_copyright_data.properties %}
45
18
 
46
19
  {%- if with_copyright %}
47
-
48
- {%- comment %} === Presentation === {% endcomment %}
49
- {%- comment %} --- Defaults --- {% endcomment %}
50
20
  {%- assign copyright_author = with_copyright_data.author | default: with_copyright_schema.author.default %}
51
21
  {%- assign copyright_legals = with_copyright_data.legals | default: with_copyright_schema.legals.default %}
52
22
 
@@ -54,20 +24,19 @@ Data Source Hierarchy (priority):
54
24
 
55
25
  {%- capture copyright_links -%}
56
26
  {%- for item in copyright_legals -%}
57
- {%- assign copyright_link_color = item.color | default: copyright_legal_schema.color.default %}
58
- {%- assign copyright_link_class = item.class | default: copyright_legal_schema.class.default | replace: '{color}', copyright_link_color %}
27
+ {%- assign copyright_link_class = item.class | default: copyright_legal_schema.class.default %}
28
+ {%- comment %} The span draws the separator outside the link, so the focus ring covers only the link text. Its border color matches the footer's default text color. {% endcomment -%}
29
+ <span class="border-start border-centos-purple-100 ms-2 ps-2">
59
30
  {%- include base/link.html.liquid
60
31
  class = copyright_link_class
61
32
  name = item.name
62
33
  link = item.link
63
- color = item.color
64
34
  role = item.role
65
35
  icon = item.icon
66
36
  icon_position = item.icon_position
67
37
  -%}
38
+ </span>
68
39
  {%- endfor -%}
69
- {%- endcapture -%}
70
-
71
- {%- comment %} --- HTML --- {% endcomment %}
40
+ {%- endcapture %}
72
41
  <p class="my-1">Copyright &copy; {{ site.time | date: "%Y" }} {{ copyright_author }} {{ copyright_links }}</p>
73
42
  {%- endif -%}
@@ -1,59 +1,7 @@
1
1
  {% comment %}
2
- ================================================================================
3
- Template: Copy Value Component
4
- Purpose: Copy to clipboard with visual feedback (checkmark + tooltip).
5
- ================================================================================
6
-
7
- Required Usage (in template):
8
- <button type="button" class="copy-btn focus-ring" data-copy-value="{{ command | escape }}"
9
- data-bs-toggle="tooltip" data-bs-title="Copy command">
10
- <span class="fa-regular fa-copy"></span>
11
- </button>
12
-
13
- ACCESSIBILITY — every `.copy-btn` must be a `<button type="button">`. This
14
- gives it keyboard focus and Enter/Space activation automatically.
15
- `_customization.scss` resets the button's default border, background, and
16
- padding back to a plain icon trigger. `.input-group-text.copy-btn`
17
- (checksum and SRI-hash rows) is the exception — it keeps Bootstrap's own
18
- addon box styling instead. Every `.copy-btn` also carries `focus-ring` so
19
- its keyboard-focus ring matches the rest of the theme's interactive
20
- elements — see `.focus-ring:focus:not(:focus-visible)` in
21
- `_customization.scss`.
22
-
23
- SECURITY — `data-copy-value` MUST always go through Liquid's `| escape` (or
24
- equivalent HTML-attribute encoding) at the point where it is written, with no
25
- exception for values that "look safe". The browser's own HTML parser decodes
26
- entities (&quot;, &amp;, &#39;, …) in an attribute value as it parses the
27
- tag, so by the time the JS reads `data-copy-value` it is already plain text —
28
- there is no separate decoding step to rely on for sanitization. An unescaped,
29
- attacker-influenced value (a query parameter, CMS field, etc.) placed
30
- directly in data-copy-value breaks out of the HTML attribute at render time,
31
- before this script ever runs.
32
-
33
- Behavior:
34
- - Visual feedback: checkmark icon + success tooltip
35
- - Auto-reset: returns to original state after delay
36
- - Clipboard API: native browser copy functionality
37
-
38
- Required Inputs (from HTML element):
39
- - class="copy-btn": Marks the element as a copy trigger
40
- - data-copy-value: Text to copy to clipboard
41
-
42
- Optional Inputs (from data objects):
43
- - reset_delay: Milliseconds before reset (default varies by schema)
44
- - checkmark_path: SVG path for success icon
45
- - checkmark_viewbox: SVG viewBox for success icon
46
- - success_message: Tooltip message on success
47
-
48
- Data Source Hierarchy (in priority order):
49
- 1. include.data - Explicitly passed configuration
50
- 2. page.with_copyvalue_data - Page front matter
51
- 3. site.data.copyvalue - Site configuration
52
- 4. site.data.base.copyvalue - Theme defaults
53
-
54
- Dependencies:
55
- Bootstrap 5, FontAwesome 6+, Clipboard API (modern browsers)
56
- ================================================================================
2
+ copyvalue: loads the script that copies each .copy-btn's data-copy-value to the clipboard.
3
+ Data: include.data > page.with_copyvalue_data > site.data.copyvalue > site.data.base.copyvalue
4
+ Schema: src/YAML/copyvalue_schema.yml
57
5
  {% endcomment %}
58
6
 
59
7
  {%- assign with_copyvalue = include.with_copyvalue
@@ -69,30 +17,15 @@ Bootstrap 5, FontAwesome 6+, Clipboard API (modern browsers)
69
17
  {%- assign with_copyvalue_schema = site.data.base.copyvalue_schema.properties.with_copyvalue_data.properties %}
70
18
 
71
19
  {%- if with_copyvalue %}
72
-
73
- {%- comment %} === Presentation === {% endcomment %}
74
-
75
- {%- comment %} --- Defaults --- {% endcomment %}
76
20
  {%- assign copyvalue_reset_delay = with_copyvalue_data.reset_delay | default: with_copyvalue_schema.reset_delay.default %}
77
21
  {%- assign copyvalue_checkmark_path = with_copyvalue_data.checkmark_path | default: with_copyvalue_schema.checkmark_path.default %}
78
22
  {%- assign copyvalue_checkmark_viewbox = with_copyvalue_data.checkmark_viewbox | default: with_copyvalue_schema.checkmark_viewbox.default %}
79
23
  {%- assign copyvalue_success_message = with_copyvalue_data.success_message | default: with_copyvalue_schema.success_message.default %}
80
-
81
- {%- comment %} --- HTML --- {% endcomment %}
82
- <!-- Load Copy Value functionality with configuration via data attributes -->
83
24
  <script id="centos-copyvalue"
84
- {%- if with_copyvalue_data.reset_delay %}
85
25
  data-reset-delay="{{ copyvalue_reset_delay }}"
86
- {%- endif %}
87
- {%- if with_copyvalue_data.checkmark_path %}
88
26
  data-checkmark-path="{{ copyvalue_checkmark_path }}"
89
- {%- endif %}
90
- {%- if with_copyvalue_data.checkmark_viewbox %}
91
27
  data-checkmark-viewbox="{{ copyvalue_checkmark_viewbox }}"
92
- {%- endif %}
93
- {%- if with_copyvalue_data.success_message %}
94
28
  data-success-message="{{ copyvalue_success_message }}"
95
- {%- endif %}
96
29
  src="{{ site.baseurl }}/assets/js/base/copyvalue.min.js"
97
30
  defer></script>
98
31
 
@@ -1,32 +1,7 @@
1
1
  {% comment %}
2
- ================================================================================
3
- Template: DataTables Library Initialization Component
4
- Purpose: Loads DataTables jQuery plugin from CDN with SRI integrity checks.
5
- Initializes DataTables on all .dataTable elements with responsive design,
6
- pagination, searching, and sorting.
7
- ================================================================================
8
-
9
- Design Logic:
10
- - CDN-based library loading with SRI integrity checks
11
- - Responsive table design for mobile devices
12
- - Smart pagination: only if table has >10 rows
13
- - Automatic element detection: targets all .dataTable classes
14
- - jQuery dependency: requires jQuery loaded first
15
-
16
- Optional Inputs:
17
- - type: "head" for CSS include, "script" for JavaScript
18
- - version: DataTables version (e.g., "1.13.7")
19
- - css_integrity, script_integrity: SRI integrity hashes
20
- - page_length: Default rows per page (default: 10)
21
- - length_menu: Pagination options
22
- - data: DataTables configuration object
23
-
24
- Data Source Hierarchy (priority):
25
- 1. include.data
26
- 2. page.with_datatable_data
27
- 3. site.data.datatable
28
- 4. site.data.base.datatable
29
- ================================================================================
2
+ datatable: loads DataTables from the CDN and enables it on every table.dataTable. head.html.liquid calls it with type="head" (CSS) and type="script" (JS).
3
+ Data: include.data > page.with_datatable_data > site.data.datatable > site.data.base.datatable
4
+ Schema: src/YAML/datatable_schema.yml
30
5
  {% endcomment %}
31
6
 
32
7
  {%- assign with_datatable = include.with_datatable
@@ -43,8 +18,6 @@ Data Source Hierarchy (priority):
43
18
 
44
19
  {%- if with_datatable %}
45
20
 
46
- {%- comment %} === Presentation === {% endcomment %}
47
- {%- comment %} --- Defaults --- {% endcomment %}
48
21
  {%- assign datatable_version = with_datatable_data.version | default: with_datatable_schema.version.default %}
49
22
  {%- assign datatable_css_integrity = with_datatable_data.css_integrity | default: with_datatable_schema.css_integrity.default %}
50
23
  {%- assign datatable_script_integrity = with_datatable_data.script_integrity | default: with_datatable_schema.script_integrity.default %}
@@ -56,24 +29,15 @@ Data Source Hierarchy (priority):
56
29
  {%- endcapture %}
57
30
 
58
31
  {%- capture html_datatable_script %}
59
- <!-- DataTables Library -->
60
32
  <script src="https://cdn.datatables.net/{{ datatable_version }}/datatables.min.js" integrity="{{ datatable_script_integrity }}" crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
61
-
62
- <!-- Initialize DataTables with configuration via data attributes -->
63
33
  <script id="centos-datatable"
64
- {%- if with_datatable_data.page_length %}
65
34
  data-page-length="{{ datatable_page_length }}"
66
- {%- endif %}
67
- {%- if with_datatable_data.length_menu %}
68
35
  data-length-menu='{{ datatable_length_menu | split: "," | jsonify }}'
69
- {%- endif %}
70
36
  src="{{ site.baseurl }}/assets/js/base/datatable.min.js"
71
37
  defer></script>
72
38
  {%- endcapture %}
73
39
 
74
- {%- comment %} --- HTML --- {% endcomment %}
75
- {%- assign type = include.type %}
76
- {%- case type %}
40
+ {%- case include.type %}
77
41
  {%- when "head" %}
78
42
  {{ html_datatable_head }}
79
43
  {%- when "script" %}
@@ -1,36 +1,7 @@
1
1
  {% comment %}
2
- ================================================================================
3
- Template: Download Component
4
- Purpose: Renders one or more download entries. Full data-driven section
5
- component (like bits/sponsors), gated by with_download and resolved through
6
- the standard 4-level fallback chain. Each entry in with_download_data is an
7
- independent download "card" (e.g. one per distribution); include.title
8
- filters to a single entry. Per-release sections (architecture/documentation/
9
- eol/commands/screenshots) render automatically when their data is present —
10
- no separate with_X flag per section, matching how card.html.liquid renders
11
- its own sections only "if content". Shared between both render paths via
12
- download_sections.html.liquid.
13
-
14
- Alternative images (flavors) are not a section of this schema — a flavor
15
- is just another download entry, linked via `documentation` or page
16
- content rather than nested inside this one.
17
-
18
- Data Source Hierarchy (in priority order):
19
- 1. include.data - Explicitly passed array
20
- 2. page.with_download_data - Page front matter
21
- 3. site.data.download - Site data
22
- 4. site.data.base.download - Theme defaults
23
-
24
- Optional Inputs (from include):
25
- - title: Render only the entry whose title matches exactly.
26
- - id: HTML id prefix for the nav-pills tab selector (default: DownloadsCard-1).
27
- - with_title: Set to false to skip the entry's own title heading, e.g. when
28
- the calling page already presents it as a real markdown
29
- heading immediately above the include (for TOC/anchor
30
- navigation). Default: true.
31
- - with_description: Set to false to skip the entry's own description text for the
32
- same reason. Default: true.
33
- ================================================================================
2
+ download: renders each download entry, or only the one matching include.title, with its releases as tabs, or inline when there is one release.
3
+ Data: include.data > page.with_download_data > site.data.download > site.data.base.download
4
+ Schema: src/YAML/download_schema.yml
34
5
  {% endcomment %}
35
6
 
36
7
  {%- assign with_download = include.with_download
@@ -52,29 +23,17 @@ Optional Inputs (from include):
52
23
  {%- continue %}
53
24
  {%- endif %}
54
25
 
55
- {%- comment %} image_base_path defaults to a path derived from the current
56
- page's own URL (page.url already ends in "/" for pretty permalinks) rather
57
- than a fixed schema default — every page gets its own collision-free
58
- screenshot directory automatically, with no per-page front matter needed,
59
- unless the entry explicitly overrides it. See download_schema.yml's
60
- image_base_path description for the full rationale. {% endcomment %}
26
+ {%- comment %} Without an override, each page gets its own screenshot directory from page.url; see image_base_path in download_schema.yml. {% endcomment %}
61
27
  {%- if entry.image_base_path and entry.image_base_path != "" %}
62
28
  {%- assign entry_image_base_path = entry.image_base_path %}
63
29
  {%- else %}
64
30
  {%- assign entry_image_base_path = "/assets/img" | append: page.url %}
65
31
  {%- endif %}
66
32
 
67
- {%- comment %} entry.title feeds an HTML id (card_id) below. A hand-picked
68
- remove: chain (mirroring link.html.liquid's anchor assignment) missed
69
- characters like em dashes, which the id spec forbids just the same —
70
- slugify handles any non-alphanumeric character, not just a fixed list. {% endcomment %}
33
+ {%- comment %} slugify, not a remove: chain, because entry.title feeds an HTML id and can hold any character, such as an em dash. {% endcomment %}
71
34
  {%- assign entry_title_id_safe = entry.title | slugify %}
72
35
 
73
- {%- if include.id and include.id != "" %}
74
- {%- assign card_id = include.id | append: "-" | append: entry_title_id_safe | downcase | replace: " ", "-" %}
75
- {%- else %}
76
- {%- assign card_id = "DownloadsCard-1-" | append: entry_title_id_safe | downcase | replace: " ", "-" %}
77
- {%- endif %}
36
+ {%- assign card_id = include.id | default: "DownloadsCard-1" | append: "-" | append: entry_title_id_safe | downcase | replace: " ", "-" %}
78
37
 
79
38
  <div class="mb-1">
80
39
  {%- assign single_release = nil %}
@@ -92,21 +51,13 @@ Optional Inputs (from include):
92
51
  {{ entry.description | strip_newlines | markdownify }}
93
52
  {%- endif %}
94
53
 
95
- {%- comment %} A `releases` field always identifies which version(s) an
96
- entry covers; when there's more than one, a tabbed nav-pills selector
97
- switches between them. When there's exactly one — the common case for a
98
- project like CentOS Stream, which only ever has one active version —
99
- the tab selector is skipped and that release's sections render
100
- directly, since a single, permanently-active, non-interactive tab would
101
- just be chrome with nothing to switch between. {% endcomment %}
102
- {%- if entry.releases.size == 1 %}
103
- {%- for release in entry.releases %}
104
- {%- assign release_data = release[1] %}
54
+ {%- comment %} A single release renders its sections directly; a lone tab would have nothing to switch between. {% endcomment %}
55
+ {%- if single_release %}
56
+ {%- assign release_data = single_release[1] %}
105
57
  {%- assign release_id = card_id | append: "-pills1" %}
106
58
  <div id="{{ release_id }}" class="bg-white rounded py-3 border">
107
59
  {%- include base/download_sections.html.liquid release_data=release_data release_id=release_id image_base_path=entry_image_base_path -%}
108
60
  </div>
109
- {%- endfor %}
110
61
  {%- elsif entry.releases.size > 1 %}
111
62
  <ul class="nav nav-pills mb-3 justify-content-start" id="{{ card_id }}-pills-tab" role="tablist">
112
63
  {%- for release in entry.releases %}
@@ -117,19 +68,14 @@ Optional Inputs (from include):
117
68
  data-bs-target="#{{ card_id }}-pills{{ forloop.index }}"
118
69
  type="button"
119
70
  role="tab"
120
- aria-controls="{{ card_id }}-pills-tab{{ forloop.index }}"
121
- {%- if forloop.first %} aria-selected="true"{% endif %}
71
+ aria-controls="{{ card_id }}-pills{{ forloop.index }}"
72
+ {%- if forloop.first %} aria-selected="true"{% else %} aria-selected="false"{% endif %}
122
73
  >{%- if release[1].icon and release[1].icon != "" %}<i class="{{ release[1].icon }} me-1"></i>{% endif %}{{ release[0] }}</button>
123
74
  </li>
124
75
  {%- endfor %}
125
76
  </ul>
126
77
 
127
- {%- comment %} border/rounded/bg-white live on this outer .tab-content div,
128
- not the inner .tab-pane — a .tab-pane's own corners are always square, so if
129
- the rounded, filled look lived there instead, its corners would poke past
130
- this div's border at each corner. overflow-hidden clips each pane to match.
131
- Mirrors the single-release path above, which only ever needs one
132
- rounded/bordered/filled box. {% endcomment %}
78
+ {%- comment %} Border and rounding live on .tab-content, not .tab-pane, whose square corners would poke past the border; overflow-hidden clips each pane. {% endcomment %}
133
79
  <div class="tab-content border rounded overflow-hidden bg-white" id="{{ card_id }}-pills-tabContent">
134
80
  {%- for release in entry.releases %}
135
81
  {%- assign release_data = release[1] %}
@@ -1,37 +1,9 @@
1
1
  {% comment %}
2
- ================================================================================
3
- Utility: Download Architecture Grid
4
- Purpose: One box per architecture, each with that architecture's own main
5
- download/checksum/signature and mirror links — the single per-architecture
6
- data source for a release. Called from download_sections.html.liquid.
7
-
8
- Required Inputs:
9
- - architecture: Map of architecture label (e.g. `x86_64`) to
10
- {download, checksum, signature, checksum_value, signature_value,
11
- mirrors: {iso, rpm, cloud, containers, vagrant, torrent}} — see
12
- download_schema.yml for the full field-by-field description.
13
- - release_id: The parent release's unique id, combined with each
14
- architecture's own label to build a page-unique id_prefix for the
15
- checksum/signature rows — see download_filevalue.html.liquid's id_prefix
16
- note for why that's needed.
17
-
18
- Within each box: icon + architecture title, then (if present) a Download
19
- button, then (if present) one input-group row each for checksum and
20
- signature, then (if present) the outline-primary mirror buttons — mirrors
21
- is always the last section, so it never carries a trailing rule.
22
-
23
- Design rationale for specific decisions lives as inline comments next to the
24
- code they explain: the architecture map's shape versus mirrors/flavors
25
- (above the grid loop below), the Download button's layout (above its
26
- markup), the checksum/signature row design (above the download_filevalue
27
- includes), and the responsive column/divider/orphan-row grid math (above
28
- the column-count and orphan-detection blocks immediately below).
29
- ================================================================================
2
+ download_architecture: renders one box per architecture with its download button, checksum and signature rows, and mirror buttons.
3
+ Schema: src/YAML/download_schema.yml
30
4
  {% endcomment %}
31
5
 
32
- {%- comment %} Column count tracks item count (capped at 4) so the grid always
33
- fills the row width, instead of a fixed row-cols-md-3 leaving a gap when there
34
- are only 1 or 2 architectures. {% endcomment %}
6
+ {%- comment %} Column count tracks the architecture count so the grid always fills the row width. {% endcomment %}
35
7
  {%- assign arch_count = include.architecture.size -%}
36
8
  {%- case arch_count -%}
37
9
  {%- when 1 -%}{%- assign arch_cols = 1 -%}
@@ -40,13 +12,7 @@ are only 1 or 2 architectures. {% endcomment %}
40
12
  {%- else -%}{%- assign arch_cols = 4 -%}
41
13
  {%- endcase -%}
42
14
 
43
- {%- comment %} Orphan-row detection: for each breakpoint's column count,
44
- figure out whether arch_count leaves a trailing partial row, and if so, the
45
- 0-based index where that partial row starts (orphan_*_start) and the width
46
- each of its boxes should stretch to (orphan_*_width = 100% / remainder). A
47
- value of -1 means "no orphan row at this breakpoint" — every box divides
48
- evenly, or this breakpoint isn't reached at all (arch_cols too low).
49
- {% endcomment %}
15
+ {%- comment %} orphan_*_start is the index where a trailing partial row begins and orphan_*_width the width its boxes stretch to; -1 means no partial row. {% endcomment %}
50
16
  {%- assign row_cols_classes = "row-cols-1" -%}
51
17
  {%- assign arch_divider_class = "" -%}
52
18
  {%- assign orphan_sm_start = -1 -%}
@@ -56,12 +22,7 @@ evenly, or this breakpoint isn't reached at all (arch_cols too low).
56
22
  {%- assign orphan_lg_start = -1 -%}
57
23
  {%- assign orphan_lg_width = "" -%}
58
24
 
59
- {%- comment %} Steps up one column per breakpoint (1 → sm:2 → md:3 → lg:4)
60
- instead of jumping straight from 1 to arch_cols at a single breakpoint, so
61
- stretching the window grows the grid progressively rather than in one hard
62
- jump. Capped at arch_cols throughout — a 2-architecture release stops
63
- adding columns at sm and never reaches 3 or 4, so it doesn't leave empty
64
- gaps at wider viewports. {% endcomment %}
25
+ {%- comment %} Columns step up one per breakpoint, capped at arch_cols, so the grid grows progressively and never leaves empty gaps. {% endcomment %}
65
26
  {%- if arch_cols >= 2 -%}
66
27
  {%- assign row_cols_classes = row_cols_classes | append: " row-cols-sm-2" -%}
67
28
  {%- assign arch_divider_class = "download-arch-divider-sm" -%}
@@ -99,30 +60,15 @@ gaps at wider viewports. {% endcomment %}
99
60
  {%- endif -%}
100
61
  {%- endif -%}
101
62
 
102
- {%- comment %} Each architecture owns its own download/checksum/signature/
103
- mirrors row directly in the architecture map, replacing an older separate
104
- mirrors array (which carried its own `architecture` field per row and could
105
- drift out of sync with itself across rows). Alternative images (flavors,
106
- e.g. a desktop spin) are deliberately NOT part of this grid — a flavor is
107
- just another download entry, not a smaller variant of this one; model it as
108
- its own with_download_data entry instead (releases keyed by flavor name
109
- reuse the tab selector) and link the two via `documentation` or page
110
- content — see examples/components/download-alternative-images.md.
111
- {% endcomment %}
63
+ {%- assign download_mirror_keys = "iso,rpm,cloud,containers,vagrant,torrent" | split: "," %}
64
+ {%- assign download_mirror_labels = "ISO Mirrors,RPM Mirrors,Cloud Images,Container Images,Vagrant Boxes,Torrent" | split: "," %}
65
+
66
+ {%- comment %} Flavors are separate download entries, not part of this grid; see examples/components/download-alternative-images.md. {% endcomment %}
112
67
  <div class="row {{ row_cols_classes }} g-3 align-items-start">
113
68
  {%- for arch in include.architecture %}
114
69
  {%- assign arch_name = arch[0] %}
115
70
  {%- assign arch_data = arch[1] %}
116
- {%- comment %} Each box beyond the first in its row gets the divider
117
- class for the breakpoint the grid has reached (arch_divider_class, set
118
- above per breakpoint) — a border-left shown only once the grid reaches
119
- its full arch_cols width, not at every intermediate step. That's
120
- deliberate: forloop.index0 modulo arch_cols only identifies "first in
121
- its row" correctly once the grid is actually arch_cols-wide, so dividers
122
- are hidden during the narrower intermediate steps rather than risk
123
- misplaced lines while the grid is still at 2 or 3 columns. A box that
124
- starts a wrapped second row (5+ architectures) correctly gets no divider
125
- either, same as the very first box overall. {% endcomment %}
71
+ {%- comment %} Dividers show only at the full arch_cols width, the only width where forloop.index0 modulo arch_cols finds each row's first box. {% endcomment %}
126
72
  {%- assign arch_row_position = forloop.index0 | modulo: arch_cols %}
127
73
  {%- assign arch_col_class = "col" %}
128
74
  {%- unless arch_row_position == 0 %}
@@ -144,16 +90,13 @@ content — see examples/components/download-alternative-images.md.
144
90
  {%- endif -%}
145
91
 
146
92
  {%- assign has_mirrors = false %}
147
- {%- if arch_data.mirrors.iso and arch_data.mirrors.iso != "" or arch_data.mirrors.rpm and arch_data.mirrors.rpm != "" or arch_data.mirrors.cloud and arch_data.mirrors.cloud != "" or arch_data.mirrors.containers and arch_data.mirrors.containers != "" or arch_data.mirrors.vagrant and arch_data.mirrors.vagrant != "" or arch_data.mirrors.torrent and arch_data.mirrors.torrent != "" %}
148
- {%- assign has_mirrors = true %}
149
- {%- endif %}
150
- {%- comment %} arch_name is a raw YAML map key, so strip
151
- attribute/markup-breaking characters before it seeds arch_id_prefix, which
152
- in turn becomes id/data-copy-value scoping in download_filevalue.html.liquid.
153
- Note: download.html.liquid's own entry_title_id_safe now uses `slugify`
154
- instead of this remove: chain, since slugify catches characters (e.g. em
155
- dashes) the chain below doesn't. {% endcomment %}
156
- {%- assign arch_name_id_safe = arch_name | remove: '"' | remove: "'" | remove: '<' | remove: '>' | remove: '&' | remove: '#' | remove: '?' | remove: '/' %}
93
+ {%- for mirror_key in download_mirror_keys %}
94
+ {%- if arch_data.mirrors[mirror_key] and arch_data.mirrors[mirror_key] != "" %}
95
+ {%- assign has_mirrors = true %}
96
+ {%- endif %}
97
+ {%- endfor %}
98
+ {%- comment %} slugify, not a remove: chain, because arch_name is a raw YAML key that feeds an HTML id and can hold any character. {% endcomment %}
99
+ {%- assign arch_name_id_safe = arch_name | slugify %}
157
100
  {%- assign arch_id_prefix = include.release_id | default: "download" | append: "-" | append: arch_name_id_safe | downcase | replace: " ", "-" %}
158
101
  <div class="{{ arch_col_class }}"{% if arch_orphan_style != "" %} style="{{ arch_orphan_style }}"{% endif %}>
159
102
  <div class="d-flex flex-column align-items-start p-3 pt-1">
@@ -178,31 +121,17 @@ content — see examples/components/download-alternative-images.md.
178
121
  {%- endif %}
179
122
 
180
123
  {%- if has_download_button or has_checksum_row or has_signature_row %}
181
- {%- comment %} These rows share this gap-2 flex-column wrapper so
182
- spacing between whichever rows are present is handled by the gap
183
- alone — no per-row conditional mt-2 class needed (Bootstrap's spacing
184
- scale maps 2 to the same 0.5rem for both mt-* and gap-*, so this is a
185
- visual no-op versus a conditional-margin approach, just less
186
- conditional logic). {% endcomment %}
124
+ {%- comment %} The wrapper's gap spaces whichever rows are present, so no row needs a conditional margin. {% endcomment %}
187
125
  <div class="w-100 mb-3 pb-3 border-bottom d-flex flex-column gap-2">
188
126
  {%- if has_download_button %}
189
- {%- comment %} d-flex flex-wrap gap-2, not Bootstrap's joined
190
- .btn-group, so the button reflows instead of overflowing into the
191
- neighboring architecture box at narrow column widths (e.g. the
192
- 4-column arrangement). Reuses base/link.html.liquid, the theme's
193
- canonical link/button component. {% endcomment %}
127
+ {%- comment %} flex-wrap, not .btn-group, so the button reflows instead of overflowing into the next box at narrow widths. {% endcomment %}
194
128
  <div class="d-flex flex-wrap gap-2" role="group" aria-label="Primary download actions">
195
- {%- include base/link.html.liquid class="btn focus-ring btn-primary flex-fill text-start" role="button" name="Download" link=arch_data.download icon="fa-solid fa-download" icon_position="start" -%}
129
+ {%- include base/link.html.liquid class="btn focus-ring btn-primary flex-fill text-start" name="Download" link=arch_data.download icon="fa-solid fa-download" icon_position="start" -%}
196
130
  </div>
197
131
  {%- endif %}
198
132
 
199
133
  {%- if has_checksum_row %}
200
- {%- comment %} checksum/signature and checksum_value/signature_value
201
- are two views of the same file, merged into one input-group row
202
- (rather than a link button plus a separate copy field) via
203
- base/download_filevalue.html.liquid — see that file for the row's
204
- own markup and its .link-primary / copy-value details.
205
- {% endcomment %}
134
+ {%- comment %} The file link and its value are two views of one file, so they share one download_filevalue row. {% endcomment %}
206
135
  {%- assign checksum_display = arch_data.checksum_value | default: arch_data.checksum %}
207
136
  {%- include base/download_filevalue.html.liquid arch_name=arch_name field=arch_data.checksum field_value=checksum_display icon="fa-solid fa-shield-halved" label="checksum" id_prefix=arch_id_prefix -%}
208
137
  {%- endif %}
@@ -215,32 +144,15 @@ content — see examples/components/download-alternative-images.md.
215
144
  {%- endif %}
216
145
 
217
146
  {%- if has_mirrors %}
218
- {%- comment %} These are outbound links to a mirror/registry, not
219
- in-page navigation, so each opens in a new tab (target="_blank") with
220
- the same trailing external-link icon as the Documentation section's
221
- buttons (base/download_documentation.html.liquid), rather than the
222
- Download button's leading icon above — the leading icon there signals
223
- the primary action; the trailing external icon here signals "leaves
224
- the site". {% endcomment %}
147
+ {%- comment %} Mirrors leave the site, so they open in a new tab with a trailing external-link icon. {% endcomment %}
225
148
  <div class="d-flex flex-wrap gap-2">
226
- {%- if arch_data.mirrors.iso and arch_data.mirrors.iso != "" %}
227
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="ISO Mirrors" link=arch_data.mirrors.iso target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
228
- {%- endif %}
229
- {%- if arch_data.mirrors.rpm and arch_data.mirrors.rpm != "" %}
230
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="RPM Mirrors" link=arch_data.mirrors.rpm target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
231
- {%- endif %}
232
- {%- if arch_data.mirrors.cloud and arch_data.mirrors.cloud != "" %}
233
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="Cloud Images" link=arch_data.mirrors.cloud target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
234
- {%- endif %}
235
- {%- if arch_data.mirrors.containers and arch_data.mirrors.containers != "" %}
236
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="Container Images" link=arch_data.mirrors.containers target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
237
- {%- endif %}
238
- {%- if arch_data.mirrors.vagrant and arch_data.mirrors.vagrant != "" %}
239
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="Vagrant Boxes" link=arch_data.mirrors.vagrant target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
240
- {%- endif %}
241
- {%- if arch_data.mirrors.torrent and arch_data.mirrors.torrent != "" %}
242
- {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" role="button" name="Torrent" link=arch_data.mirrors.torrent target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
243
- {%- endif %}
149
+ {%- for mirror_key in download_mirror_keys %}
150
+ {%- assign mirror_link = arch_data.mirrors[mirror_key] %}
151
+ {%- assign mirror_label = download_mirror_labels[forloop.index0] %}
152
+ {%- if mirror_link and mirror_link != "" %}
153
+ {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary flex-fill text-start" name=mirror_label link=mirror_link target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
154
+ {%- endif %}
155
+ {%- endfor %}
244
156
  </div>
245
157
  {%- endif %}
246
158
  </div>