diff --git a/contributors.json b/contributors.json new file mode 100644 index 0000000..d8c9df6 --- /dev/null +++ b/contributors.json @@ -0,0 +1,14 @@ +[ + { + "name": "Kiya Rose", + "url": "https://kiya.cat", + "avatar": "https://avatars.githubusercontent.com/u/75678535?v=4", + "label": "Kiya Rose — Portfolio" + }, + { + "name": "Krystal Rose", + "url": "https://enby.fun", + "avatar": "https://avatars.githubusercontent.com/u/32627918?v=4", + "label": "Krystal Rose — Website" + } +] diff --git a/index.html b/index.html index 3ffd3d4..0c45d3f 100644 --- a/index.html +++ b/index.html @@ -76,6 +76,8 @@

Silly Little Tech

+ +
diff --git a/links.json b/links.json index 31cde1a..8c95d2d 100644 --- a/links.json +++ b/links.json @@ -1,10 +1,7 @@ [ { - "title": "Kiya Rose — Portfolio", - "url": "/kiya" - }, - { - "title": "Krystal Rose — Website", - "url": "/krystal" + "title": "Projects", + "url": "https://projects.sillylittle.tech", + "subtitle": "Explore our open-source projects" } ] diff --git a/script.js b/script.js index 2a8bf44..9fcbe44 100644 --- a/script.js +++ b/script.js @@ -15,6 +15,10 @@ document.addEventListener('DOMContentLoaded', () => { debugLog('Calling renderLinks') renderLinks('#linksContainer', 'links.json') + // Render contributor icons from contributors.json + debugLog('Calling renderContributors') + renderContributors('#contributorsContainer', 'contributors.json') + // Render footer from includes/footer.html debugLog('Calling renderFooter') renderFooter('#siteFooter', 'includes/footer.html') @@ -75,6 +79,47 @@ function renderLinks (containerSelector, jsonPath) { }) } +/** + * Fetches a JSON file containing contributor data and renders round profile icon links. + * Each contributor object may have: { name, url, avatar, label } + */ +function renderContributors (containerSelector, jsonPath) { + const container = document.querySelector(containerSelector) + if (!container) return + debugLog(`renderContributors: fetching ${jsonPath}`) + + fetch(jsonPath) + .then((res) => { + if (!res.ok) throw new Error(`Failed to load ${jsonPath}`) + return res.json() + }) + .then((contributors) => { + container.innerHTML = '' + debugLog(`renderContributors: got ${contributors.length} contributors`) + + contributors.forEach((contributor) => { + const anchor = document.createElement('a') + anchor.className = 'contributor-icon' + anchor.href = contributor.url || '#' + anchor.target = '_blank' + anchor.rel = 'noopener noreferrer' + anchor.setAttribute('aria-label', contributor.label || contributor.name || contributor.url || 'Contributor') + + const img = document.createElement('img') + img.src = contributor.avatar || '' + img.alt = contributor.name || '' + + anchor.appendChild(img) + container.appendChild(anchor) + }) + }) + .catch((err) => { + const msg = err?.message ?? 'unknown error' + console.error('Error rendering contributors:', err) + debugLog(`renderContributors error: ${msg}`) + }) +} + /** * Fetches an HTML file and injects its content into the target element. * Falls back to the original static footer markup already in the page if the fetch fails. diff --git a/site.css b/site.css index 510ec3d..03eba6c 100644 --- a/site.css +++ b/site.css @@ -158,6 +158,38 @@ footer.site-footer a { font-size: 14px; } +/* Contributor profile icons — displayed above the links list in the right section */ +.contributor-icons { + display: flex; + flex-direction: row; + gap: 10px; + margin-bottom: 16px; +} +.contributor-icon { + width: 44px; + height: 44px; + border-radius: 50%; + overflow: hidden; + border: 2px solid var(--border-color); + background: var(--card-bg); + display: block; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); + transition: + transform 0.18s ease, + box-shadow 0.18s ease; + text-decoration: none; +} +.contributor-icon:hover { + transform: scale(1.12); + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2); +} +.contributor-icon img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + @media (max-width: 768px) { .container { grid-template-columns: 1fr;