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

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/_data/base/announcement_schema.yml +0 -1
  3. data/_data/base/backtotop_schema.yml +7 -4
  4. data/_data/base/calendar_schema.yml +6 -4
  5. data/_data/base/card_schema.yml +2 -2
  6. data/_data/base/copyright_schema.yml +1 -1
  7. data/_data/base/datatable_schema.yml +8 -6
  8. data/_data/base/fontawesome_schema.yml +10 -2
  9. data/_data/base/heading_anchor.yml +1 -1
  10. data/_data/base/heading_anchor_schema.yml +1 -1
  11. data/_data/base/highlight_schema.yml +1433 -60
  12. data/_data/base/link_schema.yml +1 -1
  13. data/_data/base/preview_schema.yml +88 -0
  14. data/_data/base/project.yml +1 -1
  15. data/_includes/base/color-table.html.liquid +4 -4
  16. data/_includes/base/configuration_variables-default-cell.html.liquid +83 -0
  17. data/_includes/base/configuration_variables-nested.html.liquid +4 -11
  18. data/_includes/base/configuration_variables.html.liquid +3 -11
  19. data/_includes/base/copyvalue.html.liquid +23 -17
  20. data/_includes/base/download_architecture.html.liquid +13 -11
  21. data/_includes/base/download_documentation.html.liquid +1 -1
  22. data/_includes/base/download_filevalue.html.liquid +1 -1
  23. data/_includes/base/footer.html.liquid +1 -1
  24. data/_includes/base/head.html.liquid +12 -10
  25. data/_includes/base/highlight.html.liquid +40 -50
  26. data/_includes/base/navbar.html.liquid +4 -4
  27. data/_includes/base/preview.html.liquid +119 -0
  28. data/_sass/base/_calendar.scss +2 -1
  29. data/_sass/base/_customization.scss +61 -52
  30. data/_sass/base/_highlight-theme.scss +102 -0
  31. data/_sass/base/_preview.scss +74 -0
  32. data/_sass/base/_theme-colors.scss +8 -5
  33. data/_sass/base/_variables.scss +55 -73
  34. data/assets/css/base/stylesheet.min.scss +3 -1
  35. data/assets/js/base/backtotop.js +2 -2
  36. data/assets/js/base/copyvalue.js +3 -0
  37. data/assets/js/base/datatable.js +27 -9
  38. data/assets/js/base/highlight.js +86 -12
  39. metadata +6 -1
@@ -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
@@ -16,7 +16,7 @@ description: Community Enterprise Operating System
16
16
  # Project Actions
17
17
  # -------------------------------------------------------------------------------
18
18
  # Use short verbs to tell users what they can do inside the project (e.g., Know
19
- # more, Contribute, etc.). Project actions
19
+ # more, Contribute, etc.).
20
20
  actions:
21
21
  - name: Know more
22
22
  icon: fa-solid fa-arrow-right
@@ -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>
@@ -0,0 +1,83 @@
1
+ {% comment %}
2
+ ================================================================================
3
+ Template: Configuration Variables — Default Cell
4
+ Purpose: Renders one Default-column cell's contents. Included from both
5
+ configuration_variables.html.liquid (top-level table) and
6
+ configuration_variables-nested.html.liquid (recursive nested tables) so every
7
+ schema-driven Name/Type/Default/Description table presents a long default
8
+ value the same way, with one shared rule instead of two copies that could
9
+ drift apart.
10
+
11
+ Behavior:
12
+ - Liquid's `default` filter treats `false` as blank, so a plain
13
+ `include.default | default: "—"` would hide a real `false` value. This nil
14
+ check keeps `false` distinct from "no default set".
15
+ - An explicit `default: ""` (e.g. download_schema.yml's `iso`/`rpm`/`link`
16
+ URLs, whose description already says "Empty hides the ... button") is a
17
+ real, intentional default — not the same as "no default declared". Left to
18
+ fall through to `<code>{{ default_value }}</code>`, it renders as a visibly
19
+ empty inline code element: sighted users read it as a rendering glitch, and
20
+ a screen reader announces an empty `<code>` with no indication a value is
21
+ even there, which fails WCAG 1.3.1 (the "default is an empty string" fact
22
+ is conveyed by absence alone, not programmatically). It gets its own
23
+ `<code>""</code>` rendering plus a `.visually-hidden` "(empty string)" note,
24
+ so the cell reads as a value distinct from both `—` (nil) and a populated
25
+ default.
26
+ - Every real default value renders as `<code>`, the same as the Name and
27
+ Type columns — a config default is a literal value, not prose, so it's
28
+ shown raw (escaped, not markdownified) rather than interpreted as
29
+ Markdown. The nil placeholder `—` is left plain, since it isn't a value.
30
+ - A default value with a long unbroken run of characters (an SRI hash, a
31
+ CDN URL, ...) forces its column wide under normal table auto-layout, at
32
+ the Description column's expense, regardless of how many columns the
33
+ table has. Truncating it into a fixed-width single-line box instead would
34
+ need scrolling or selecting to read the rest, which runs against WCAG
35
+ 1.4.10 Reflow. Past `long_value_threshold` characters with no space to
36
+ wrap at, the *longest unbroken token* (not the value's total length —
37
+ see below) gets the extra `.long-value` class (breaks at any character
38
+ via `overflow-wrap: anywhere`, see _customization.scss): the full value
39
+ stays visible with no interaction required, and it takes only the width
40
+ it actually needs.
41
+ - Checked by longest unbroken token, not total length: a value can be long
42
+ overall but still wrap fine on its own spaces (a class list, a sentence);
43
+ what actually forces a column wide is one run with nowhere to break.
44
+ - Only `type: string` values are checked for length — a boolean or number
45
+ is never "long", and running `split`/`size` on a non-string isn't
46
+ reliable.
47
+
48
+ Required Inputs:
49
+ - default: The raw prop_details.default value (may be nil, false, a string, etc.)
50
+ - type: The property's schema `type`, e.g. "string", "boolean".
51
+ ================================================================================
52
+ {% endcomment %}
53
+
54
+ {% if include.default == nil %}
55
+ {% assign default_value = "—" %}
56
+ {% elsif include.default == "" %}
57
+ {% assign default_value = "" %}
58
+ {% assign is_empty_string = true %}
59
+ {% else %}
60
+ {% assign default_value = include.default %}
61
+ {% endif %}
62
+
63
+ {% assign long_value_threshold = 24 %}
64
+ {% assign max_token_length = 0 %}
65
+ {% if include.type == "string" and default_value != "—" %}
66
+ {% assign default_tokens = default_value | split: " " %}
67
+ {% for token in default_tokens %}
68
+ {% assign token_length = token | size %}
69
+ {% if token_length > max_token_length %}
70
+ {% assign max_token_length = token_length %}
71
+ {% endif %}
72
+ {% endfor %}
73
+ {% endif %}
74
+
75
+ {% if default_value == "—" %}
76
+ —
77
+ {% elsif is_empty_string %}
78
+ <code>""</code><span class="visually-hidden"> (empty string)</span>
79
+ {% elsif max_token_length > long_value_threshold %}
80
+ <code class="long-value">{{ default_value | escape }}</code>
81
+ {% else %}
82
+ <code>{{ default_value | escape }}</code>
83
+ {% endif %}
@@ -17,14 +17,10 @@ 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
- {% assign current_property_count = current_properties | size %}
22
-
23
- {% comment %} 2. Determine CSS classes and ID for THIS table {% endcomment %}
24
20
  {% assign table_classes = "table dataTable" %}
25
21
  {% assign table_id = parent_name | slugify | append: '-nested-' | append: current_level %}
26
22
 
27
- {% comment %} 3. Render the current nested table (Loop 1: Rendering) {% endcomment %}
23
+ {% comment %} Loop 1: render this table {% endcomment %}
28
24
  <p class="my-3">The <code>{{ parent_name }}</code> variable is a {{ type_desc }} with the following properties:</p>
29
25
 
30
26
  <table id="{{ table_id }}" class="{{ table_classes }}">
@@ -44,15 +40,14 @@ others. Requires two distinct loops for valid HTML structure.
44
40
  {% for property in current_properties -%}
45
41
  {% assign prop_key = property[0] %}
46
42
  {% assign prop_details = property[1] %}
47
- {% assign default_value = prop_details.default | default: "—" %}
48
43
 
49
44
  <tr>
50
45
  <td><code>{{ prop_key }}</code></td>
51
46
  {% if columns %}
52
47
  {% for col in columns %}<td>{{ prop_details[col.key] | strip_newlines | markdownify | default: "—" }}</td>{% endfor %}
53
48
  {% else %}
54
- <td>{{ prop_details.type | strip_newlines | markdownify | default: "—" }}</td>
55
- <td>{{ default_value | strip_newlines | markdownify | default: "—" }}</td>
49
+ <td>{% if prop_details.type %}<code>{{ prop_details.type }}</code>{% else %}—{% endif %}</td>
50
+ <td>{% include base/configuration_variables-default-cell.html.liquid default=prop_details.default type=prop_details.type %}</td>
56
51
  <td>{{ prop_details.description | strip_newlines | markdownify | default: "—" }}</td>
57
52
  {% endif %}
58
53
  </tr>
@@ -60,14 +55,13 @@ others. Requires two distinct loops for valid HTML structure.
60
55
  </tbody>
61
56
  </table>
62
57
 
63
- {% comment %} 4. Run recursion check (Loop 2: Initiation, must run AFTER </table>) {% endcomment %}
58
+ {% comment %} Loop 2: recurse into nested properties, run only after </table> closes {% endcomment %}
64
59
  {% for property in current_properties %}
65
60
  {% assign prop_key = property[0] %}
66
61
  {% assign prop_details = property[1] %}
67
62
 
68
63
  {% assign nested_properties = nil %}
69
64
 
70
- {% comment %} Check if the current property requires further recursion {% endcomment %}
71
65
  {% if prop_details.type == "object" and prop_details.properties %}
72
66
  {% assign nested_properties = prop_details.properties %}
73
67
  {% assign next_type_desc = "dictionary (map)" %}
@@ -77,7 +71,6 @@ others. Requires two distinct loops for valid HTML structure.
77
71
  {% endif %}
78
72
 
79
73
  {% if nested_properties %}
80
- {% comment %} Found deeper nesting: Call the next level {% endcomment %}
81
74
  {% assign next_parent_name = include.parent_name | append: "." | append: prop_key %}
82
75
  {% assign next_level_val = current_level | plus: 1 %}
83
76
 
@@ -17,15 +17,10 @@ of separate, nested DataTables for complex properties.
17
17
 
18
18
  {% if property_schema.properties %}
19
19
 
20
- {% comment %} 1. Calculate the property count for the top table using | size. {% endcomment %}
21
- {% assign property_count = property_schema.properties | size %}
22
-
23
- {% comment %} 2. Determine the CSS classes for the top-level table. {% endcomment %}
24
20
  {% assign table_classes = "table dataTable" %}
25
21
 
26
22
  <p>The <code>{{ page.component.name }}</code> {{ page.component.type }} understands the following configuration variables:</p>
27
23
 
28
- {% comment %} 3. Render the top-level table completely {% endcomment %}
29
24
  <table class="{{ table_classes }}">
30
25
  <thead>
31
26
  <tr>
@@ -48,8 +43,8 @@ of separate, nested DataTables for complex properties.
48
43
  {% if columns %}
49
44
  {% for col in columns %}<td>{{ prop_details[col.key] | strip_newlines | markdownify | default: "—" }}</td>{% endfor %}
50
45
  {% else %}
51
- <td>{{ prop_details.type | strip_newlines | markdownify | default: "—" }}</td>
52
- <td>{{ prop_details.default | strip_newlines | markdownify | default: "—" }}</td>
46
+ <td>{% if prop_details.type %}<code>{{ prop_details.type }}</code>{% else %}—{% endif %}</td>
47
+ <td>{% include base/configuration_variables-default-cell.html.liquid default=prop_details.default type=prop_details.type %}</td>
53
48
  <td>{{ prop_details.description | strip_newlines | markdownify | default: "—" }}</td>
54
49
  {% endif %}
55
50
  </tr>
@@ -57,7 +52,7 @@ of separate, nested DataTables for complex properties.
57
52
  </tbody>
58
53
  </table>
59
54
 
60
- {% comment %} 4. Initiate recursive nested table generation (separate loop) {% endcomment %}
55
+ {% comment %} Separate loop, run only after </table> closes above: recurses into any property that nests its own properties. {% endcomment %}
61
56
  {% for property in property_schema.properties -%}
62
57
  {% assign prop_key = property[0] %}
63
58
  {% assign prop_details = property[1] %}
@@ -65,12 +60,10 @@ of separate, nested DataTables for complex properties.
65
60
  {% assign nested_properties = nil %}
66
61
  {% assign type_desc = "" %}
67
62
 
68
- {% comment %} Check for nested structures in the top-level properties {% endcomment %}
69
63
  {% if prop_details.type == "object" and prop_details.properties %}
70
64
  {% assign nested_properties = prop_details.properties %}
71
65
  {% assign type_desc = "dictionary (map)" %}
72
66
  {% elsif prop_details.type == "array" and prop_details.items.properties %}
73
- {% comment %} Handle array of objects {% endcomment %}
74
67
  {% assign nested_properties = prop_details.items.properties %}
75
68
  {% assign type_desc = "list of dictionaries" %}
76
69
  {% endif %}
@@ -78,7 +71,6 @@ of separate, nested DataTables for complex properties.
78
71
  {% if nested_properties %}
79
72
  {% assign initial_level = 1 %}
80
73
 
81
- {% comment %} Call the recursive include for the first level of nested properties. {% endcomment %}
82
74
  {% include base/configuration_variables-nested.html.liquid
83
75
  properties=nested_properties
84
76
  parent_name=prop_key
@@ -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 }}"
@@ -12,7 +12,10 @@ Design Logic:
12
12
  - Google Fonts: Montserrat (5 weights) and Source Code Pro (2 weights) via async stylesheet
13
13
  - Breakingnews scroll offset: dynamic CSS calculation
14
14
  - Conditional includes: OGP, Highlight.js, DataTables, FontAwesome (SVG+JS), Event Calendar
15
- - CSS prioritization: custom stylesheet last for override
15
+ - CSS load order: theme stylesheet loads first, then per-component vendor
16
+ CSS (FontAwesome, DataTables, Event Calendar). Components whose vendor CSS
17
+ needs correcting win with higher-specificity selectors in their own SCSS
18
+ partial instead of load order — see _datatable.scss and _calendar.scss.
16
19
  - Script loading: Bootstrap base first (defer), then optional extensions, then UI behaviors
17
20
  - Bootstrap and FontAwesome preloaded (high-priority fetch); CDN origins preconnected + dns-prefetched per active component
18
21
 
@@ -29,7 +32,9 @@ Optional Inputs:
29
32
  - page.with_breakingnews_data: Array of breaking news items
30
33
  - page.with_backtotop: Include back-to-top button (boolean)
31
34
  - page.with_copyvalue: Include copy-to-clipboard behavior (boolean)
32
- - page.theme_color, site.theme_color: Override browser chrome color (default: #A54C93)
35
+ - page.theme_color, site.theme_color: Override browser chrome color. The
36
+ fallback below is a literal, not read from head_schema.yml — keep both in
37
+ sync if the default ever changes.
33
38
  ================================================================================
34
39
  {% endcomment %}
35
40
 
@@ -59,7 +64,7 @@ Optional Inputs:
59
64
  <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin />
60
65
  <link rel="dns-prefetch" href="https://cdnjs.cloudflare.com" />
61
66
  {%- endif %}
62
- {%- if page.with_highlight or page.with_backtotop %}
67
+ {%- if page.with_backtotop %}
63
68
  <link rel="preconnect" href="https://unpkg.com" crossorigin />
64
69
  <link rel="dns-prefetch" href="https://unpkg.com" />
65
70
  {%- endif %}
@@ -76,11 +81,9 @@ Optional Inputs:
76
81
  <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
82
  <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
83
  <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 %}
84
+ {%- comment %} Sets scroll-margin-top via CSS custom properties, sized to
85
+ the current number of breaking news items, so anchor links land below the
86
+ breaking news bar (and navbar, if present) instead of under it. {%- endcomment %}
84
87
  {%- assign breakingnews = page.with_breakingnews_data | default: site.data.breakingnews | default: site.data.base.breakingnews %}
85
88
  {%- if page.with_breakingnews == true and breakingnews.size > 0 %}
86
89
  {%- assign breakingnews_alert_height = site.data.base.breakingnews_schema.properties.breakingnews_alert_height.default %}
@@ -123,7 +126,6 @@ Uses CSS custom properties for cleaner, more maintainable dynamic styling.
123
126
 
124
127
  {%- comment %} CSS dependencies {%- endcomment %}
125
128
  {%- include base/fontawesome.html.liquid type="head" %}
126
- {%- include base/highlight.html.liquid type="head" %}
127
129
  {%- include base/datatable.html.liquid type="head" %}
128
130
  {%- include base/calendar.html.liquid type="head" %}
129
131
 
@@ -133,7 +135,7 @@ Uses CSS custom properties for cleaner, more maintainable dynamic styling.
133
135
 
134
136
  {%- comment %} JavaScript optional library extensions {%- endcomment %}
135
137
  {%- include base/fontawesome.html.liquid type="script" %}
136
- {%- include base/highlight.html.liquid type="script" %}
138
+ {%- include base/highlight.html.liquid %}
137
139
  {%- include base/datatable.html.liquid type="script" %}
138
140
  {%- include base/calendar.html.liquid type="script" %}
139
141