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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8101b4b6ebc2ce836878b1bfb0bca4a0cf6a6e74dc1e35882437ae36f81dbd2
4
- data.tar.gz: 9b23833d258824df5b7b5a2dd33f5491833d35ca2491f071ffff44e660586e98
3
+ metadata.gz: 163b2010d9482e41e13e2884c3b315037236e436e6bbe25c577fb50d705ccf4a
4
+ data.tar.gz: 257d221c8f154794023f9dd21f05bd3406e607dcb823428dbc50af048bb7dcad
5
5
  SHA512:
6
- metadata.gz: 6cad0f652b298dc0467a383c513e7f83384bc5d1f0c828fb7cff3d25cacde3ebba76e5cc97ddb7cea67980d9464815e545160aaa6fb8bf06e2ecd98ba40e6720
7
- data.tar.gz: a4194106e49d4f06e26d6566529468a34fb7ea9250875e62155c08732ee0a6438d92373583b9d578ec3cc6baff52270cef6f2affa1c5da8f8688e5acd784770a
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 (default.html layout)
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`. Item types: `link`, `collection` (iterates a Jekyll collection), `collapsible` (expandable with URL filtering), `group` (labeled sub-group), `years` (year list from `site.data.news_years`). The layout has no hardcoded page knowledge — all configuration is in the consumer site's `_data/navigation_sidebar.yml`.
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 + JS tag (for dev HMR in consumer sites)
49
- - `header.html` — fixed top nav bar
50
- - `footer.html` — copyright bar
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` — conditional GA snippet (production only)
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
  });
@@ -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 toggle
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 && mobileMenu) {
17
- menuButton.addEventListener('click', function() {
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 !== '/') {
@@ -1,2 +1 @@
1
1
  {% vite_client_tag %}
2
- {% vite_javascript_tag application %}
@@ -1,10 +1,51 @@
1
- <footer class="bg-gray-50 dark:bg-gray-950 border-t border-gray-200 dark:border-white/10 py-12 transition-colors">
2
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
3
- <div class="pt-8 border-t border-gray-200 dark:border-white/10">
4
- <p class="text-sm text-gray-500 dark:text-gray-400">
5
- &copy; {{ site.time | date: '%Y' }} CalConnect<sup>SM</sup>.
6
- <a href="https://www.calconnect.org/about/policies/copyright-and-licensing" class="hover:text-gray-700 dark:hover:text-gray-300">Copyright</a>
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
+ &copy; {{ 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
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
3
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
4
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
5
- })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
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
- {% if site.favicon %}
10
- <link rel="icon" type="image/png" href="{{ site.favicon | relative_url }}" sizes="96x96" />
11
- {% endif %}
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">
@@ -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
- <a href="{{ '/' | relative_url }}" class="flex items-center">
5
- <span class="text-lg font-semibold text-gray-900 dark:text-white">{{ site.title | default: "CalConnect" }}</span>
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 %}