Skip to content

Commit 1886816

Browse files
Modernize docgen: Bootstrap 5.3.8, drop jQuery/Font Awesome, add centralized mega menu (#863)
## Summary Modernizes the documentation generation pipeline across all three template systems (JSON/Jinja2, XML/XSLT, Proto/Go). ### Dependency upgrades - **Bootstrap**: 3.4.1 (XML) and 4.6.2 (JSON, Proto) → 5.3.8 - **jQuery**: Removed entirely — all JS rewritten to vanilla DOM APIs - **Font Awesome 4.7**: Replaced with Bootstrap Icons 1.11.3 - **markdown-it**: Removed (unused dependency in JSON templates) ### Protobuf documentation overhaul - Restructured to match JSON docs in look, feel, and navigation behavior - Updated CSS for consistent styling with JSON and XML docs ### Centralized mega menu and header injection - `docgen/static/releases.json` — single source of truth for all version metadata - `docgen/static/generate-menu.py` — builds mega menu HTML from JSON data - `docgen/static/header.html` — shared header with navbar, mega menu, backdrop overlay, and interaction logic - `docgen/static/inject-header.sh` — post-processing pipeline that assembles and injects the header into generated docs - Replaces per-template hardcoded version dropdowns with a shared, data-driven mega menu - Supports featured releases, optional formats, ECMA standard references, and skip flags - Format pills are native `<a>` tags (SEO-friendly, visible in browser status bar) - Responsive breakpoints - Backdrop overlay, Escape key, and click-outside dismiss the menu ### Preview <img width="1391" height="830" alt="Screenshot 2026-03-09 at 8 20 08 PM" src="https://github.com/user-attachments/assets/37caba7b-e926-4266-bbd7-e1bc59d8b739" /> <img width="1392" height="829" alt="Screenshot 2026-03-09 at 8 20 19 PM" src="https://github.com/user-attachments/assets/a56fd2d5-4db6-4d5b-aee6-2c5459ed68dd" /> <img width="1391" height="829" alt="Screenshot 2026-03-09 at 8 20 31 PM" src="https://github.com/user-attachments/assets/ef28d7af-16e7-44b5-bba6-52e009a88413" /> <img width="1391" height="830" alt="Screenshot 2026-03-09 at 8 21 06 PM" src="https://github.com/user-attachments/assets/524c8d8c-16c2-4f79-8d61-ddf020dbee6d" /> <img width="1392" height="829" alt="Screenshot 2026-03-09 at 8 21 25 PM" src="https://github.com/user-attachments/assets/9182688d-abe1-410d-9e0e-9e485cc24d70" />
2 parents e152cc1 + ca40799 commit 1886816

20 files changed

+1745
-623
lines changed

docgen/json/gen.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ SCHEMA_PATH="$(realpath "$THIS_PATH/../../schema")"
2525
DOCS_PATH="$THIS_PATH/docs"
2626
TEMPLATES_PATH="$THIS_PATH/templates"
2727

28+
# Centralized header injection
29+
source "$THIS_PATH/../static/inject-header.sh"
30+
2831

2932
# --
3033

@@ -71,6 +74,8 @@ generate () {
7174
sed -i -e "s/\${quotedTitle}/\"$title\"/g" "$OUT_FILE"
7275
sed -i -e "s/\${title}/$title/g" "$OUT_FILE"
7376
sed -i -e "s/\${version}/$version/g" "$OUT_FILE"
77+
78+
inject_header "$OUT_FILE" "$version" "json"
7479
}
7580

7681

docgen/json/templates/cyclonedx/base.html

Lines changed: 10 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,54 +18,15 @@
1818
<meta property="og:type" content="website" />
1919
<meta property="og:image" content="https://cyclonedx.org/images/CycloneDX-Social-Card.png" />
2020
<link href="/favicon.ico" rel="shortcut icon" type="image/ico" />
21-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css" integrity="sha512-rt/SrQ4UNIaGfDyEXZtNcyWvQeOq0QLygHluFQcSjaGB04IxWhal71tKuzP6K8eYXYB6vJV4pHkXcmFGGQ1/0w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
22-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha512-SfTiTlX6kk+qitfevl/7LibUOeJWlt9rbyDn92a1DqWOw9vWG2MFoays0sgObmWazO5BQPiFucnnEAjpAB+/Sw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
21+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" crossorigin="anonymous" />
22+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
2323
<link rel="stylesheet" type="text/css" href="schema_doc.css">
24-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.slim.js" integrity="sha512-docBEeq28CCaXCXN7cINkyQs0pRszdQsVBFWUd+pLNlEk3LDlSDDtN7i1H+nTB8tshJPQHS0yu0GW9YGFd/CRg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
25-
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/js/bootstrap.min.js" integrity="sha512-7rusk8kGPFynZWu26OKbTeI+QPoYchtxsmPeBqkHIEXJxeun4yJ4ISYe7C6sz9wdxeE1Gk3VxsIWgCZTc+vX3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
26-
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.2/markdown-it.min.js" integrity="sha512-ohlWmsCxOu0bph1om5eDL0jm/83eH09fvqLDhiEdiqfDeJbEvz4FSbeY0gLJSVJwQAp0laRhTXbUQG+ZUuifUQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
2724
<script src="schema_doc.min.js"></script>
25+
<!-- mega menu CSS is injected by inject-header.sh -->
2826
</head>
29-
<body class="blue" data-spy="scroll" data-target=".js-scrollspy" onload="anchorOnLoad();" id="root">
27+
<body class="blue" data-bs-spy="scroll" data-bs-target=".js-scrollspy" onload="anchorOnLoad();" id="root">
3028

31-
<nav class="navbar fixed-top navbar-expand-sm navbar-inverse">
32-
<a href="/" class="navbar-brand site-header__logo"><img src="https://cyclonedx.org/images/logo-all-white.svg" height="48" width="276"></img></a>
33-
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarScroll" aria-controls="navbarScroll" aria-expanded="false" aria-label="Toggle navigation">
34-
<span class="navbar-toggler-icon"></span>
35-
</button>
36-
<div class="collapse navbar-collapse" id="navbarScroll">
37-
<ul class="navbar-nav mr-auto my-2 my-lg-0 navbar-nav-scroll" style="max-height: 100px;">
38-
<li class="nav-item dropdown">
39-
<a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button" data-toggle="dropdown" aria-expanded="false">
40-
v${version} (JSON)
41-
</a>
42-
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
43-
<li><a class="dropdown-item" href="/docs/1.7/json/">v1.7 (JSON)</a></li>
44-
<li><a class="dropdown-item" href="/docs/1.6/json/">v1.6 (JSON)</a></li>
45-
<li><a class="dropdown-item" href="/docs/1.5/json/">v1.5 (JSON)</a></li>
46-
<li><a class="dropdown-item" href="/docs/1.4/json/">v1.4 (JSON)</a></li>
47-
<li><a class="dropdown-item" href="/docs/1.3/json/">v1.3 (JSON)</a></li>
48-
<li><a class="dropdown-item" href="/docs/1.2/json/">v1.2 (JSON)</a></li>
49-
<li><hr class="dropdown-divider"/></li>
50-
<li><a class="dropdown-item" href="/docs/1.7/xml/">v1.7 (XML)</a></li>
51-
<li><a class="dropdown-item" href="/docs/1.6/xml/">v1.6 (XML)</a></li>
52-
<li><a class="dropdown-item" href="/docs/1.5/xml/">v1.5 (XML)</a></li>
53-
<li><a class="dropdown-item" href="/docs/1.4/xml/">v1.4 (XML)</a></li>
54-
<li><a class="dropdown-item" href="/docs/1.3/xml/">v1.3 (XML)</a></li>
55-
<li><a class="dropdown-item" href="/docs/1.2/xml/">v1.2 (XML)</a></li>
56-
<li><a class="dropdown-item" href="/docs/1.1/xml/">v1.1 (XML)</a></li>
57-
<li><a class="dropdown-item" href="/docs/1.0/xml/">v1.0 (XML)</a></li>
58-
<li><hr class="dropdown-divider"/></li>
59-
<li><a class="dropdown-item" href="/docs/1.7/proto/">v1.7 (Protobuf)</a></li>
60-
<li><a class="dropdown-item" href="/docs/1.6/proto/">v1.6 (Protobuf)</a></li>
61-
<li><a class="dropdown-item" href="/docs/1.5/proto/">v1.5 (Protobuf)</a></li>
62-
<li><a class="dropdown-item" href="/docs/1.4/proto/">v1.4 (Protobuf)</a></li>
63-
<li><a class="dropdown-item" href="/docs/1.3/proto/">v1.3 (Protobuf)</a></li>
64-
</ul>
65-
</li>
66-
</ul>
67-
</div>
68-
</nav>
29+
<!-- MEGA_MENU_HEADER -->
6930

7031

7132
<div class="container-fluid" style="margin-top:110px; margin-bottom:3rem">
@@ -75,9 +36,9 @@ <h1>${title}</h1>
7536
<h1>{{ title }}</h1>
7637
{%- endif -%}
7738
{%- if config.expand_buttons -%}
78-
<div class="text-right">
79-
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target=".collapse:not(.show)" aria-expanded="false">Expand all</button>
80-
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target=".collapse.show" aria-expanded="false">Collapse all</button>
39+
<div class="text-end">
40+
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target=".collapse:not(.show)" aria-expanded="false">Expand all</button>
41+
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target=".collapse.show" aria-expanded="false">Collapse all</button>
8142
</div>
8243
{%- endif -%}
8344

@@ -95,6 +56,7 @@ <h1>{{ title }}</h1>
9556
</footer>
9657
-->
9758

98-
<img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=ce3b481f-33a5-4c88-aaf1-00c8805f24d9" />
59+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
60+
<!-- mega menu JS is injected by inject-header.sh -->
9961
</body>
10062
</html>

docgen/json/templates/cyclonedx/content.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@
1717

1818
{# Display type #}
1919
{%- if not schema is combining -%}
20-
<span class="badge badge-dark value-type">Type: {{ type_name }}</span>
20+
<span class="badge text-bg-dark value-type">Type: {{ type_name }}</span>
2121
{%- endif -%}
2222

2323
{%- if schema.format -%}
24-
<span class="badge badge-info value-type">Format: {{ schema.format }}</span>
24+
<span class="badge text-bg-info value-type">Format: {{ schema.format }}</span>
2525
{%- endif -%}
2626

2727
{# Display default #}
2828
{%- set default_value = schema.default_value -%}
2929
{%- if default_value -%}
30-
{{ " " }}<span class="badge badge-success default-value">Default: {{ default_value }}</span>
30+
{{ " " }}<span class="badge text-bg-success default-value">Default: {{ default_value }}</span>
3131
{%- endif -%}
3232
<br/>
3333

@@ -42,7 +42,7 @@
4242
{{ content(schema.refers_to_merged, True) }}
4343
{%- else -%}
4444
{%- if schema.explicit_no_additional_properties -%}
45-
{{ " " }}<span class="badge badge-info no-additional">No Additional Properties</span>
45+
{{ " " }}<span class="badge text-bg-info no-additional">No Additional Properties</span>
4646
{%- endif -%}
4747

4848
{# Combining: allOf, anyOf, oneOf, not #}
@@ -64,7 +64,7 @@
6464
<div class="enum-value" id="{{ schema.kw_enum.html_id }}">
6565
<h4>Must be one of:</h4>
6666
<table class="table table-striped table-bordered">
67-
<thead class="thead-dark">
67+
<thead class="table-dark">
6868
<tr>
6969
<th class="text-nowrap" scope="col">Name</th>
7070
<th scope="col">Description</th>

docgen/json/templates/cyclonedx/schema_doc.css

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,22 @@ body {
55
padding: 0;
66
}
77
.navbar {
8-
height: 90px;
8+
min-height: 90px;
99
padding: 0;
1010
}
11-
.navbar-inverse .navbar-nav>.open>a,
12-
.navbar-inverse .navbar-nav>.open>a:focus,
13-
.navbar-inverse .navbar-nav>.open>a:hover,
14-
.navbar-inverse {
11+
.navbar-toggler-icon {
12+
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
13+
}
14+
.navbar-dark .navbar-nav>.open>a,
15+
.navbar-dark .navbar-nav>.open>a:focus,
16+
.navbar-dark .navbar-nav>.open>a:hover,
17+
.navbar-dark {
1518
background-image: linear-gradient(269.12deg, rgba(232, 52, 82, 1) 0%, rgba(136, 38, 125, 1) 51.26%, rgba(52, 57, 175, 1) 100%);
1619
}
17-
.navbar-brand, .navbar-fixed-top {
20+
.navbar-brand, .fixed-top {
1821
padding: 0 30px 0 30px;
1922
}
20-
.navbar-inverse .navbar-nav>li>a {
23+
.navbar-dark .navbar-nav>li>a {
2124
color: #ffffff;
2225
}
2326
.site-header__logo img {
@@ -26,8 +29,9 @@ body {
2629
.version-selector {
2730
font-size: 1.2rem
2831
}
29-
.table .thead-dark th {
32+
.table .table-dark th {
3033
background-color: #323550;
34+
color: #ffffff;
3135
}
3236
.container {
3337
margin-right: auto;
@@ -73,15 +77,24 @@ ul .dropdown-menu li {
7377
}
7478
.card {
7579
border-radius: 0;
80+
--bs-card-border-color: rgba(0, 0, 0, 0.125);
81+
}
82+
.accordion + .accordion .card {
83+
margin-top: -1px;
7684
}
7785
.card-header {
7886
padding: 0;
7987
}
80-
.card-header .fa {
88+
.card-header .btn .bi {
89+
display: inline-block;
8190
transition: .3s transform ease-in-out;
91+
-webkit-text-stroke: 2px;
92+
}
93+
.card-header .btn[aria-expanded="true"] .bi {
8294
transform: rotate(90deg);
8395
}
84-
.card-header .collapsed .fa {
96+
.card-header .btn[aria-expanded="false"] .bi,
97+
.card-header .btn.collapsed .bi {
8598
transform: rotate(0deg);
8699
}
87100
.btn.btn-link {
@@ -136,52 +149,55 @@ ul .dropdown-menu li {
136149
content: '- Read Less';
137150
}
138151
.badge {
139-
color: #222222;
152+
color: #222222 !important;
140153
padding: .1em .4em .2em;
141154
margin-right: .2em;
142155
font-weight: normal;
143156
font-size: 0.9rem;
144157
border-radius: 0;
145158
}
146159
.badge.required-property {
147-
background-color: rgba(255,137,29,0.3);
160+
background-color: rgba(255,137,29,0.3) !important;
148161
border: 1px solid #FF7F0B;
149162
}
150163
.badge.value-type {
151-
background-color: rgba(174,206,229,0.3);
164+
background-color: rgba(174,206,229,0.3) !important;
152165
border: 1px solid #5C9CCB;
153166
}
154167
.badge.default-value {
155-
background-color: rgba(175,228,191,0.3);
168+
background-color: rgba(175,228,191,0.3) !important;
156169
border: 1px solid #73D08F;
157170
}
158171
.badge.example {
159-
background-color: rgba(235,202,255,0.3);
172+
background-color: rgba(235,202,255,0.3) !important;
160173
border: 1px solid #DA9FFF;
161174
}
162175
.badge.deprecated-property {
163-
background-color: rgba(255,95,95,0.3);
176+
background-color: rgba(255,95,95,0.3) !important;
164177
border: 1px solid #FF3333;
165178
}
166179
.badge.no-additional {
167-
background-color: rgba(255,82,174,0.3);
180+
background-color: rgba(255,82,174,0.3) !important;
168181
border: 1px solid #FF33A0;
169182
}
170183
.badge.pattern-property {
171-
background-color: rgba(235,229,168,0.3);
184+
background-color: rgba(235,229,168,0.3) !important;
172185
border: 1px solid #FFEA1C;
173186
}
174187
.accordion div.card:only-child {
175188
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
176189
}
177190
.examples {
178-
padding: 1rem !important;
191+
padding: 0 !important;
192+
margin-top: 0.5rem;
193+
margin-bottom: 0.5rem;
179194
}
180195
.examples pre {
181196
margin-bottom: 0;
197+
padding: 1rem 1.5rem;
182198
}
183199
.highlight.jumbotron {
184-
padding: 1rem !important;
200+
padding: 1rem 1.5rem !important;
185201
}
186202
.generated-by-footer {
187203
margin-top: 1em;

docgen/json/templates/cyclonedx/schema_doc.js

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
$(document).on('click', 'a[href^="#"]', function(event) {
2-
event.preventDefault();
3-
history.pushState({}, '', this.href);
1+
document.addEventListener('click', function(event) {
2+
var anchor = event.target.closest('a[href^="#"]');
3+
if (anchor) {
4+
event.preventDefault();
5+
history.pushState({}, '', anchor.href);
6+
}
47
});
58

69
function flashElement(elementId) {
7-
// $( "#" + elementId ).fadeOut(100).fadeIn(200).fadeOut(100).fadeIn(500);
8-
myElement = document.getElementById(elementId);
9-
myElement.classList.add("jsfh-animated-property");
10-
setTimeout(function() {
11-
myElement.classList.remove("jsfh-animated-property");
12-
}, 1000);
10+
var myElement = document.getElementById(elementId);
11+
if (myElement) {
12+
myElement.classList.add("jsfh-animated-property");
13+
setTimeout(function() {
14+
myElement.classList.remove("jsfh-animated-property");
15+
}, 1000);
16+
}
1317
}
1418

1519
function setAnchor(anchorLinkDestination) {
@@ -19,7 +23,7 @@ function setAnchor(anchorLinkDestination) {
1923

2024
function anchorOnLoad() {
2125
// Added to onload on body, checks if there is an anchor link and if so, expand
22-
let linkTarget = decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);
26+
var linkTarget = decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);
2327
if (linkTarget[0] === "#") {
2428
linkTarget = linkTarget.substr(1);
2529
}
@@ -30,31 +34,35 @@ function anchorOnLoad() {
3034
}
3135

3236
function anchorLink(linkTarget) {
33-
const target = $( "#" + linkTarget );
34-
// Find the targeted element to expand and all its parents that can be expanded
35-
target.parents().addBack().filter(".collapse:not(.show), .tab-pane, [role='tab']").each(
36-
function(index) {
37-
if($( this ).hasClass("collapse")) {
38-
$( this ).collapse("show");
39-
} else if ($( this ).hasClass("tab-pane")) {
40-
// We have the pane and not the tab itself, find the tab
41-
const tabToShow = $( "a[href='#" + $( this ).attr("id") + "']" );
42-
if (tabToShow) {
43-
tabToShow.tab("show");
44-
}
45-
} else if ($( this ).attr("role") === "tab") {
46-
// The tab is not a parent of underlying elements, the tab pane is
47-
// However, it can still be linked directly
48-
$( this ).tab("show");
37+
var target = document.getElementById(linkTarget);
38+
if (!target) return;
39+
40+
// Find the targeted element and all its parents that can be expanded
41+
var element = target;
42+
while (element) {
43+
// Expand collapsed sections
44+
if (element.classList.contains("collapse") && !element.classList.contains("show")) {
45+
var bsCollapse = new bootstrap.Collapse(element, { toggle: true });
46+
}
47+
// Activate tab panes
48+
if (element.classList.contains("tab-pane")) {
49+
var tabTrigger = document.querySelector('a[href="#' + element.id + '"]');
50+
if (tabTrigger) {
51+
var bsTab = new bootstrap.Tab(tabTrigger);
52+
bsTab.show();
4953
}
5054
}
51-
);
55+
// Handle direct tab links
56+
if (element.getAttribute("role") === "tab") {
57+
var bsTab = new bootstrap.Tab(element);
58+
bsTab.show();
59+
}
60+
element = element.parentElement;
61+
}
5262

5363
// Wait a little so the user has time to see the page scroll
54-
// Or maybe it is to be sure everything is expanded before scrolling and I was not able to bind to the bootstrap
55-
// events in a way that works all the time, we may never know
5664
setTimeout(function() {
57-
let targetElement = document.getElementById(linkTarget);
65+
var targetElement = document.getElementById(linkTarget);
5866
if (targetElement) {
5967
targetElement.scrollIntoView({ block: "center", behavior:"smooth" });
6068
// Flash the element so that the user notices where the link points to

docgen/json/templates/cyclonedx/schema_doc.min.js

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docgen/json/templates/cyclonedx/section_array.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{{ restriction("All items must be unique", "unique-items", schema.kw_unique_items.html_id) }}
99
{%- endif -%}
1010
{%- if not schema.array_additional_items -%}
11-
{{ " " }}<span class="badge badge-info no-additional">No Additional Items</span>
11+
{{ " " }}<span class="badge text-bg-info no-additional">No Additional Items</span>
1212
{%- endif -%}
1313
{%- if schema.array_items_def -%}
1414
<h4>Each item of this array must be:</h4>
@@ -24,7 +24,7 @@ <h4>Tuple Validation</h4>
2424
<h5>Item at {{ loop.index }} must be:</h5>
2525
<div class="card">
2626
<div class="card-body items-definition" id="{{ item.html_id }}">
27-
<i class="fa fa-chevron-down pull-right"></i>
27+
<i class="bi bi-chevron-down float-end"></i>
2828
{{ content(item) }}
2929
</div>
3030
</div>

0 commit comments

Comments
 (0)