jekyll-calconnect-theme 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/CLAUDE.md +30 -10
- data/_frontend/entrypoints/application.css +6 -1
- data/_frontend/entrypoints/application.js +34 -0
- data/_frontend/js/navigation.js +31 -7
- data/_includes/custom-head.html +0 -1
- data/_includes/footer.html +47 -6
- data/_includes/google-analytics.html +5 -7
- data/_includes/head.html +8 -3
- data/_includes/header.html +67 -2
- data/_includes/icons/external-link.svg +1 -0
- data/_includes/icons/github.svg +1 -0
- data/_includes/icons/linkedin.svg +1 -0
- data/_includes/icons/menu.svg +1 -0
- data/_includes/icons/moon.svg +1 -0
- data/_includes/icons/sun.svg +1 -0
- data/_includes/icons/twitter.svg +1 -0
- data/_includes/icons/youtube.svg +1 -0
- data/_includes/sidebar-nav-item.html +126 -0
- data/_includes/sidebar-nav.html +36 -0
- data/_layouts/default.html +4 -229
- data/_sass/calconnect/_code-copy.scss +33 -0
- data/_sass/calconnect/_code.scss +5 -5
- data/_sass/calconnect/_dark-mode.scss +32 -23
- data/_sass/calconnect/_edit-on-github.scss +24 -0
- data/_sass/calconnect/_layout.scss +45 -10
- data/_sass/calconnect/_mobile-sidebar-toggle.scss +35 -0
- data/_sass/calconnect/_navigation.scss +12 -12
- data/_sass/calconnect/_page-nav.scss +60 -0
- data/_sass/calconnect/_scroll-progress.scss +10 -0
- data/_sass/calconnect/_tables.scss +3 -3
- data/_sass/calconnect/_typography.scss +9 -9
- data/lib/jekyll/calconnect/theme/version.rb +1 -1
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 163b2010d9482e41e13e2884c3b315037236e436e6bbe25c577fb50d705ccf4a
|
|
4
|
+
data.tar.gz: 257d221c8f154794023f9dd21f05bd3406e607dcb823428dbc50af048bb7dcad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 85d744603dc67d956985f03e05bf5e46c805bf8aa813168ae680d3042abe9ff124d167ae35a3dbe4400707ad573d2c6e393458a1e805ec7a6fff33ef648278a7
|
|
7
|
+
data.tar.gz: fcdaa9bc4c95b6c1ea7b2abe162e61bef099de16e855ca74dc3801ffaf88cfcbe7748a8061890abf888c6a7434e9e620b3d36abafc0ee40eb4c165443fdde84c
|
data/CLAUDE.md
CHANGED
|
@@ -35,22 +35,42 @@ base.html → HTML shell: head, header, footer, Vite JS entry
|
|
|
35
35
|
|
|
36
36
|
CSS and JS live under `_frontend/` and are built by [jekyll-vite](https://github.com/elixir-vite/vite_ruby) in the consumer site. The theme provides:
|
|
37
37
|
- `_frontend/entrypoints/application.css` — Tailwind v4 config (`@import "tailwindcss"`), `@tailwindcss/typography` plugin, custom theme tokens (primary/accent colors, fonts)
|
|
38
|
-
- `_frontend/entrypoints/application.js` — imports `theme.js` (dark/light toggle with localStorage) and `navigation.js` (mobile menu, active state highlighting), plus sidebar collapsible toggle init
|
|
39
|
-
- `_sass/calconnect/` — supplementary SCSS for layout, navigation, typography, code blocks, tables, dark mode
|
|
38
|
+
- `_frontend/entrypoints/application.js` — imports `theme.js` (dark/light toggle with localStorage) and `navigation.js` (mobile menu, sidebar drawer, active state highlighting), plus sidebar collapsible toggle init
|
|
39
|
+
- `_sass/calconnect/` — supplementary SCSS for layout, navigation, typography, code blocks, tables, dark mode. All colors use `var(--color-*, fallback)` referencing Tailwind's CSS custom properties — single source of truth for the palette.
|
|
40
40
|
|
|
41
|
-
### Sidebar navigation
|
|
41
|
+
### Sidebar navigation
|
|
42
42
|
|
|
43
|
-
The sidebar is fully data-driven from `site.data.navigation_sidebar.sections`. Each section has a `match` string matched against `page.url`.
|
|
43
|
+
The sidebar is fully data-driven from `site.data.navigation_sidebar.sections`. Each section has a `match` string matched against `page.url`. The rendering is split across two includes:
|
|
44
|
+
- `sidebar-nav.html` — finds the matching section, iterates its items
|
|
45
|
+
- `sidebar-nav-item.html` — renders a single item by type. Group items recursively include this for their children, eliminating duplication.
|
|
46
|
+
|
|
47
|
+
Item types: `link`, `collection` (iterates a Jekyll collection), `collapsible` (expandable with URL filtering), `group` (labeled sub-group with children), `years` (year list from `site.data.news_years`).
|
|
48
|
+
|
|
49
|
+
### Icon system
|
|
50
|
+
|
|
51
|
+
Icons live in `_includes/icons/` as `.svg` files (Liquid templates that output `<svg>` markup). Each accepts an optional `class` parameter for sizing (`{{ include.class | default: 'w-5 h-5' }}`). Consumer sites override individual icons by dropping a same-named file in their own `_includes/icons/`.
|
|
52
|
+
|
|
53
|
+
### Site configuration (_config.yml)
|
|
54
|
+
|
|
55
|
+
- `site.logo` / `site.logo_dark` — header/footer logo paths (dark variant for dark mode)
|
|
56
|
+
- `site.header_cta` — `{url, label}` for a CTA button in the header
|
|
57
|
+
- `site.data.navigation_header` — list of `{label, url, external?}` items for header nav
|
|
58
|
+
- `site.data.navigation_footer` — list of `{title, links: [{label, url}]}` columns for footer
|
|
59
|
+
- `site.data.social_links` — list of `{platform, url}` for footer social icons (github, linkedin, twitter/x, youtube)
|
|
60
|
+
- `site.google_analytics` — GA4 measurement ID (production only)
|
|
61
|
+
- Favicon files at site root: `favicon.svg`, `favicon-96x96.png`, `favicon.ico`, `apple-touch-icon.png`, `site.webmanifest`
|
|
44
62
|
|
|
45
63
|
### Includes
|
|
46
64
|
|
|
47
|
-
- `head.html` — meta tags, Google Fonts (Inter + JetBrains Mono), critical inline CSS to prevent FOUC, `{% vite_stylesheet_tag %}
|
|
48
|
-
- `custom-head.html` — Vite client
|
|
49
|
-
- `header.html` — fixed top nav
|
|
50
|
-
- `footer.html` — copyright
|
|
65
|
+
- `head.html` — meta tags, full favicon set, Google Fonts (Inter + JetBrains Mono), critical inline CSS to prevent FOUC, `{% vite_stylesheet_tag %}`, GA4 (production only)
|
|
66
|
+
- `custom-head.html` — Vite client tag (for dev HMR in consumer sites)
|
|
67
|
+
- `header.html` — fixed top nav with logo, desktop nav, dark mode toggle, CTA button, mobile hamburger (sidebar drawer on docs pages, dropdown on others)
|
|
68
|
+
- `footer.html` — logo + description column, data-driven nav columns, social links, copyright
|
|
69
|
+
- `sidebar-nav.html` + `sidebar-nav-item.html` — data-driven sidebar navigation
|
|
51
70
|
- `breadcrumbs.html` — URL-derived breadcrumb trail
|
|
52
71
|
- `feedback.html` — "Was this helpful?" link
|
|
53
|
-
- `google-analytics.html` —
|
|
72
|
+
- `google-analytics.html` — GA4 gtag.js snippet
|
|
73
|
+
- `icons/*.svg` — parametric icon includes (class parameter for sizing)
|
|
54
74
|
|
|
55
75
|
### Dark mode
|
|
56
76
|
|
|
@@ -66,4 +86,4 @@ Class-based (`html.dark`). `theme.js` runs immediately (before DOM ready) to pre
|
|
|
66
86
|
|
|
67
87
|
### Key Tailwind notes
|
|
68
88
|
|
|
69
|
-
Uses Tailwind v4 (CSS-first config with `@theme` blocks, not `tailwind.config.js`). Colors use `gray` not `slate` for class compatibility. Primary color scale is indigo-based; accent is cyan.
|
|
89
|
+
Uses Tailwind v4 (CSS-first config with `@theme` blocks, not `tailwind.config.js`). Colors use `gray` not `slate` for class compatibility. Primary color scale is indigo-based; accent is cyan. SCSS files reference colors via `var(--color-*, fallback)` to stay in sync with the Tailwind theme.
|
|
@@ -48,9 +48,14 @@
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
:focus-visible {
|
|
51
|
-
outline: 2px solid #6366f1;
|
|
51
|
+
outline: 2px solid var(--color-primary-500, #6366f1);
|
|
52
52
|
outline-offset: 2px;
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
/* Smooth scroll offset for anchor links */
|
|
56
|
+
[id] {
|
|
57
|
+
scroll-margin-top: 5rem;
|
|
58
|
+
}
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
/* Custom component styles */
|
|
@@ -2,6 +2,7 @@ import '../js/theme.js'
|
|
|
2
2
|
import '../js/navigation.js'
|
|
3
3
|
|
|
4
4
|
document.addEventListener('DOMContentLoaded', function() {
|
|
5
|
+
// Sidebar nav toggles
|
|
5
6
|
document.querySelectorAll('.nav-toggle').forEach(function(toggle) {
|
|
6
7
|
toggle.addEventListener('click', function() {
|
|
7
8
|
this.classList.toggle('open');
|
|
@@ -9,4 +10,37 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
9
10
|
if (subsection) subsection.classList.toggle('collapsed');
|
|
10
11
|
});
|
|
11
12
|
});
|
|
13
|
+
|
|
14
|
+
// Code copy buttons
|
|
15
|
+
document.querySelectorAll('.documentation .doc-content pre').forEach(function(pre) {
|
|
16
|
+
var btn = document.createElement('button');
|
|
17
|
+
btn.className = 'code-copy-btn';
|
|
18
|
+
btn.textContent = 'Copy';
|
|
19
|
+
btn.setAttribute('aria-label', 'Copy code');
|
|
20
|
+
btn.addEventListener('click', function() {
|
|
21
|
+
var code = pre.querySelector('code');
|
|
22
|
+
var text = code ? code.textContent : pre.textContent;
|
|
23
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
24
|
+
btn.textContent = 'Copied!';
|
|
25
|
+
btn.classList.add('copied');
|
|
26
|
+
setTimeout(function() {
|
|
27
|
+
btn.textContent = 'Copy';
|
|
28
|
+
btn.classList.remove('copied');
|
|
29
|
+
}, 2000);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
pre.style.position = 'relative';
|
|
33
|
+
pre.appendChild(btn);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Scroll progress bar
|
|
37
|
+
var progressBar = document.getElementById('scroll-progress');
|
|
38
|
+
if (progressBar) {
|
|
39
|
+
window.addEventListener('scroll', function() {
|
|
40
|
+
var scrollTop = window.scrollY;
|
|
41
|
+
var docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
42
|
+
var progress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0;
|
|
43
|
+
progressBar.style.width = progress + '%';
|
|
44
|
+
}, { passive: true });
|
|
45
|
+
}
|
|
12
46
|
});
|
data/_frontend/js/navigation.js
CHANGED
|
@@ -1,24 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Navigation Script
|
|
3
|
-
* Handles mobile menu toggle and active state highlighting
|
|
3
|
+
* Handles mobile menu toggle, sidebar drawer, and active state highlighting
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
(function() {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Initialize mobile menu
|
|
10
|
+
* Initialize mobile menu / sidebar drawer
|
|
11
|
+
* On docs pages: hamburger opens sidebar drawer
|
|
12
|
+
* On other pages: hamburger opens dropdown menu
|
|
11
13
|
*/
|
|
12
14
|
function initMobileMenu() {
|
|
13
15
|
var menuButton = document.getElementById('mobile-menu-btn');
|
|
14
16
|
var mobileMenu = document.getElementById('mobile-menu');
|
|
17
|
+
var sidebar = document.querySelector('.docs-nav');
|
|
18
|
+
var backdrop = document.querySelector('.docs-nav-backdrop');
|
|
15
19
|
|
|
16
|
-
if (menuButton
|
|
17
|
-
|
|
20
|
+
if (!menuButton) return;
|
|
21
|
+
|
|
22
|
+
function closeSidebar() {
|
|
23
|
+
if (sidebar) sidebar.classList.remove('mobile-open');
|
|
24
|
+
if (backdrop) backdrop.classList.remove('active');
|
|
25
|
+
menuButton.setAttribute('aria-expanded', 'false');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
menuButton.addEventListener('click', function() {
|
|
29
|
+
if (sidebar) {
|
|
30
|
+
var isOpen = sidebar.classList.contains('mobile-open');
|
|
31
|
+
if (isOpen) {
|
|
32
|
+
closeSidebar();
|
|
33
|
+
} else {
|
|
34
|
+
sidebar.classList.add('mobile-open');
|
|
35
|
+
if (backdrop) backdrop.classList.add('active');
|
|
36
|
+
menuButton.setAttribute('aria-expanded', 'true');
|
|
37
|
+
}
|
|
38
|
+
} else if (mobileMenu) {
|
|
18
39
|
var isExpanded = menuButton.getAttribute('aria-expanded') === 'true';
|
|
19
40
|
menuButton.setAttribute('aria-expanded', !isExpanded);
|
|
20
41
|
mobileMenu.classList.toggle('hidden');
|
|
21
|
-
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (backdrop) {
|
|
46
|
+
backdrop.addEventListener('click', closeSidebar);
|
|
22
47
|
}
|
|
23
48
|
}
|
|
24
49
|
|
|
@@ -33,7 +58,6 @@
|
|
|
33
58
|
sidebarLinks.forEach(function(link) {
|
|
34
59
|
var href = link.getAttribute('href');
|
|
35
60
|
if (href) {
|
|
36
|
-
// Normalize paths for comparison
|
|
37
61
|
var linkPath = href.replace(/\/$/, '');
|
|
38
62
|
var pagePath = currentPath.replace(/\/$/, '');
|
|
39
63
|
if (linkPath === pagePath) {
|
|
@@ -43,7 +67,7 @@
|
|
|
43
67
|
});
|
|
44
68
|
|
|
45
69
|
// Header navigation
|
|
46
|
-
var headerLinks = document.querySelectorAll('nav a[href]');
|
|
70
|
+
var headerLinks = document.querySelectorAll('#nav-header nav a[href]');
|
|
47
71
|
headerLinks.forEach(function(link) {
|
|
48
72
|
var href = link.getAttribute('href');
|
|
49
73
|
if (href && href !== '/') {
|
data/_includes/custom-head.html
CHANGED
data/_includes/footer.html
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
|
-
<footer class="bg-gray-50 dark:bg-gray-950 border-t border-gray-200 dark:border-white/10
|
|
2
|
-
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<footer class="bg-gray-50 dark:bg-gray-950 border-t border-gray-200 dark:border-white/10 transition-colors">
|
|
2
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
3
|
+
{% if site.data.navigation_footer %}
|
|
4
|
+
<div class="grid grid-cols-2 md:grid-cols-{{ site.data.navigation_footer.size | plus: 1 | at_least: 2 }} gap-8 mb-8">
|
|
5
|
+
<!-- Logo + description column -->
|
|
6
|
+
<div class="col-span-2 md:col-span-1">
|
|
7
|
+
{% if site.logo %}
|
|
8
|
+
<img src="{{ site.logo | relative_url }}" alt="{{ site.title }}" class="h-8 mb-4 block dark:hidden">
|
|
9
|
+
<img src="{{ site.logo_dark | default: site.logo | relative_url }}" alt="{{ site.title }}" class="h-8 mb-4 hidden dark:block">
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if site.description %}
|
|
12
|
+
<p class="text-sm text-gray-600 dark:text-gray-500">{{ site.description }}</p>
|
|
13
|
+
{% endif %}
|
|
14
|
+
</div>
|
|
15
|
+
<!-- Nav columns -->
|
|
16
|
+
{% for column in site.data.navigation_footer %}
|
|
17
|
+
<div>
|
|
18
|
+
<h3 class="text-sm font-semibold text-gray-900 dark:text-white uppercase tracking-wider mb-4">{{ column.title }}</h3>
|
|
19
|
+
<ul class="space-y-2">
|
|
20
|
+
{% for link in column.links %}
|
|
21
|
+
<li>
|
|
22
|
+
<a href="{{ link.url | relative_url }}"
|
|
23
|
+
class="text-sm text-gray-600 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors">
|
|
24
|
+
{{ link.label }}
|
|
25
|
+
</a>
|
|
26
|
+
</li>
|
|
27
|
+
{% endfor %}
|
|
28
|
+
</ul>
|
|
29
|
+
</div>
|
|
30
|
+
{% endfor %}
|
|
31
|
+
</div>
|
|
32
|
+
{% endif %}
|
|
33
|
+
|
|
34
|
+
<div class="pt-8 border-t border-gray-200 dark:border-white/10 flex flex-col sm:flex-row justify-between items-center gap-4">
|
|
35
|
+
<p class="text-sm text-gray-500 dark:text-gray-600">
|
|
36
|
+
© {{ site.time | date: '%Y' }} {{ site.title | default: 'CalConnect' }}<sup>SM</sup>.
|
|
37
|
+
<a href="{{ site.copyright_url | default: '/about/policies/copyright-and-licensing' | relative_url }}" class="hover:text-gray-700 dark:hover:text-gray-300">Copyright</a>
|
|
7
38
|
</p>
|
|
39
|
+
{% if site.data.social_links %}
|
|
40
|
+
<div class="flex gap-3">
|
|
41
|
+
{% for social in site.data.social_links %}
|
|
42
|
+
<a href="{{ social.url }}" class="text-gray-400 hover:text-gray-600 dark:hover:text-white transition-colors" aria-label="{{ social.platform | capitalize }}" target="_blank" rel="noopener">
|
|
43
|
+
{% assign icon_path = social.platform | prepend: 'icons/' | append: '.svg' %}
|
|
44
|
+
{% include {{ icon_path }} %}
|
|
45
|
+
</a>
|
|
46
|
+
{% endfor %}
|
|
47
|
+
</div>
|
|
48
|
+
{% endif %}
|
|
8
49
|
</div>
|
|
9
50
|
</div>
|
|
10
51
|
</footer>
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.google_analytics }}"></script>
|
|
1
2
|
<script>
|
|
2
|
-
|
|
3
|
-
(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
ga('create', '{{ site.google_analytics }}', 'auto');
|
|
8
|
-
ga('send', 'pageview');
|
|
3
|
+
window.dataLayer = window.dataLayer || [];
|
|
4
|
+
function gtag(){dataLayer.push(arguments);}
|
|
5
|
+
gtag('js', new Date());
|
|
6
|
+
gtag('config', '{{ site.google_analytics }}');
|
|
9
7
|
</script>
|
data/_includes/head.html
CHANGED
|
@@ -6,9 +6,14 @@
|
|
|
6
6
|
<meta name="description"
|
|
7
7
|
content="{{ page.excerpt | default: site.description | strip_html | normalize_whitespace | truncate: 160 | escape }}" />
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
<link rel="icon" type="image/
|
|
11
|
-
{
|
|
9
|
+
<!-- Favicons -->
|
|
10
|
+
<link rel="icon" type="image/svg+xml" href="{{ '/favicon.svg' | relative_url }}">
|
|
11
|
+
<link rel="icon" type="image/png" href="{{ '/favicon-96x96.png' | relative_url }}" sizes="96x96">
|
|
12
|
+
<link rel="shortcut icon" href="{{ '/favicon.ico' | relative_url }}" type="image/vnd.microsoft.icon">
|
|
13
|
+
<link rel="apple-touch-icon" sizes="180x180" href="{{ '/apple-touch-icon.png' | relative_url }}">
|
|
14
|
+
<link rel="manifest" href="{{ '/site.webmanifest' | relative_url }}">
|
|
15
|
+
<meta name="theme-color" content="#ffffff">
|
|
16
|
+
<meta name="theme-color" content="#0f172a" media="(prefers-color-scheme: dark)">
|
|
12
17
|
|
|
13
18
|
<!-- Google Fonts - Inter + JetBrains Mono -->
|
|
14
19
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
data/_includes/header.html
CHANGED
|
@@ -1,9 +1,74 @@
|
|
|
1
1
|
<header id="nav-header" class="fixed top-0 left-0 right-0 z-50 bg-white dark:bg-gray-950 backdrop-blur-md border-b border-gray-200 dark:border-white/10 transition-colors">
|
|
2
2
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
3
3
|
<div class="flex items-center justify-between h-16">
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
<!-- Left: Logo -->
|
|
5
|
+
<a href="{{ '/' | relative_url }}" class="flex items-center gap-2.5">
|
|
6
|
+
{% if site.logo %}
|
|
7
|
+
<img src="{{ site.logo | relative_url }}" alt="{{ site.title | default: 'CalConnect' }}" class="h-8 w-auto block dark:hidden">
|
|
8
|
+
<img src="{{ site.logo_dark | default: site.logo | relative_url }}" alt="{{ site.title | default: 'CalConnect' }}" class="h-8 w-auto hidden dark:block">
|
|
9
|
+
{% else %}
|
|
10
|
+
<span class="text-lg font-semibold text-gray-900 dark:text-white tracking-tight">{{ site.title | default: "CalConnect" }}</span>
|
|
11
|
+
{% endif %}
|
|
6
12
|
</a>
|
|
13
|
+
|
|
14
|
+
<!-- Center: Desktop nav -->
|
|
15
|
+
{% if site.data.navigation_header %}
|
|
16
|
+
<nav class="hidden lg:flex items-center gap-1" aria-label="Main navigation">
|
|
17
|
+
{% for item in site.data.navigation_header %}
|
|
18
|
+
<a href="{{ item.url | relative_url }}"
|
|
19
|
+
class="px-3 py-2 text-sm font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 transition-colors
|
|
20
|
+
{% if item.external %}inline-flex items-center gap-1{% endif %}">
|
|
21
|
+
{{ item.label }}
|
|
22
|
+
{% if item.external %}{% include icons/external-link.svg %}{% endif %}
|
|
23
|
+
</a>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</nav>
|
|
26
|
+
{% endif %}
|
|
27
|
+
|
|
28
|
+
<!-- Right: Theme toggle + CTA + Mobile menu -->
|
|
29
|
+
<div class="flex items-center gap-2">
|
|
30
|
+
<button id="theme-toggle" type="button"
|
|
31
|
+
class="p-2 rounded-lg text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
|
|
32
|
+
aria-label="Toggle dark mode">
|
|
33
|
+
<span class="dark:hidden">{% include icons/moon.svg %}</span>
|
|
34
|
+
<span class="hidden dark:block">{% include icons/sun.svg %}</span>
|
|
35
|
+
</button>
|
|
36
|
+
|
|
37
|
+
{% if site.header_cta %}
|
|
38
|
+
<a href="{{ site.header_cta.url | relative_url }}"
|
|
39
|
+
class="hidden sm:inline-flex items-center px-4 py-2 bg-primary-500 text-white text-sm font-medium rounded-full hover:bg-primary-600 transition-colors">
|
|
40
|
+
{{ site.header_cta.label }}
|
|
41
|
+
</a>
|
|
42
|
+
{% endif %}
|
|
43
|
+
|
|
44
|
+
<button id="mobile-menu-btn" type="button"
|
|
45
|
+
class="lg:hidden p-2 rounded-lg text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"
|
|
46
|
+
aria-label="Open menu" aria-expanded="false">
|
|
47
|
+
{% include icons/menu.svg %}
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
7
50
|
</div>
|
|
8
51
|
</div>
|
|
52
|
+
|
|
53
|
+
<!-- Mobile dropdown menu (for non-docs pages) -->
|
|
54
|
+
{% if site.data.navigation_header %}
|
|
55
|
+
<div id="mobile-menu" class="lg:hidden hidden border-t border-gray-200 dark:border-white/10">
|
|
56
|
+
<div class="px-4 py-4 space-y-1">
|
|
57
|
+
{% for item in site.data.navigation_header %}
|
|
58
|
+
<a href="{{ item.url | relative_url }}"
|
|
59
|
+
class="block px-4 py-2 text-base font-medium text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white rounded-lg hover:bg-gray-100 dark:hover:bg-white/10
|
|
60
|
+
{% if item.external %}flex items-center gap-1{% endif %}">
|
|
61
|
+
{{ item.label }}
|
|
62
|
+
{% if item.external %}{% include icons/external-link.svg %}{% endif %}
|
|
63
|
+
</a>
|
|
64
|
+
{% endfor %}
|
|
65
|
+
{% if site.header_cta %}
|
|
66
|
+
<a href="{{ site.header_cta.url | relative_url }}"
|
|
67
|
+
class="block px-4 py-2 mt-2 text-center bg-primary-500 text-white font-medium rounded-full hover:bg-primary-600">
|
|
68
|
+
{{ site.header_cta.label }}
|
|
69
|
+
</a>
|
|
70
|
+
{% endif %}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
{% endif %}
|
|
9
74
|
</header>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-3 h-3' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-6 h-6' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg class="{{ include.class | default: 'w-5 h-5' }}" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{% comment %}
|
|
2
|
+
Renders a single sidebar nav item. Expects `include.item`.
|
|
3
|
+
Types: link, collection, collapsible, group, years.
|
|
4
|
+
{% endcomment %}
|
|
5
|
+
|
|
6
|
+
{% comment %} --- Collection helper --- {% endcomment %}
|
|
7
|
+
{% assign _collection_name = include.item.collection %}
|
|
8
|
+
{% case _collection_name %}
|
|
9
|
+
{% when 'about_pages' %}
|
|
10
|
+
{% assign _docs = site.about_pages %}
|
|
11
|
+
{% when 'membership_pages' %}
|
|
12
|
+
{% assign _docs = site.membership_pages %}
|
|
13
|
+
{% when 'events' %}
|
|
14
|
+
{% assign _docs = site.events %}
|
|
15
|
+
{% when 'resources' %}
|
|
16
|
+
{% assign _docs = site.resources %}
|
|
17
|
+
{% else %}
|
|
18
|
+
{% assign _docs = site.posts %}
|
|
19
|
+
{% endcase %}
|
|
20
|
+
|
|
21
|
+
{% comment %} --- LINK TYPE --- {% endcomment %}
|
|
22
|
+
{% if include.item.type == 'link' or include.item.type == nil %}
|
|
23
|
+
{% assign _active = false %}
|
|
24
|
+
{% if page.url == include.item.url %}{% assign _active = true %}{% endif %}
|
|
25
|
+
<li>
|
|
26
|
+
<a href="{{ include.item.url | relative_url }}" {% if _active %}class="active"{% endif %}>
|
|
27
|
+
{{ include.item.label }}
|
|
28
|
+
</a>
|
|
29
|
+
</li>
|
|
30
|
+
|
|
31
|
+
{% comment %} --- COLLECTION TYPE --- {% endcomment %}
|
|
32
|
+
{% elsif include.item.type == 'collection' %}
|
|
33
|
+
{% if include.item.sort_field == 'date' and include.item.sort_order == 'desc' %}
|
|
34
|
+
{% assign _sorted = _docs | sort: 'date' | reverse %}
|
|
35
|
+
{% elsif include.item.sort_field %}
|
|
36
|
+
{% assign _sorted = _docs | sort: include.item.sort_field %}
|
|
37
|
+
{% else %}
|
|
38
|
+
{% assign _sorted = _docs %}
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% for doc in _sorted %}
|
|
41
|
+
{% assign _doc_active = false %}
|
|
42
|
+
{% if page.url == doc.url %}{% assign _doc_active = true %}{% endif %}
|
|
43
|
+
<li>
|
|
44
|
+
<a href="{{ doc.url | relative_url }}" {% if _doc_active %}class="active"{% endif %}>
|
|
45
|
+
{{ doc.title | default: doc.name }}
|
|
46
|
+
</a>
|
|
47
|
+
</li>
|
|
48
|
+
{% endfor %}
|
|
49
|
+
|
|
50
|
+
{% comment %} --- COLLAPSIBLE TYPE --- {% endcomment %}
|
|
51
|
+
{% elsif include.item.type == 'collapsible' %}
|
|
52
|
+
{% assign _open = false %}
|
|
53
|
+
{% if page.url contains include.item.url_filter %}{% assign _open = true %}{% endif %}
|
|
54
|
+
{% if include.item.url and page.url == include.item.url %}{% assign _open = true %}{% endif %}
|
|
55
|
+
{% if include.item.children %}
|
|
56
|
+
{% for child in include.item.children %}
|
|
57
|
+
{% if page.url == child.url %}{% assign _open = true %}{% break %}{% endif %}
|
|
58
|
+
{% endfor %}
|
|
59
|
+
{% endif %}
|
|
60
|
+
<li>
|
|
61
|
+
<span class="nav-toggle {% if _open %}open{% endif %}">{{ include.item.label }}</span>
|
|
62
|
+
<ul class="nav-subsection {% unless _open %}collapsed{% endunless %}">
|
|
63
|
+
{% if include.item.first_child_label %}
|
|
64
|
+
{% assign _first_active = false %}
|
|
65
|
+
{% if page.url == include.item.url %}{% assign _first_active = true %}{% endif %}
|
|
66
|
+
<li><a href="{{ include.item.url | relative_url }}" {% if _first_active %}class="active"{% endif %}>{{ include.item.first_child_label }}</a></li>
|
|
67
|
+
{% endif %}
|
|
68
|
+
{% if include.item.children %}
|
|
69
|
+
{% for child in include.item.children %}
|
|
70
|
+
{% assign _child_active = false %}
|
|
71
|
+
{% if page.url == child.url %}{% assign _child_active = true %}{% endif %}
|
|
72
|
+
<li>
|
|
73
|
+
<a href="{{ child.url | relative_url }}" {% if _child_active %}class="active"{% endif %}>
|
|
74
|
+
{{ child.label }}
|
|
75
|
+
</a>
|
|
76
|
+
</li>
|
|
77
|
+
{% endfor %}
|
|
78
|
+
{% elsif include.item.collection %}
|
|
79
|
+
{% for doc in _docs %}
|
|
80
|
+
{% assign _url_matches = false %}
|
|
81
|
+
{% if doc.url contains include.item.url_filter %}{% assign _url_matches = true %}{% endif %}
|
|
82
|
+
{% assign _excluded = false %}
|
|
83
|
+
{% if doc.url == include.item.url %}{% assign _excluded = true %}{% endif %}
|
|
84
|
+
{% if include.item.exclude_urls %}
|
|
85
|
+
{% for excl in include.item.exclude_urls %}
|
|
86
|
+
{% if doc.url == excl %}{% assign _excluded = true %}{% endif %}
|
|
87
|
+
{% endfor %}
|
|
88
|
+
{% elsif include.item.exclude_url %}
|
|
89
|
+
{% if doc.url == include.item.exclude_url %}{% assign _excluded = true %}{% endif %}
|
|
90
|
+
{% endif %}
|
|
91
|
+
{% if _url_matches and _excluded == false %}
|
|
92
|
+
{% assign _child_active = false %}
|
|
93
|
+
{% if page.url == doc.url %}{% assign _child_active = true %}{% endif %}
|
|
94
|
+
<li>
|
|
95
|
+
<a href="{{ doc.url | relative_url }}" {% if _child_active %}class="active"{% endif %}>
|
|
96
|
+
{{ doc.title }}
|
|
97
|
+
</a>
|
|
98
|
+
</li>
|
|
99
|
+
{% endif %}
|
|
100
|
+
{% endfor %}
|
|
101
|
+
{% endif %}
|
|
102
|
+
</ul>
|
|
103
|
+
</li>
|
|
104
|
+
|
|
105
|
+
{% comment %} --- YEARS TYPE --- {% endcomment %}
|
|
106
|
+
{% elsif include.item.type == 'years' %}
|
|
107
|
+
{% assign _years = site.data.news_years %}
|
|
108
|
+
{% for year in _years %}
|
|
109
|
+
{% assign _yactive = false %}
|
|
110
|
+
{% if page.url contains year %}{% assign _yactive = true %}{% endif %}
|
|
111
|
+
<li>
|
|
112
|
+
<a href="/news/{{ year }}/" {% if _yactive %}class="active"{% endif %}>
|
|
113
|
+
{{ year }}
|
|
114
|
+
</a>
|
|
115
|
+
</li>
|
|
116
|
+
{% endfor %}
|
|
117
|
+
|
|
118
|
+
{% comment %} --- GROUP TYPE --- {% endcomment %}
|
|
119
|
+
{% elsif include.item.type == 'group' %}
|
|
120
|
+
</ul>
|
|
121
|
+
<div class="nav-group-title">{{ include.item.label }}</div>
|
|
122
|
+
<ul class="nav-items">
|
|
123
|
+
{% for child in include.item.children %}
|
|
124
|
+
{% include sidebar-nav-item.html item=child %}
|
|
125
|
+
{% endfor %}
|
|
126
|
+
{% endif %}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{% comment %}
|
|
2
|
+
Sidebar navigation — fully data-driven from site.data.navigation_sidebar.sections.
|
|
3
|
+
Matches the current page URL against each section's `match` string or `match_list` array,
|
|
4
|
+
then renders items by type: link, collection, collapsible, group, years.
|
|
5
|
+
{% endcomment %}
|
|
6
|
+
{% assign current_section = nil %}
|
|
7
|
+
{% for section in site.data.navigation_sidebar.sections %}
|
|
8
|
+
{% assign _matched = false %}
|
|
9
|
+
{% if section.match_list %}
|
|
10
|
+
{% for m in section.match_list %}
|
|
11
|
+
{% if page.url contains m %}
|
|
12
|
+
{% assign _matched = true %}
|
|
13
|
+
{% break %}
|
|
14
|
+
{% endif %}
|
|
15
|
+
{% endfor %}
|
|
16
|
+
{% elsif section.match %}
|
|
17
|
+
{% if page.url contains section.match %}
|
|
18
|
+
{% assign _matched = true %}
|
|
19
|
+
{% endif %}
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% if _matched %}
|
|
22
|
+
{% assign current_section = section %}
|
|
23
|
+
{% break %}
|
|
24
|
+
{% endif %}
|
|
25
|
+
{% endfor %}
|
|
26
|
+
|
|
27
|
+
{% if current_section %}
|
|
28
|
+
<div class="nav-section">
|
|
29
|
+
<div class="nav-section-title">{{ current_section.title }}</div>
|
|
30
|
+
<ul class="nav-items">
|
|
31
|
+
{% for item in current_section.items %}
|
|
32
|
+
{% include sidebar-nav-item.html item=item %}
|
|
33
|
+
{% endfor %}
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
{% endif %}
|