Skip to content

Commit badee2b

Browse files
Copilotmvadari
andcommitted
Add category-based navigation with separate category pages like eips.ethereum.org
Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
1 parent 4159668 commit badee2b

File tree

4 files changed

+326
-1
lines changed

4 files changed

+326
-1
lines changed

site/assets/style.css

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,91 @@ main { min-height: calc(100vh - 200px); padding: 48px 0; }
250250
color: #FFD700;
251251
}
252252

253+
.category-badge.all {
254+
border: 1px solid var(--color-gray-4);
255+
background-color: var(--color-gray-7);
256+
color: var(--color-gray-4);
257+
}
258+
259+
/* Category navigation */
260+
.category-nav {
261+
margin: 32px 0;
262+
padding: 24px;
263+
background: var(--bg-color-raised);
264+
border: 1px solid var(--border-color);
265+
border-radius: 8px;
266+
}
267+
268+
.category-nav h3 {
269+
margin: 0 0 16px 0;
270+
color: var(--text-color);
271+
font-size: 16px;
272+
font-weight: 600;
273+
}
274+
275+
.category-links {
276+
display: flex;
277+
flex-wrap: wrap;
278+
gap: 12px;
279+
}
280+
281+
.category-link {
282+
display: flex;
283+
align-items: center;
284+
gap: 8px;
285+
padding: 8px 16px;
286+
background: var(--bg-color);
287+
border: 1px solid var(--border-color);
288+
border-radius: 6px;
289+
text-decoration: none;
290+
transition: all 0.2s;
291+
font-size: 14px;
292+
}
293+
294+
.category-link:hover {
295+
background: var(--color-gray-7);
296+
border-color: var(--color-gray-4);
297+
transform: translateY(-1px);
298+
}
299+
300+
.category-link.active {
301+
background: var(--color-gray-7);
302+
border-color: var(--link-color);
303+
}
304+
305+
.category-link .count {
306+
color: var(--text-color-secondary);
307+
font-size: 12px;
308+
}
309+
310+
/* Category header for individual category pages */
311+
.category-header {
312+
display: flex;
313+
align-items: center;
314+
gap: 16px;
315+
margin-bottom: 16px;
316+
}
317+
318+
.category-meta {
319+
display: flex;
320+
align-items: center;
321+
gap: 12px;
322+
}
323+
324+
.category-description {
325+
margin: 16px 0 24px 0;
326+
padding: 16px;
327+
background: var(--color-gray-8);
328+
border-left: 4px solid var(--link-color);
329+
border-radius: 0 4px 4px 0;
330+
}
331+
332+
.category-description p {
333+
margin: 0;
334+
color: var(--text-color-secondary);
335+
line-height: 1.6;
336+
}
337+
253338
/* XLS document page */
254339
.xls-document {
255340
padding: 32px;

site/build_site.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def build_site():
4747

4848
# Create subdirectories
4949
(site_dir / "xls").mkdir()
50+
(site_dir / "category").mkdir() # New directory for category pages
5051
(site_dir / "assets").mkdir()
5152

5253
# Setup Jinja2 environment
@@ -93,12 +94,45 @@ def build_site():
9394
# Sort documents by number in reverse order (later ones more relevant)
9495
xls_docs.sort(key=lambda x: int(x.number), reverse=True)
9596

96-
# Generate index page
97+
# Group documents by category for category pages and navigation
98+
categories = {}
99+
for doc in xls_docs:
100+
category = doc.category
101+
if category not in categories:
102+
categories[category] = []
103+
categories[category].append(doc)
104+
105+
# Generate category pages
106+
category_template = env.get_template("category.html")
107+
all_categories = [(cat, len(docs)) for cat, docs in sorted(categories.items())]
108+
109+
for category, category_docs in categories.items():
110+
# Sort category documents by number in reverse order
111+
category_docs.sort(key=lambda x: int(x.number), reverse=True)
112+
113+
category_html = category_template.render(
114+
title=f"{category} XLS Standards",
115+
category=category,
116+
category_docs=category_docs,
117+
all_categories=all_categories,
118+
total_count=len(xls_docs),
119+
base_url=".." if base_url == "." else base_url,
120+
)
121+
122+
# Write category HTML file
123+
category_file = site_dir / "category" / f"{category.lower()}.html"
124+
with open(category_file, "w", encoding="utf-8") as f:
125+
f.write(category_html)
126+
127+
print(f"Generated category page: {category_file}")
128+
129+
# Generate index page with category navigation
97130
index_template = env.get_template("index.html")
98131
index_html = index_template.render(
99132
title="XRP Ledger Standards (XLS)",
100133
total_count=len(xls_docs),
101134
xls_docs=xls_docs,
135+
all_categories=all_categories,
102136
base_url=base_url,
103137
)
104138

site/templates/category.html

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<!--
2+
Category page template for XLS Standards website
3+
4+
Displays XLS standards filtered by category with:
5+
- Category-specific introduction and metadata
6+
- Interactive sortable table with all columns (except category since it's consistent)
7+
- Status badges with color coding
8+
- JavaScript-powered sorting functionality
9+
- Navigation links back to main page and other categories
10+
-->
11+
{% extends "base.html" %}
12+
13+
{% block content %}
14+
<!-- Category introduction section -->
15+
<div class="intro">
16+
<div class="category-header">
17+
<h2>{{ category.title() }} XLS Standards</h2>
18+
<div class="category-meta">
19+
<span class="category-badge {{ category|lower }}">{{ category }}</span>
20+
<span class="count">{{ category_docs|length }} standard{{ 's' if category_docs|length != 1 else '' }}</span>
21+
</div>
22+
</div>
23+
24+
<!-- Category description -->
25+
<div class="category-description">
26+
{% if category|lower == 'amendment' %}
27+
<p>Amendment standards define changes to the core XRP Ledger protocol that require network consensus and activation through the amendment process.</p>
28+
{% elif category|lower == 'community' %}
29+
<p>Community standards describe best practices, conventions, and guidelines for the XRP Ledger ecosystem that don't require protocol changes.</p>
30+
{% elif category|lower == 'protocol' %}
31+
<p>Protocol standards specify XRP Ledger APIs, formats, and behaviors that don't require consensus changes but define how applications interact with the network.</p>
32+
{% else %}
33+
<p>Standards in the {{ category }} category.</p>
34+
{% endif %}
35+
</div>
36+
37+
<!-- Navigation to other categories -->
38+
<div class="category-nav">
39+
<h3>Browse by Category:</h3>
40+
<div class="category-links">
41+
<a href="{{ base_url }}/" class="category-link">
42+
<span class="category-badge all">All Categories</span>
43+
<span class="count">{{ total_count }} standards</span>
44+
</a>
45+
{% for cat_name, cat_count in all_categories %}
46+
<a href="{{ base_url }}/category/{{ cat_name|lower }}.html" class="category-link{% if cat_name|lower == category|lower %} active{% endif %}">
47+
<span class="category-badge {{ cat_name|lower }}">{{ cat_name }}</span>
48+
<span class="count">{{ cat_count }} standard{{ 's' if cat_count != 1 else '' }}</span>
49+
</a>
50+
{% endfor %}
51+
</div>
52+
</div>
53+
</div>
54+
55+
<!-- Category standards table -->
56+
<div class="standards-table-container">
57+
<table class="standards-table" id="standardsTable">
58+
<thead>
59+
<tr>
60+
<!-- Sortable column headers - no category column since all are the same category -->
61+
<th class="sortable" data-column="number">Number</th>
62+
<th class="sortable" data-column="title">Title</th>
63+
<th class="sortable" data-column="author">Author(s)</th>
64+
<th class="sortable" data-column="status">Status</th>
65+
<th class="sortable" data-column="created">Created</th>
66+
</tr>
67+
</thead>
68+
<tbody>
69+
<!-- Loop through category-specific XLS documents -->
70+
{% for doc in category_docs %}
71+
<tr class="status-{{ doc.status }}">
72+
<!-- XLS number column with link to document -->
73+
<td class="number-col" data-label="Number" data-sort-value="{{ doc.number|int }}">
74+
<a href="{{ base_url }}/xls/{{ doc.folder }}.html" class="xls-link">XLS-{{ doc.number }}</a>
75+
</td>
76+
77+
<!-- Document title with link -->
78+
<td class="title-col" data-label="Title" data-sort-value="{{ doc.title|lower }}">
79+
<a href="{{ base_url }}/xls/{{ doc.folder }}.html">{{ doc.title }}</a>
80+
</td>
81+
82+
<!-- Author information -->
83+
<td class="author-col" data-label="Author" data-sort-value="{% for author in doc.authors %}{% if author %}{{ author[0]|lower }}{% endif %}{% if not loop.last %}, {% endif %}{% endfor %}">
84+
{% for author in doc.authors %}
85+
<a href="{{ author[1] }}">{{ author[0] }}</a>{% if not loop.last %}, {% endif %}
86+
{% endfor %}
87+
</td>
88+
89+
<!-- Status badge with conditional styling -->
90+
<td class="status-col" data-label="Status" data-sort-value="{{ doc.status|lower }}">
91+
<span class="status-badge {{doc.status|lower}}">{{ doc.status.title() }}</span>
92+
</td>
93+
94+
<!-- Created date column -->
95+
<td class="created-col" data-label="Created" data-sort-value="{{ doc.created }}">
96+
{{ doc.created }}
97+
</td>
98+
</tr>
99+
{% endfor %}
100+
</tbody>
101+
</table>
102+
</div>
103+
{% endblock %}
104+
105+
{% block scripts %}
106+
<!-- JavaScript for interactive table sorting functionality -->
107+
<script>
108+
document.addEventListener('DOMContentLoaded', function() {
109+
const table = document.getElementById('standardsTable');
110+
const headers = table.querySelectorAll('th.sortable');
111+
// Default sort: reverse numerical order (later standards more relevant)
112+
let currentSort = {column: 'number', direction: 'desc'};
113+
114+
// Set initial sort indicator on the Number column
115+
const numberHeader = table.querySelector('th[data-column="number"]');
116+
if (numberHeader) numberHeader.classList.add('sort-desc');
117+
118+
// Add click event listeners to all sortable headers
119+
headers.forEach(header => {
120+
header.addEventListener('click', function() {
121+
const column = this.dataset.column;
122+
let direction = 'asc';
123+
124+
// Toggle direction if clicking the same column
125+
if (currentSort.column === column && currentSort.direction === 'asc') {
126+
direction = 'desc';
127+
}
128+
129+
// Perform the sort and update UI indicators
130+
sortTable(column, direction);
131+
updateSortIndicators(this, direction);
132+
133+
// Update current sort state
134+
currentSort = {column, direction};
135+
});
136+
});
137+
138+
/**
139+
* Sort the table by the specified column and direction
140+
*/
141+
function sortTable(column, direction) {
142+
const tbody = table.querySelector('tbody');
143+
const rows = Array.from(tbody.querySelectorAll('tr'));
144+
145+
// Sort rows based on data-sort-value attributes
146+
rows.sort((a, b) => {
147+
let aVal, bVal;
148+
149+
// Special handling for numerical sorting of XLS numbers and dates
150+
if (column === 'number') {
151+
aVal = parseInt(a.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue);
152+
bVal = parseInt(b.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue);
153+
} else if (column === 'created') {
154+
// Sort by date - data is already in YYYY-MM-DD format which sorts correctly as strings
155+
aVal = a.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue;
156+
bVal = b.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue;
157+
} else {
158+
// Text-based sorting for other columns
159+
aVal = a.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue;
160+
bVal = b.querySelector(`td[data-label="${column.charAt(0).toUpperCase() + column.slice(1)}"]`).dataset.sortValue;
161+
}
162+
163+
// Apply sort direction
164+
if (direction === 'asc') {
165+
return aVal > bVal ? 1 : -1;
166+
} else {
167+
return aVal < bVal ? 1 : -1;
168+
}
169+
});
170+
171+
// Re-append sorted rows to the table body
172+
rows.forEach(row => tbody.appendChild(row));
173+
}
174+
175+
/**
176+
* Update visual sort indicators in column headers
177+
*/
178+
function updateSortIndicators(activeHeader, direction) {
179+
// Clear all existing sort indicators
180+
headers.forEach(h => {
181+
h.classList.remove('sort-asc', 'sort-desc');
182+
});
183+
184+
// Add indicator to the active header
185+
activeHeader.classList.add(`sort-${direction}`);
186+
}
187+
});
188+
</script>
189+
{% endblock %}

site/templates/index.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@
1616
<h2>XRP Ledger Standards (XLS)</h2>
1717
<p>XRP Ledger Standards (XLSes) describe standards and specifications relating to the XRP Ledger ecosystem that help achieve interoperability, compatibility, and excellent user experience.</p>
1818
<p>Total standards: <strong>{{ total_count }}</strong></p>
19+
20+
<!-- Category navigation -->
21+
<div class="category-nav">
22+
<h3>Browse by Category:</h3>
23+
<div class="category-links">
24+
<a href="{{ base_url }}/" class="category-link active">
25+
<span class="category-badge all">All Categories</span>
26+
<span class="count">{{ total_count }} standards</span>
27+
</a>
28+
{% for cat_name, cat_count in all_categories %}
29+
<a href="{{ base_url }}/category/{{ cat_name|lower }}.html" class="category-link">
30+
<span class="category-badge {{ cat_name|lower }}">{{ cat_name }}</span>
31+
<span class="count">{{ cat_count }} standard{{ 's' if cat_count != 1 else '' }}</span>
32+
</a>
33+
{% endfor %}
34+
</div>
35+
</div>
1936
</div>
2037

2138
<!-- Main standards table with horizontal scrolling for mobile -->

0 commit comments

Comments
 (0)