jekyll-theme-centos 2.52.0.beta.101 → 2.52.0.beta.103

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.
@@ -49,7 +49,7 @@ properties:
49
49
  - _blank
50
50
  class:
51
51
  type: string
52
- default: "link link-{color} link-offset-3 link-offset-3-hover link-underline-{color} link-underline-opacity-0 link-underline-opacity-100-hover"
52
+ default: "link link-{color} link-offset-3 link-offset-3-hover link-underline-{color} link-underline-opacity-0 link-underline-opacity-100-hover focus-ring"
53
53
  description: >-
54
54
  One or more CSS classes to apply to the link element. Use this
55
55
  to override default link styling and apply component styles like
@@ -0,0 +1,88 @@
1
+ title: Preview Box Component Configuration
2
+ description: >-
3
+ Schema for configuring the preview box wrapper. `preview.html.liquid`
4
+ renders a bordered container around up to three independently optional
5
+ rows, stacked flush against the container and each other (zero margin
6
+ between rows, though individual rows may carry their own padding) in
7
+ this order — a live-rendered demo, a thin toolbar divider (language
8
+ label, copy button), and a syntax-highlighted code sample — modeled on
9
+ Bootstrap's own documentation snippet pattern
10
+ (https://getbootstrap.com/docs/5.3/components/accordion/).
11
+ Used by documentation pages to visually set off a component's rendered
12
+ output and the source that produced it as one unit. When only `content`
13
+ is supplied, this component renders exactly as it always has: a single
14
+ bordered/padded `<div>` with no toolbar or code row, for backward
15
+ compatibility with existing call sites.
16
+ type: object
17
+ properties:
18
+ class:
19
+ type: string
20
+ default: "mb-3 p-3 text-bg-light border border-1 rounded"
21
+ description: >-
22
+ CSS classes applied to the wrapper `<div>` (e.g., `mb-3 p-3 border
23
+ border-1 rounded` for a plain box without the light background, or
24
+ `mb-3 p-3 text-bg-light border border-1 border-primary-subtle
25
+ rounded` for a primary-tinted border).
26
+ content:
27
+ type: string
28
+ default: ""
29
+ description: >-
30
+ Pre-rendered HTML string for the demo row's contents, built by the
31
+ caller via a `capture` tag around one or more sibling includes. Set
32
+ off with `content_class`'s background to read as distinct from the
33
+ code row below it. When empty, this row does not render.
34
+ content_class:
35
+ type: string
36
+ default: "bg-body pt-2 px-2"
37
+ description: >-
38
+ CSS classes applied to the demo row's `<div class="preview-content">`
39
+ wrapper (e.g., `bg-body pt-2 px-2` for the page's own background with
40
+ top/side breathing room and no bottom padding, so it sits flush
41
+ against the toolbar row below; the outer `.preview` container
42
+ supplies the box's border, rounding, and corner-clipping — this row
43
+ does no rounding of its own).
44
+ language:
45
+ type: string
46
+ default: ""
47
+ description: >-
48
+ Language identifier for the code sample (e.g., `liquid`, `html`, `js`,
49
+ `yaml`), kept lowercase. Shown on the toolbar's left side (visually
50
+ uppercased via `language_class`'s `text-uppercase`, the value itself
51
+ stays lowercase) and also applied as the `language-{{ language }}`
52
+ class on the code row's `<code>` element so highlight.js colorizes it
53
+ correctly (see base/highlight.html.liquid). When empty and `code` is
54
+ also empty, the toolbar row does not render; when empty but `code` is
55
+ set, no language label renders and highlight.js falls back to
56
+ language auto-detection.
57
+ language_class:
58
+ type: string
59
+ default: "small font-monospace text-body-secondary text-uppercase"
60
+ description: >-
61
+ CSS classes applied to the language label `<span>` (e.g., `small
62
+ font-monospace text-body-secondary text-uppercase`).
63
+ copy_class:
64
+ type: string
65
+ default: ""
66
+ description: >-
67
+ Extra CSS classes applied to the toolbar's copy-button `<span>`. Set
68
+ to `d-none` to hide the button without removing `code` (e.g., a
69
+ snippet that's intentionally not meant to be copied). The button
70
+ itself only renders when `code` is supplied — there is nothing to
71
+ copy otherwise.
72
+ code:
73
+ type: string
74
+ default: ""
75
+ description: >-
76
+ Raw code sample text for the code row, escaped at render time (via
77
+ Liquid's `escape` filter) before being placed inside `<code>` and
78
+ inside the copy button's `data-copy-value` attribute. When empty,
79
+ this row does not render, and the toolbar's copy button does not
80
+ render either.
81
+ code_class:
82
+ type: string
83
+ default: "p-0 m-0"
84
+ description: >-
85
+ Extra CSS classes applied to the code row's `<div class="preview-code">`
86
+ wrapper. The `<pre>` inside it carries its own fixed `p-2` padding,
87
+ separate from this property.
88
+ additionalProperties: false
@@ -24,10 +24,10 @@
24
24
  {%- capture _webaim_link %}https://webaim.org/resources/contrastchecker/?fcolor={{ _webaim_fcolor }}&bcolor={{ _webaim_bcolor }}{%- endcapture %}
25
25
  <tr>
26
26
  <td class="text-nowrap">{{ _item_name }}</td>
27
- <td class="text-nowrap"><i class="fas fa-circle fa-xl" style="color: {{ item.hex }}"></i><span role="button" class="copy-btn" data-bs-toggle="tooltip" data-bs-title="Copy hex value" data-copy-value="{{ item.hex }}" aria-label="Copy hex value {{ item.hex }}"><code>{{ item.hex }}</code> <span class="fa-regular fa-copy"></span></span></td>
28
- <td class="text-nowrap"><span role="button" class="copy-btn" data-bs-toggle="tooltip" data-bs-title="Copy RGB value" data-copy-value="{{ item.rgb }}" aria-label="Copy RGB value {{ item.rgb }}"><code>{{ item.rgb }}</code> <span class="fa-regular fa-copy"></span></span></td>
29
- <td class="text-nowrap"><span role="button" class="copy-btn" data-bs-toggle="tooltip" data-bs-title="Copy HSV value" data-copy-value="{{ item.hsv }}" aria-label="Copy HSV value {{ item.hsv }}"><code>{{ item.hsv }}</code> <span class="fa-regular fa-copy"></span></span></td>
30
- <td class="text-nowrap">{% if item.text_color == "Black" %}<span class="badge {{ item.text_badge }}" data-bs-toggle="tooltip" data-bs-title="{{ item.text_color }} — near-black brand color, not literal black"><span aria-hidden="true">{{ item.text_color | slice: 0, 1 }}</span><span class="visually-hidden">{{ item.text_color }}</span></span>{% else %}<span class="badge {{ item.text_badge }}" data-bs-toggle="tooltip" data-bs-title="{{ item.text_color }}"><span aria-hidden="true">{{ item.text_color | slice: 0, 1 }}</span><span class="visually-hidden">{{ item.text_color }}</span></span>{% endif %} <span role="button" class="copy-btn" data-bs-toggle="tooltip" data-bs-title="Copy text color hex value" data-copy-value="{{ item.text_color_hex }}" aria-label="Copy text color hex value {{ item.text_color_hex }}"><code>{{ item.text_color_hex }}</code> <span class="fa-regular fa-copy"></span></span></td>
27
+ <td class="text-nowrap"><i class="fas fa-circle fa-xl" style="color: {{ item.hex }}"></i><button type="button" class="copy-btn focus-ring" data-bs-toggle="tooltip" data-bs-title="Copy hex value" data-copy-value="{{ item.hex }}" aria-label="Copy hex value {{ item.hex }}"><code>{{ item.hex }}</code> <span class="fa-regular fa-copy"></span></button></td>
28
+ <td class="text-nowrap"><button type="button" class="copy-btn focus-ring" data-bs-toggle="tooltip" data-bs-title="Copy RGB value" data-copy-value="{{ item.rgb }}" aria-label="Copy RGB value {{ item.rgb }}"><code>{{ item.rgb }}</code> <span class="fa-regular fa-copy"></span></button></td>
29
+ <td class="text-nowrap"><button type="button" class="copy-btn focus-ring" data-bs-toggle="tooltip" data-bs-title="Copy HSV value" data-copy-value="{{ item.hsv }}" aria-label="Copy HSV value {{ item.hsv }}"><code>{{ item.hsv }}</code> <span class="fa-regular fa-copy"></span></button></td>
30
+ <td class="text-nowrap">{% if item.text_color == "Black" %}<span class="badge {{ item.text_badge }}" data-bs-toggle="tooltip" data-bs-title="{{ item.text_color }} — near-black brand color, not literal black"><span aria-hidden="true">{{ item.text_color | slice: 0, 1 }}</span><span class="visually-hidden">{{ item.text_color }}</span></span>{% else %}<span class="badge {{ item.text_badge }}" data-bs-toggle="tooltip" data-bs-title="{{ item.text_color }}"><span aria-hidden="true">{{ item.text_color | slice: 0, 1 }}</span><span class="visually-hidden">{{ item.text_color }}</span></span>{% endif %} <button type="button" class="copy-btn focus-ring" data-bs-toggle="tooltip" data-bs-title="Copy text color hex value" data-copy-value="{{ item.text_color_hex }}" aria-label="Copy text color hex value {{ item.text_color_hex }}"><code>{{ item.text_color_hex }}</code> <span class="fa-regular fa-copy"></span></button></td>
31
31
  <td class="text-nowrap"><code>{{ item.contrast_ratio | remove: '`' }}</code> {% include base/link.html.liquid link=_webaim_link target="_blank" class="text-decoration-none" icon="fas fa-external-link-alt" data_bs_toggle="tooltip" data_bs_title="Verify with WebAIM" aria_label="Verify contrast ratio with WebAIM" %}</td>
32
32
  <td class="text-nowrap">{% if item.wcag_2_2_aa %}<span class="d-inline-block" data-bs-toggle="tooltip" title="Exceeds WCAG 2.2 AA standard (4.5:1 minimum)"><i class="fas fa-circle-check text-success"></i></span>{% else %}<span class="d-inline-block" data-bs-toggle="tooltip" title="Underperforms WCAG 2.2 AA standard (4.5:1 minimum)"><i class="fas fa-circle-xmark text-danger"></i></span>{% endif %} <code>{{ item.wcag_2_2_aa_margin | remove: '`' }}</code></td>
33
33
  <td class="text-nowrap">{% if item.wcag_2_2_aaa %}<span class="d-inline-block" data-bs-toggle="tooltip" title="Exceeds WCAG 2.2 AAA standard (7.0:1 minimum)"><i class="fas fa-circle-check text-success"></i></span>{% else %}<span class="d-inline-block" data-bs-toggle="tooltip" title="Underperforms WCAG 2.2 AAA standard (7.0:1 minimum)"><i class="fas fa-circle-xmark text-danger"></i></span>{% endif %} <code>{{ item.wcag_2_2_aaa_margin | remove: '`' }}</code></td>
@@ -17,14 +17,12 @@ others. Requires two distinct loops for valid HTML structure.
17
17
 
18
18
  {% if current_properties %}
19
19
 
20
- {% comment %} 1. Calculate the property count for THIS specific set of data using | size. {% endcomment %}
21
20
  {% assign current_property_count = current_properties | size %}
22
21
 
23
- {% comment %} 2. Determine CSS classes and ID for THIS table {% endcomment %}
24
22
  {% assign table_classes = "table dataTable" %}
25
23
  {% assign table_id = parent_name | slugify | append: '-nested-' | append: current_level %}
26
24
 
27
- {% comment %} 3. Render the current nested table (Loop 1: Rendering) {% endcomment %}
25
+ {% comment %} Loop 1: render this table {% endcomment %}
28
26
  <p class="my-3">The <code>{{ parent_name }}</code> variable is a {{ type_desc }} with the following properties:</p>
29
27
 
30
28
  <table id="{{ table_id }}" class="{{ table_classes }}">
@@ -44,15 +42,39 @@ others. Requires two distinct loops for valid HTML structure.
44
42
  {% for property in current_properties -%}
45
43
  {% assign prop_key = property[0] %}
46
44
  {% assign prop_details = property[1] %}
47
- {% assign default_value = prop_details.default | default: "—" %}
45
+ {% comment %}
46
+ Liquid's `default` filter treats `false` as blank, so a plain
47
+ `prop_details.default | default: "—"` would hide a real `false`
48
+ value. This nil check keeps `false` distinct from no default set.
49
+ {% endcomment %}
50
+ {% if prop_details.default == nil %}
51
+ {% assign default_value = "—" %}
52
+ {% else %}
53
+ {% assign default_value = prop_details.default %}
54
+ {% endif %}
48
55
 
49
56
  <tr>
50
57
  <td><code>{{ prop_key }}</code></td>
51
58
  {% if columns %}
52
59
  {% for col in columns %}<td>{{ prop_details[col.key] | strip_newlines | markdownify | default: "—" }}</td>{% endfor %}
53
60
  {% else %}
54
- <td>{{ prop_details.type | strip_newlines | markdownify | default: "—" }}</td>
55
- <td>{{ default_value | strip_newlines | markdownify | default: "—" }}</td>
61
+ <td>{{ prop_details.type | strip_newlines | markdownify | default: "—" }}</td>
62
+ {% comment %}
63
+ A long SRI hash as plain text stretches this column past the
64
+ others. format: sri-hash renders it as a copyable value
65
+ instead, the same pattern download_filevalue.html.liquid uses
66
+ for checksums.
67
+ {% endcomment %}
68
+ {% if prop_details.format == "sri-hash" %}
69
+ <td>
70
+ <div class="input-group input-group-sm">
71
+ <input type="text" class="form-control font-monospace" readonly value="{{ default_value | escape }}" aria-label="Value for {{ prop_key }}">
72
+ <button type="button" class="input-group-text copy-btn focus-ring" data-copy-value="{{ default_value | escape }}" data-bs-toggle="tooltip" data-bs-title="Copy value" aria-label="Copy value for {{ prop_key }}"><span class="fa-regular fa-copy"></span></button>
73
+ </div>
74
+ </td>
75
+ {% else %}
76
+ <td>{{ default_value | strip_newlines | markdownify | default: "—" }}</td>
77
+ {% endif %}
56
78
  <td>{{ prop_details.description | strip_newlines | markdownify | default: "—" }}</td>
57
79
  {% endif %}
58
80
  </tr>
@@ -60,14 +82,13 @@ others. Requires two distinct loops for valid HTML structure.
60
82
  </tbody>
61
83
  </table>
62
84
 
63
- {% comment %} 4. Run recursion check (Loop 2: Initiation, must run AFTER </table>) {% endcomment %}
85
+ {% comment %} Loop 2: recurse into nested properties, run only after </table> closes {% endcomment %}
64
86
  {% for property in current_properties %}
65
87
  {% assign prop_key = property[0] %}
66
88
  {% assign prop_details = property[1] %}
67
89
 
68
90
  {% assign nested_properties = nil %}
69
91
 
70
- {% comment %} Check if the current property requires further recursion {% endcomment %}
71
92
  {% if prop_details.type == "object" and prop_details.properties %}
72
93
  {% assign nested_properties = prop_details.properties %}
73
94
  {% assign next_type_desc = "dictionary (map)" %}
@@ -2,35 +2,41 @@
2
2
  ================================================================================
3
3
  Template: Copy Value Component
4
4
  Purpose: Copy to clipboard with visual feedback (checkmark + tooltip).
5
- Decodes HTML-entity-escaped values back to plain text before copying.
6
5
  ================================================================================
7
6
 
8
7
  Required Usage (in template):
9
- <span role="button" class="copy-btn" data-copy-value="{{ command | escape }}"
10
- data-bs-toggle="tooltip" data-bs-title="Copy command">
8
+ <button type="button" class="copy-btn focus-ring" data-copy-value="{{ command | escape }}"
9
+ data-bs-toggle="tooltip" data-bs-title="Copy command">
11
10
  <span class="fa-regular fa-copy"></span>
12
- </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`.
13
22
 
14
23
  SECURITY — `data-copy-value` MUST always go through Liquid's `| escape` (or
15
24
  equivalent HTML-attribute encoding) at the point where it is written, with no
16
- exception for values that "look safe". The JS below decodes HTML entities
17
- (&quot;, &amp;, &#39;, …) back to plain text before copying so the clipboard
18
- receives the original string rather than its escaped form — this decoding is
19
- a UX convenience, not sanitization, and provides no protection if the value
20
- was interpolated unescaped in the first place. An unescaped, attacker-
21
- influenced value (a query parameter, CMS field, etc.) placed directly in
22
- data-copy-value breaks out of the HTML attribute at render time, before this
23
- script ever runs.
24
-
25
- Strategic Design Logic:
26
- - HTML entity decoding: reverses &quot;, &amp;, &#39; encoding so the
27
- clipboard receives plain text, independent of how the value was escaped
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:
28
34
  - Visual feedback: checkmark icon + success tooltip
29
35
  - Auto-reset: returns to original state after delay
30
36
  - Clipboard API: native browser copy functionality
31
37
 
32
38
  Required Inputs (from HTML element):
33
- - class="copy-btn": Element to attach handler
39
+ - class="copy-btn": Marks the element as a copy trigger
34
40
  - data-copy-value: Text to copy to clipboard
35
41
 
36
42
  Optional Inputs (from data objects):
@@ -147,10 +147,12 @@ content — see examples/components/download-alternative-images.md.
147
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
148
  {%- assign has_mirrors = true %}
149
149
  {%- endif %}
150
- {%- comment %} arch_name is a raw YAML map key — strip the same
151
- attribute/markup-breaking characters entry_title_id_safe strips in
152
- download.html.liquid before it seeds arch_id_prefix, which in turn
153
- becomes id/data-copy-value scoping in download_filevalue.html.liquid. {% endcomment %}
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 %}
154
156
  {%- assign arch_name_id_safe = arch_name | remove: '"' | remove: "'" | remove: '<' | remove: '>' | remove: '&' | remove: '#' | remove: '?' | remove: '/' %}
155
157
  {%- assign arch_id_prefix = include.release_id | default: "download" | append: "-" | append: arch_name_id_safe | downcase | replace: " ", "-" %}
156
158
  <div class="{{ arch_col_class }}"{% if arch_orphan_style != "" %} style="{{ arch_orphan_style }}"{% endif %}>
@@ -190,7 +192,7 @@ content — see examples/components/download-alternative-images.md.
190
192
  4-column arrangement). Reuses base/link.html.liquid, the theme's
191
193
  canonical link/button component. {% endcomment %}
192
194
  <div class="d-flex flex-wrap gap-2" role="group" aria-label="Primary download actions">
193
- {%- include base/link.html.liquid class="btn btn-primary flex-fill text-start" role="button" name="Download" link=arch_data.download icon="fa-solid fa-download" icon_position="start" -%}
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" -%}
194
196
  </div>
195
197
  {%- endif %}
196
198
 
@@ -222,22 +224,22 @@ content — see examples/components/download-alternative-images.md.
222
224
  the site". {% endcomment %}
223
225
  <div class="d-flex flex-wrap gap-2">
224
226
  {%- if arch_data.mirrors.iso and arch_data.mirrors.iso != "" %}
225
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
226
228
  {%- endif %}
227
229
  {%- if arch_data.mirrors.rpm and arch_data.mirrors.rpm != "" %}
228
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
229
231
  {%- endif %}
230
232
  {%- if arch_data.mirrors.cloud and arch_data.mirrors.cloud != "" %}
231
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
232
234
  {%- endif %}
233
235
  {%- if arch_data.mirrors.containers and arch_data.mirrors.containers != "" %}
234
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
235
237
  {%- endif %}
236
238
  {%- if arch_data.mirrors.vagrant and arch_data.mirrors.vagrant != "" %}
237
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
238
240
  {%- endif %}
239
241
  {%- if arch_data.mirrors.torrent and arch_data.mirrors.torrent != "" %}
240
- {%- include base/link.html.liquid class="btn 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" -%}
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" -%}
241
243
  {%- endif %}
242
244
  </div>
243
245
  {%- endif %}
@@ -45,7 +45,7 @@ kinds doesn't end up with paragraphs breaking up a button row.
45
45
  {%- assign has_description = true %}
46
46
  {%- endif %}
47
47
  {%- if row.title and row.title != "" and has_description == false %}
48
- {%- include base/link.html.liquid class="btn btn-outline-primary text-start" role="button" name=row.title link=row.link target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
48
+ {%- include base/link.html.liquid class="btn focus-ring btn-outline-primary text-start" role="button" name=row.title link=row.link target="_blank" icon="fa-solid fa-up-right-from-square" icon_position="end" -%}
49
49
  {%- endif %}
50
50
  {%- endfor %}
51
51
  {%- endcapture -%}
@@ -64,5 +64,5 @@ both an empty `field` and a with_link-disabled page.
64
64
  <input type="text" id="{{ download_filevalue_id }}" class="form-control font-monospace" readonly value="{{ include.field_value | escape }}" aria-label="{{ download_filevalue_label_cap }} value for {{ include.arch_name | escape }}">
65
65
  {%- comment %} data-copy-value MUST go through `| escape` — see
66
66
  base/copyvalue.html.liquid's own security note. {% endcomment %}
67
- <span class="input-group-text copy-btn" role="button" data-copy-value="{{ include.field_value | escape }}" data-bs-toggle="tooltip" data-bs-title="Copy {{ include.label }} value" aria-label="Copy {{ include.label }} value"><span class="fa-regular fa-copy"></span></span>
67
+ <button type="button" class="input-group-text copy-btn focus-ring" data-copy-value="{{ include.field_value | escape }}" data-bs-toggle="tooltip" data-bs-title="Copy {{ include.label }} value" aria-label="Copy {{ include.label }} value"><span class="fa-regular fa-copy"></span></button>
68
68
  </div>
@@ -25,7 +25,7 @@
25
25
  <div class="row justify-content-between align-items-start gx-5">
26
26
  <div class="col-sm-12 col-lg-4">
27
27
  {%- if footer_brand_image != "" %}
28
- <a class="navbar-brand" href="{{ site.baseurl }}/">
28
+ <a class="navbar-brand focus-ring" href="{{ site.baseurl }}/">
29
29
  <img
30
30
  class="d-inline-block align-text-middle me-2{% if footer_brand_image_class != "" %} {{ footer_brand_image_class }}{% endif %}"
31
31
  src="{{ site.baseurl }}/assets/img/{{ footer_brand_image }}"
@@ -29,7 +29,9 @@ Optional Inputs:
29
29
  - page.with_breakingnews_data: Array of breaking news items
30
30
  - page.with_backtotop: Include back-to-top button (boolean)
31
31
  - page.with_copyvalue: Include copy-to-clipboard behavior (boolean)
32
- - page.theme_color, site.theme_color: Override browser chrome color (default: #A54C93)
32
+ - page.theme_color, site.theme_color: Override browser chrome color. The
33
+ fallback below is a literal, not read from head_schema.yml — keep both in
34
+ sync if the default ever changes.
33
35
  ================================================================================
34
36
  {% endcomment %}
35
37
 
@@ -59,7 +61,7 @@ Optional Inputs:
59
61
  <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin />
60
62
  <link rel="dns-prefetch" href="https://cdnjs.cloudflare.com" />
61
63
  {%- endif %}
62
- {%- if page.with_highlight or page.with_backtotop %}
64
+ {%- if page.with_backtotop %}
63
65
  <link rel="preconnect" href="https://unpkg.com" crossorigin />
64
66
  <link rel="dns-prefetch" href="https://unpkg.com" />
65
67
  {%- endif %}
@@ -76,11 +78,9 @@ Optional Inputs:
76
78
  <link rel="preload" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Source+Code+Pro:ital,wght@0,400;0,700&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'" />
77
79
  <noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&family=Source+Code+Pro:ital,wght@0,400;0,700&display=swap" /></noscript>
78
80
  <link rel="stylesheet" href="{{ site.baseurl }}/assets/css/base/stylesheet.min.css" />
79
- {%- comment %}
80
- Dynamic Breaking News Offset CSS
81
- Adjust scroll-margin-top values based on the number of breaking news items.
82
- Uses CSS custom properties for cleaner, more maintainable dynamic styling.
83
- {%- endcomment %}
81
+ {%- comment %} Sets scroll-margin-top via CSS custom properties, sized to
82
+ the current number of breaking news items, so anchor links land below the
83
+ breaking news bar (and navbar, if present) instead of under it. {%- endcomment %}
84
84
  {%- assign breakingnews = page.with_breakingnews_data | default: site.data.breakingnews | default: site.data.base.breakingnews %}
85
85
  {%- if page.with_breakingnews == true and breakingnews.size > 0 %}
86
86
  {%- assign breakingnews_alert_height = site.data.base.breakingnews_schema.properties.breakingnews_alert_height.default %}
@@ -123,7 +123,6 @@ Uses CSS custom properties for cleaner, more maintainable dynamic styling.
123
123
 
124
124
  {%- comment %} CSS dependencies {%- endcomment %}
125
125
  {%- include base/fontawesome.html.liquid type="head" %}
126
- {%- include base/highlight.html.liquid type="head" %}
127
126
  {%- include base/datatable.html.liquid type="head" %}
128
127
  {%- include base/calendar.html.liquid type="head" %}
129
128
 
@@ -133,7 +132,7 @@ Uses CSS custom properties for cleaner, more maintainable dynamic styling.
133
132
 
134
133
  {%- comment %} JavaScript optional library extensions {%- endcomment %}
135
134
  {%- include base/fontawesome.html.liquid type="script" %}
136
- {%- include base/highlight.html.liquid type="script" %}
135
+ {%- include base/highlight.html.liquid %}
137
136
  {%- include base/datatable.html.liquid type="script" %}
138
137
  {%- include base/calendar.html.liquid type="script" %}
139
138
 
@@ -1,23 +1,24 @@
1
1
  {% comment %}
2
2
  ================================================================================
3
3
  Template: Highlight.js Code Syntax Highlighting Component
4
- Purpose: Loads Highlight.js library CSS and JavaScript from CDN with
5
- copy-to-clipboard functionality for code blocks.
6
- ================================================================================
4
+ Purpose: Loads Highlight.js from a CDN to color code blocks in the browser.
5
+
6
+ This template only loads the JS library. It never loads a CSS theme file —
7
+ the colors come from this site's own theme, _highlight-theme.scss, already
8
+ part of the compiled stylesheet. It also doesn't add a copy button; that's
9
+ base/highlight.js's job, which wraps every code block (plain Markdown or
10
+ one built with base/preview.html.liquid) in the same toolbar, reusing the
11
+ site-wide `.copy-btn` handler (src/JS/copyvalue.js).
7
12
 
8
- Design Logic:
9
- - CDN-based library: Highlight.js from cdnjs
10
- - Copy plugin: additional plugin for code clipboard functionality
11
- - Format switching: separate head (CSS) and script (JS) includes
12
- - Automatic initialization: on document load
13
- - Configurable theme: different color schemes available
14
- - 190+ languages supported
13
+ Cdnjs's default build covers about 40 common languages. A block whose
14
+ language isn't in that build renders as plain, uncolored text instead of
15
+ erroring. Load any other language by setting
16
+ with_highlight_data.languages.<id>: true in a page's front matter — see
17
+ src/YAML/highlight_schema.yml for the full list of language ids.
15
18
 
16
19
  Optional Inputs:
17
- - type: "head" for CSS, "script" for JavaScript
18
- - theme: Syntax highlighting theme (default: "default")
19
- - version: Highlight.js version (e.g., "11.9.0")
20
- - data: Highlight.js configuration object
20
+ - data: Highlight.js configuration object — version, script_integrity,
21
+ and languages (see above)
21
22
 
22
23
  Data Source Hierarchy (priority):
23
24
  1. include.data
@@ -41,44 +42,33 @@ Data Source Hierarchy (priority):
41
42
 
42
43
  {%- if with_highlight %}
43
44
 
44
- {%- assign highlight_theme = with_highlight_data.theme | default: with_highlight_schema.theme.default %}
45
- {%- assign highlight_version = with_highlight_data.version | default: with_highlight_schema.version.default %}
46
- {%- assign highlight_css_sri = with_highlight_data.css_integrity | default: with_highlight_schema.css_integrity.default %}
47
- {%- assign highlight_script_sri = with_highlight_data.script_integrity | default: with_highlight_schema.script_integrity.default %}
48
- {%- assign copy_version = with_highlight_data.copy_version | default: with_highlight_schema.copy_version.default %}
49
- {%- assign copy_script_sri = with_highlight_data.copy_script_integrity | default: with_highlight_schema.copy_script_integrity.default %}
50
- {%- assign copy_css_sri = with_highlight_data.copy_css_integrity | default: with_highlight_schema.copy_css_integrity.default %}
51
- {%- assign copy_icon_svg = with_highlight_data.copy_button_icons.copy.svg | default: with_highlight_schema.copy_button_icons.properties.copy.properties.svg.default %}
52
- {%- assign success_icon_svg = with_highlight_data.copy_button_icons.success.svg | default: with_highlight_schema.copy_button_icons.properties.success.properties.svg.default %}
45
+ {%- assign highlight_version = with_highlight_data.version | default: with_highlight_schema.version.default %}
46
+ {%- assign highlight_script_sri = with_highlight_data.script_integrity | default: with_highlight_schema.script_integrity.default %}
53
47
 
54
- {%- capture html_highlight_head %}
55
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{{ highlight_version }}/styles/{{ highlight_theme | replace_first: 'base16-', 'base16/' | default: 'default' }}.min.css" integrity="{{ highlight_css_sri }}" crossorigin="anonymous" referrerpolicy="no-referrer" />
56
- <link rel="stylesheet" href="https://unpkg.com/highlightjs-copy@{{ copy_version }}/dist/highlightjs-copy.min.css" integrity="{{ copy_css_sri }}" crossorigin="anonymous" referrerpolicy="no-referrer" />
57
- <!-- Highlight.js Copy Button Icon Customization (from schema) -->
58
- <style>
59
- .hljs-copy-button:not([data-copied="true"])::before {
60
- mask: url('data:image/svg+xml;utf-8,{{ copy_icon_svg }}');
61
- }
62
- .hljs-copy-button[data-copied='true']::after {
63
- background-image: url('data:image/svg+xml;utf-8,{{ success_icon_svg }}');
64
- }
65
- </style>
66
- {%- endcapture %}
48
+ <!-- Highlight.js Library -->
49
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{{ highlight_version }}/highlight.min.js" integrity="{{ highlight_script_sri }}" crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
67
50
 
68
- {%- capture html_highlight_script %}
69
- <!-- Highlight.js Library and Copy Plugin -->
70
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{{ highlight_version }}/highlight.min.js" integrity="{{ highlight_script_sri }}" crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
71
- <script src="https://unpkg.com/highlightjs-copy@{{ copy_version }}/dist/highlightjs-copy.min.js" integrity="{{ copy_script_sri }}" crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
72
- <!-- Initialize Highlight.js -->
73
- <script src="{{ site.baseurl }}/assets/js/base/highlight.min.js" defer></script>
74
- {%- endcapture %}
51
+ {%- comment %}
52
+ Optional per-language modules. Each script tag must come after the core
53
+ library above, so its `hljs.registerLanguage` call has a global `hljs` to
54
+ register against. A language's toggle lives in with_highlight_data.languages,
55
+ set in a page's front matter. Its SRI hash comes from the schema's own
56
+ x_integrity map — a page only names the languages it wants, never their
57
+ hashes.
58
+ {%- endcomment %}
59
+ {%- assign highlight_languages = with_highlight_data.languages %}
60
+ {%- assign language_integrity = with_highlight_schema.languages.properties.x_integrity.properties %}
61
+ {%- if highlight_languages %}
62
+ {%- for language in highlight_languages %}
63
+ {%- assign language_id = language[0] %}
64
+ {%- assign language_enabled = language[1] %}
65
+ {%- if language_enabled %}
66
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{{ highlight_version }}/languages/{{ language_id }}.min.js" integrity="{{ language_integrity[language_id].default }}" crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
67
+ {%- endif %}
68
+ {%- endfor %}
69
+ {%- endif %}
75
70
 
76
- {%- assign type = include.type %}
77
- {%- case type %}
78
- {%- when "head" %}
79
- {{ html_highlight_head }}
80
- {%- when "script" %}
81
- {{ html_highlight_script }}
82
- {%- endcase %}
71
+ <!-- Initialize Highlight.js and wrap code blocks in the shared preview toolbar -->
72
+ <script src="{{ site.baseurl }}/assets/js/base/highlight.min.js" defer></script>
83
73
 
84
74
  {%- endif %}
@@ -58,7 +58,7 @@ Data Source Hierarchy (priority):
58
58
 
59
59
  {%- if navbar_brand_image != "" %}
60
60
  {%- assign navbar_brand_link = "/" | locale_url: page.locale %}
61
- <a class="navbar-brand" href="{{ site.baseurl }}{{ navbar_brand_link }}">
61
+ <a class="navbar-brand focus-ring" href="{{ site.baseurl }}{{ navbar_brand_link }}">
62
62
 
63
63
  <img
64
64
  class="d-inline-block align-text-middle me-2{% if navbar_brand_image_class != "" %} {{ navbar_brand_image_class }}{% endif %}"
@@ -85,7 +85,7 @@ Data Source Hierarchy (priority):
85
85
  {%- if navitem.menu.size == 0 %}
86
86
  <li class="nav-item">
87
87
  {%- include base/link.html.liquid
88
- class = "nav-link"
88
+ class = "nav-link focus-ring"
89
89
  name = navitem.name
90
90
  link = navitem_link
91
91
  role = "presentation"
@@ -94,7 +94,7 @@ Data Source Hierarchy (priority):
94
94
  </li>
95
95
  {%- else %}
96
96
  <li class="nav-item dropdown">
97
- <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown-{{ forloop.index }}" role="button" data-bs-toggle="dropdown" aria-expanded="false">
97
+ <a class="nav-link dropdown-toggle focus-ring" href="#" id="navbarDropdown-{{ forloop.index }}" role="button" data-bs-toggle="dropdown" aria-expanded="false">
98
98
  <i class="{{ navitem.icon }} me-1" aria-hidden="true"></i>{{ navitem.name }}
99
99
  </a>
100
100
  <ul class="dropdown-menu dropdown-menu-dark shadow">
@@ -102,7 +102,7 @@ Data Source Hierarchy (priority):
102
102
  {%- assign dropdownitem_link = dropdownitem.link | locale_url: page.locale %}
103
103
  <li>
104
104
  {%- include base/link.html.liquid
105
- class = "dropdown-item"
105
+ class = "dropdown-item focus-ring"
106
106
  name = dropdownitem.name
107
107
  link = dropdownitem_link
108
108
  role = "presentation"