Improve website accessibility (#979)

* Improve website accessibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refine homepage search semantics

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Aaron Powell
2026-03-12 14:25:18 +11:00
committed by GitHub
parent eb7d223446
commit 423be2fc70
17 changed files with 293 additions and 160 deletions

View File

@@ -11,7 +11,7 @@ const initialItems = sortAgents(agentsData.items, 'title');
--- ---
<StarlightPage frontmatter={{ title: 'Custom Agents', description: 'Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Custom Agents', description: 'Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="🤖 Custom Agents" description="Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains" /> <PageHeader title="🤖 Custom Agents" description="Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains" />
<div class="page-content"> <div class="page-content">
@@ -54,7 +54,7 @@ const initialItems = sortAgents(agentsData.items, 'title');
<ContributeCTA resourceType="agents" /> <ContributeCTA resourceType="agents" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />
<EmbeddedPageData filename="agents.json" data={agentsData} /> <EmbeddedPageData filename="agents.json" data={agentsData} />

View File

@@ -4,7 +4,7 @@ import PageHeader from '../components/PageHeader.astro';
--- ---
<StarlightPage frontmatter={{ title: 'Contributors', description: 'The wonderful people who have contributed to Awesome GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Contributors', description: 'The wonderful people who have contributed to Awesome GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="🌟 Contributors" description="The wonderful people who have contributed to Awesome GitHub Copilot" /> <PageHeader title="🌟 Contributors" description="The wonderful people who have contributed to Awesome GitHub Copilot" />
<div class="page-content"> <div class="page-content">
@@ -281,7 +281,7 @@ import PageHeader from '../components/PageHeader.astro';
</div> </div>
</div> </div>
</div> </div>
</main> </div>
<style> <style>

View File

@@ -11,7 +11,7 @@ const initialItems = sortHooks(hooksData.items, 'title');
--- ---
<StarlightPage frontmatter={{ title: 'Hooks', description: 'Automated workflows triggered by Copilot coding agent events', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Hooks', description: 'Automated workflows triggered by Copilot coding agent events', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="🪝 Hooks" description="Automated workflows triggered by Copilot coding agent events" /> <PageHeader title="🪝 Hooks" description="Automated workflows triggered by Copilot coding agent events" />
<div class="page-content"> <div class="page-content">
@@ -47,7 +47,7 @@ const initialItems = sortHooks(hooksData.items, 'title');
<ContributeCTA resourceType="hooks" /> <ContributeCTA resourceType="hooks" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />

View File

@@ -6,7 +6,7 @@ const base = import.meta.env.BASE_URL;
--- ---
<StarlightPage frontmatter={{ title: 'Awesome GitHub Copilot', template: 'splash', pagefind: false, prev: false, next: false, editUrl: false }} hasSidebar={false}> <StarlightPage frontmatter={{ title: 'Awesome GitHub Copilot', template: 'splash', pagefind: false, prev: false, next: false, editUrl: false }} hasSidebar={false}>
<main id="main-content"> <div id="main-content">
<!-- Hero Section --> <!-- Hero Section -->
<section class="hero" aria-labelledby="hero-heading"> <section class="hero" aria-labelledby="hero-heading">
<div class="container"> <div class="container">
@@ -14,8 +14,18 @@ const base = import.meta.env.BASE_URL;
<p class="hero-subtitle">Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience</p> <p class="hero-subtitle">Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience</p>
<div class="hero-search"> <div class="hero-search">
<label for="global-search" class="sr-only">Search all resources</label> <label for="global-search" class="sr-only">Search all resources</label>
<input type="text" id="global-search" placeholder="Search all resources..." autocomplete="off" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="search-results"> <p id="global-search-help" class="sr-only">
<div id="search-results" class="search-results hidden" role="listbox" aria-label="Search results"></div> Type at least two characters to show matching resources, then press the Down Arrow key to move into the results.
</p>
<p id="global-search-status" class="sr-only" aria-live="polite"></p>
<input
type="text"
id="global-search"
placeholder="Search all resources..."
autocomplete="off"
aria-describedby="global-search-help global-search-status"
>
<div id="search-results" class="search-results hidden" aria-label="Search results"></div>
</div> </div>
</div> </div>
</section> </section>
@@ -92,7 +102,7 @@ const base = import.meta.env.BASE_URL;
</div> </div>
</section> </section>
</main> </div>
<Modal /> <Modal />

View File

@@ -11,7 +11,7 @@ const initialItems = sortInstructions(instructionsData.items, 'title');
--- ---
<StarlightPage frontmatter={{ title: 'Instructions', description: 'Coding standards and best practices for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Instructions', description: 'Coding standards and best practices for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="📋 Instructions" description="Coding standards and best practices for GitHub Copilot" /> <PageHeader title="📋 Instructions" description="Coding standards and best practices for GitHub Copilot" />
<div class="page-content"> <div class="page-content">
@@ -43,7 +43,7 @@ const initialItems = sortInstructions(instructionsData.items, 'title');
<ContributeCTA resourceType="instructions" /> <ContributeCTA resourceType="instructions" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />
<EmbeddedPageData filename="instructions.json" data={instructionsData} /> <EmbeddedPageData filename="instructions.json" data={instructionsData} />

View File

@@ -11,7 +11,7 @@ const initialItems = pluginsData.items;
--- ---
<StarlightPage frontmatter={{ title: 'Plugins', description: 'Curated plugins of agents, hooks, and skills for specific workflows', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Plugins', description: 'Curated plugins of agents, hooks, and skills for specific workflows', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="🔌 Plugins" description="Curated plugins of agents, hooks, and skills for specific workflows" /> <PageHeader title="🔌 Plugins" description="Curated plugins of agents, hooks, and skills for specific workflows" />
<div class="page-content"> <div class="page-content">
@@ -45,7 +45,7 @@ const initialItems = pluginsData.items;
<ContributeCTA resourceType="plugins" /> <ContributeCTA resourceType="plugins" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />
<EmbeddedPageData filename="plugins.json" data={pluginsData} /> <EmbeddedPageData filename="plugins.json" data={pluginsData} />

View File

@@ -11,7 +11,7 @@ const initialItems = sortSkills(skillsData.items, 'title');
--- ---
<StarlightPage frontmatter={{ title: 'Skills', description: 'Self-contained agent skills with instructions and bundled resources', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Skills', description: 'Self-contained agent skills with instructions and bundled resources', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="⚡ Skills" description="Self-contained agent skills with instructions and bundled resources" /> <PageHeader title="⚡ Skills" description="Self-contained agent skills with instructions and bundled resources" />
<div class="page-content"> <div class="page-content">
@@ -49,7 +49,7 @@ const initialItems = sortSkills(skillsData.items, 'title');
<ContributeCTA resourceType="skills" /> <ContributeCTA resourceType="skills" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />

View File

@@ -13,7 +13,7 @@ const initialItems = toolsData.items.map((item) => ({
--- ---
<StarlightPage frontmatter={{ title: 'Tools', description: 'MCP servers and developer tools for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Tools', description: 'MCP servers and developer tools for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="🔧 Tools" description="MCP servers and developer tools for GitHub Copilot" /> <PageHeader title="🔧 Tools" description="MCP servers and developer tools for GitHub Copilot" />
<div class="page-content"> <div class="page-content">
@@ -55,7 +55,7 @@ const initialItems = toolsData.items.map((item) => ({
<ContributeCTA resourceType="tools" /> <ContributeCTA resourceType="tools" />
</div> </div>
</div> </div>
</main> </div>
<EmbeddedPageData filename="tools.json" data={toolsData} /> <EmbeddedPageData filename="tools.json" data={toolsData} />

View File

@@ -11,7 +11,7 @@ const initialItems = sortWorkflows(workflowsData.items, 'title');
--- ---
<StarlightPage frontmatter={{ title: 'Agentic Workflows', description: 'AI-powered repository automations that run coding agents in GitHub Actions', template: 'splash', prev: false, next: false, editUrl: false }}> <StarlightPage frontmatter={{ title: 'Agentic Workflows', description: 'AI-powered repository automations that run coding agents in GitHub Actions', template: 'splash', prev: false, next: false, editUrl: false }}>
<main id="main-content"> <div id="main-content">
<PageHeader title="⚡ Agentic Workflows" description=""> <PageHeader title="⚡ Agentic Workflows" description="">
AI-powered repository automations that run coding agents in <a href="https://gh.io/gh-aw" target="_blank" rel="noopener">GitHub Actions</a> AI-powered repository automations that run coding agents in <a href="https://gh.io/gh-aw" target="_blank" rel="noopener">GitHub Actions</a>
</PageHeader> </PageHeader>
@@ -45,7 +45,7 @@ const initialItems = sortWorkflows(workflowsData.items, 'title');
<ContributeCTA resourceType="workflows" /> <ContributeCTA resourceType="workflows" />
</div> </div>
</div> </div>
</main> </div>
<Modal /> <Modal />
<EmbeddedPageData filename="workflows.json" data={workflowsData} /> <EmbeddedPageData filename="workflows.json" data={workflowsData} />

View File

@@ -61,44 +61,46 @@ export function renderAgentsHtml(
: escapeHtml(item.title); : escapeHtml(item.title);
return ` return `
<div class="resource-item" data-path="${escapeHtml(item.path)}"> <article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml( <div class="resource-title">${titleHtml}</div>
item.description || "No description" <div class="resource-description">${escapeHtml(
)}</div> item.description || "No description"
<div class="resource-meta"> )}</div>
${ <div class="resource-meta">
item.model ${
? `<span class="resource-tag tag-model">${escapeHtml( item.model
item.model ? `<span class="resource-tag tag-model">${escapeHtml(
)}</span>` item.model
: "" )}</span>`
} : ""
${ }
item.tools ${
?.slice(0, 3) item.tools
.map( ?.slice(0, 3)
(tool) => .map(
`<span class="resource-tag">${escapeHtml(tool)}</span>` (tool) =>
) `<span class="resource-tag">${escapeHtml(tool)}</span>`
.join("") || "" )
} .join("") || ""
${ }
item.tools && item.tools.length > 3 ${
? `<span class="resource-tag">+${ item.tools && item.tools.length > 3
item.tools.length - 3 ? `<span class="resource-tag">+${
} more</span>` item.tools.length - 3
: "" } more</span>`
} : ""
${ }
item.hasHandoffs ${
? `<span class="resource-tag tag-handoffs">handoffs</span>` item.hasHandoffs
: "" ? `<span class="resource-tag tag-handoffs">handoffs</span>`
} : ""
${getLastUpdatedHtml(item.lastUpdated)} }
${getLastUpdatedHtml(item.lastUpdated)}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
${getInstallDropdownHtml(resourceType, item.path, true)} ${getInstallDropdownHtml(resourceType, item.path, true)}
${getActionButtonsHtml(item.path, true)} ${getActionButtonsHtml(item.path, true)}
@@ -108,7 +110,7 @@ export function renderAgentsHtml(
GitHub GitHub
</a> </a>
</div> </div>
</div> </article>
`; `;
}) })
.join(""); .join("");

View File

@@ -59,41 +59,43 @@ export function renderHooksHtml(
: escapeHtml(item.title); : escapeHtml(item.title);
return ` return `
<div class="resource-item" data-path="${escapeHtml( <article class="resource-item" data-path="${escapeHtml(
item.readmeFile item.readmeFile
)}" data-hook-id="${escapeHtml(item.id)}"> )}" data-hook-id="${escapeHtml(item.id)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml( <div class="resource-title">${titleHtml}</div>
item.description || "No description" <div class="resource-description">${escapeHtml(
)}</div> item.description || "No description"
<div class="resource-meta"> )}</div>
${item.hooks <div class="resource-meta">
.map( ${item.hooks
(hook) => .map(
`<span class="resource-tag tag-hook">${escapeHtml( (hook) =>
hook `<span class="resource-tag tag-hook">${escapeHtml(
)}</span>` hook
) )}</span>`
.join("")} )
${item.tags .join("")}
.map( ${item.tags
(tag) => .map(
`<span class="resource-tag tag-tag">${escapeHtml( (tag) =>
tag `<span class="resource-tag tag-tag">${escapeHtml(
)}</span>` tag
) )}</span>`
.join("")} )
${ .join("")}
item.assets.length > 0 ${
? `<span class="resource-tag tag-assets">${ item.assets.length > 0
item.assets.length ? `<span class="resource-tag tag-assets">${
} asset${item.assets.length === 1 ? "" : "s"}</span>` item.assets.length
: "" } asset${item.assets.length === 1 ? "" : "s"}</span>`
} : ""
${getLastUpdatedHtml(item.lastUpdated)} }
${getLastUpdatedHtml(item.lastUpdated)}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
<button class="btn btn-primary download-hook-btn" data-hook-id="${escapeHtml( <button class="btn btn-primary download-hook-btn" data-hook-id="${escapeHtml(
item.id item.id
@@ -108,7 +110,7 @@ export function renderHooksHtml(
item.path item.path
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a> )}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
</div> </div>
</div> </article>
`; `;
}) })
.join(""); .join("");

View File

@@ -55,43 +55,120 @@ export async function initHomepage(): Promise<void> {
const resultsDiv = document.getElementById('search-results'); const resultsDiv = document.getElementById('search-results');
if (searchInput && resultsDiv) { if (searchInput && resultsDiv) {
const statusEl = document.getElementById("global-search-status");
const hideResults = (): void => {
resultsDiv.classList.add("hidden");
};
const showResults = (): void => {
resultsDiv.classList.remove("hidden");
};
const getResultButtons = (): HTMLButtonElement[] =>
Array.from(
resultsDiv.querySelectorAll<HTMLButtonElement>(".search-result")
);
const openResult = (resultEl: HTMLElement): void => {
const path = resultEl.dataset.path;
const type = resultEl.dataset.type;
if (path && type) {
hideResults();
openFileModal(path, type);
}
};
searchInput.addEventListener('input', debounce(() => { searchInput.addEventListener('input', debounce(() => {
const query = searchInput.value.trim(); const query = searchInput.value.trim();
if (query.length < 2) { if (query.length < 2) {
resultsDiv.classList.add('hidden'); resultsDiv.innerHTML = '';
if (statusEl) {
statusEl.textContent = '';
}
hideResults();
return; return;
} }
const results = search.search(query).slice(0, 10); const results = search.search(query).slice(0, 10);
if (results.length === 0) { if (results.length === 0) {
resultsDiv.innerHTML = '<div class="search-result-empty">No results found</div>'; resultsDiv.innerHTML = '<div class="search-result-empty">No results found</div>';
if (statusEl) {
statusEl.textContent = 'No results found.';
}
} else { } else {
resultsDiv.innerHTML = results.map(item => ` resultsDiv.innerHTML = results.map(item => `
<div class="search-result" data-path="${escapeHtml(item.path)}" data-type="${escapeHtml(item.type)}"> <button type="button" class="search-result" data-path="${escapeHtml(item.path)}" data-type="${escapeHtml(item.type)}">
<span class="search-result-type">${getResourceIcon(item.type)}</span> <span class="search-result-type">${getResourceIcon(item.type)}</span>
<div> <div>
<div class="search-result-title">${search.highlight(item.title, query)}</div> <div class="search-result-title">${search.highlight(item.title, query)}</div>
<div class="search-result-description">${truncate(item.description, 60)}</div> <div class="search-result-description">${truncate(item.description, 60)}</div>
</div> </div>
</div> </button>
`).join(''); `).join('');
// Add click handlers if (statusEl) {
resultsDiv.querySelectorAll('.search-result').forEach(el => { statusEl.textContent = `${results.length} result${results.length === 1 ? '' : 's'} available.`;
}
getResultButtons().forEach((el, index, buttons) => {
el.addEventListener('click', () => { el.addEventListener('click', () => {
const path = (el as HTMLElement).dataset.path; openResult(el);
const type = (el as HTMLElement).dataset.type; });
if (path && type) openFileModal(path, type);
el.addEventListener("keydown", (event) => {
switch (event.key) {
case "ArrowDown":
event.preventDefault();
buttons[(index + 1) % buttons.length]?.focus();
break;
case "ArrowUp":
event.preventDefault();
if (index === 0) {
searchInput.focus();
} else {
buttons[index - 1]?.focus();
}
break;
case "Home":
event.preventDefault();
buttons[0]?.focus();
break;
case "End":
event.preventDefault();
buttons[buttons.length - 1]?.focus();
break;
case "Escape":
event.preventDefault();
hideResults();
searchInput.focus();
break;
}
}); });
}); });
} }
resultsDiv.classList.remove('hidden');
showResults();
}, 200)); }, 200));
searchInput.addEventListener("keydown", (event) => {
if (event.key === "ArrowDown") {
const firstResult = getResultButtons()[0];
if (firstResult) {
event.preventDefault();
firstResult.focus();
}
}
if (event.key === "Escape") {
hideResults();
}
});
// Close results when clicking outside // Close results when clicking outside
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (!searchInput.contains(e.target as Node) && !resultsDiv.contains(e.target as Node)) { if (!searchInput.contains(e.target as Node) && !resultsDiv.contains(e.target as Node)) {
resultsDiv.classList.add('hidden'); hideResults();
} }
}); });
} }

View File

@@ -61,17 +61,19 @@ export function renderInstructionsHtml(
: escapeHtml(item.title); : escapeHtml(item.title);
return ` return `
<div class="resource-item" data-path="${escapeHtml(item.path)}"> <article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div> <div class="resource-title">${titleHtml}</div>
<div class="resource-meta"> <div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''} <div class="resource-meta">
${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''} ${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''}
${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''} ${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''}
${getLastUpdatedHtml(item.lastUpdated)} ${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''}
${getLastUpdatedHtml(item.lastUpdated)}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
${getInstallDropdownHtml('instructions', item.path, true)} ${getInstallDropdownHtml('instructions', item.path, true)}
${getActionButtonsHtml(item.path, true)} ${getActionButtonsHtml(item.path, true)}
@@ -79,7 +81,7 @@ export function renderInstructionsHtml(
GitHub GitHub
</a> </a>
</div> </div>
</div> </article>
`; `;
}) })
.join(''); .join('');

View File

@@ -70,21 +70,23 @@ export function renderPluginsHtml(
: escapeHtml(item.name); : escapeHtml(item.name);
return ` return `
<div class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}"> <article class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div> <div class="resource-title">${titleHtml}</div>
<div class="resource-meta"> <div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
${metaTag} <div class="resource-meta">
${authorTag} ${metaTag}
${item.tags?.slice(0, 4).map((tag) => `<span class="resource-tag">${escapeHtml(tag)}</span>`).join('') || ''} ${authorTag}
${item.tags && item.tags.length > 4 ? `<span class="resource-tag">+${item.tags.length - 4} more</span>` : ''} ${item.tags?.slice(0, 4).map((tag) => `<span class="resource-tag">${escapeHtml(tag)}</span>`).join('') || ''}
${item.tags && item.tags.length > 4 ? `<span class="resource-tag">+${item.tags.length - 4} more</span>` : ''}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
<a href="${githubHref}" class="btn btn-secondary" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()" title="${isExternal ? 'View repository' : 'View on GitHub'}">${isExternal ? 'Repository' : 'GitHub'}</a> <a href="${githubHref}" class="btn btn-secondary" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()" title="${isExternal ? 'View repository' : 'View on GitHub'}">${isExternal ? 'Repository' : 'GitHub'}</a>
</div> </div>
</div> </article>
`; `;
}) })
.join(''); .join('');

View File

@@ -65,31 +65,33 @@ export function renderSkillsHtml(
: escapeHtml(item.title); : escapeHtml(item.title);
return ` return `
<div class="resource-item" data-path="${escapeHtml( <article class="resource-item" data-path="${escapeHtml(
item.skillFile item.skillFile
)}" data-skill-id="${escapeHtml(item.id)}"> )}" data-skill-id="${escapeHtml(item.id)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml( <div class="resource-title">${titleHtml}</div>
item.description || "No description" <div class="resource-description">${escapeHtml(
)}</div> item.description || "No description"
<div class="resource-meta"> )}</div>
<span class="resource-tag tag-category">${escapeHtml( <div class="resource-meta">
item.category <span class="resource-tag tag-category">${escapeHtml(
)}</span> item.category
${ )}</span>
item.hasAssets ${
? `<span class="resource-tag tag-assets">${ item.hasAssets
item.assetCount ? `<span class="resource-tag tag-assets">${
} asset${item.assetCount === 1 ? "" : "s"}</span>` item.assetCount
: "" } asset${item.assetCount === 1 ? "" : "s"}</span>`
} : ""
<span class="resource-tag">${item.files.length} file${ }
item.files.length === 1 ? "" : "s" <span class="resource-tag">${item.files.length} file${
}</span> item.files.length === 1 ? "" : "s"
${getLastUpdatedHtml(item.lastUpdated)} }</span>
${getLastUpdatedHtml(item.lastUpdated)}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
<button class="btn btn-primary download-skill-btn" data-skill-id="${escapeHtml( <button class="btn btn-primary download-skill-btn" data-skill-id="${escapeHtml(
item.id item.id
@@ -104,7 +106,7 @@ export function renderSkillsHtml(
item.path item.path
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a> )}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
</div> </div>
</div> </article>
`; `;
}) })
.join(""); .join("");

View File

@@ -56,20 +56,22 @@ export function renderWorkflowsHtml(
: escapeHtml(item.title); : escapeHtml(item.title);
return ` return `
<div class="resource-item" data-path="${escapeHtml(item.path)}"> <article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
<div class="resource-info"> <button type="button" class="resource-preview">
<div class="resource-title">${titleHtml}</div> <div class="resource-info">
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div> <div class="resource-title">${titleHtml}</div>
<div class="resource-meta"> <div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')} <div class="resource-meta">
${getLastUpdatedHtml(item.lastUpdated)} ${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')}
${getLastUpdatedHtml(item.lastUpdated)}
</div>
</div> </div>
</div> </button>
<div class="resource-actions"> <div class="resource-actions">
${getActionButtonsHtml(item.path)} ${getActionButtonsHtml(item.path)}
<a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a> <a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
</div> </div>
</div> </article>
`; `;
}) })
.join(''); .join('');

View File

@@ -275,11 +275,16 @@ body:has(#main-content) {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 14px; gap: 14px;
width: 100%;
padding: 14px 18px; padding: 14px 18px;
background: transparent;
border: 0;
cursor: pointer; cursor: pointer;
border-bottom: 1px solid var(--color-glass-border); border-bottom: 1px solid var(--color-glass-border);
transition: all var(--transition); transition: all var(--transition);
text-align: left; text-align: left;
color: inherit;
font: inherit;
} }
.search-result:last-child, .search-result:last-child,
@@ -288,10 +293,16 @@ body:has(#main-content) {
} }
.search-result:hover, .search-result:hover,
.search-result:focus-visible,
.search-result-item:hover { .search-result-item:hover {
background: var(--color-bg-tertiary); background: var(--color-bg-tertiary);
} }
.search-result:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: -2px;
}
.search-result-type { .search-result-type {
font-size: 20px; font-size: 20px;
flex-shrink: 0; flex-shrink: 0;
@@ -1464,7 +1475,6 @@ body:has(#main-content) {
justify-content: space-between; justify-content: space-between;
gap: 20px; gap: 20px;
transition: all var(--transition); transition: all var(--transition);
cursor: pointer;
position: relative; position: relative;
} }
@@ -1481,21 +1491,45 @@ body:has(#main-content) {
transition: opacity var(--transition), left var(--transition); transition: opacity var(--transition), left var(--transition);
} }
.resource-item:hover { .resource-item:hover,
.resource-item:focus-within {
background: var(--color-card-hover); background: var(--color-card-hover);
transform: translateX(4px); transform: translateX(4px);
box-shadow: var(--shadow); box-shadow: var(--shadow);
border-radius: 0px var(--border-radius-lg) var(--border-radius-lg) 0px; border-radius: 0px var(--border-radius-lg) var(--border-radius-lg) 0px;
} }
.resource-item:hover::before { .resource-item:hover::before,
.resource-item:focus-within::before {
opacity: 1; opacity: 1;
left: -4px; left: -4px;
} }
.resource-preview {
flex: 1;
min-width: 0;
display: block;
width: 100%;
padding: 0;
margin: 0;
background: transparent;
border: 0;
color: inherit;
font: inherit;
text-align: left;
cursor: pointer;
}
.resource-preview:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 4px;
border-radius: var(--border-radius);
}
.resource-info { .resource-info {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
width: 100%;
} }
.resource-title { .resource-title {