Skip to content

Commit 7e2a834

Browse files
feat: QA fixes batch — copy cite, 404, Schema.org, keyboard nav, empty state (#129)
Lighthouse CI fix: - Add checkout step so LHCI can determine git hash Change history empty state (Fix 3): - Styled informative message instead of plain text - Warm background, centered, explains pipeline auto-detection Copy citation button (Feature 6): - "Copy cite" button next to Print in metadata toolbar - Uses clipboard API with "Copied!" feedback Schema.org structured data (Feature 5): - JSON-LD Legislation schema on all section pages - Includes legislationIdentifier, jurisdiction, legalForce, publisher Custom 404 page (Feature 9): - "Section not found" with browse link and search hint - Matches site branding Keyboard navigation (Feature 8): - j/k keys navigate prev/next sections - Keyboard hints shown on desktop only - Skips when typing in inputs Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9fd167e commit 7e2a834

4 files changed

Lines changed: 76 additions & 2 deletions

File tree

.github/workflows/deploy-site.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ jobs:
8484
needs: deploy
8585
runs-on: ubuntu-latest
8686
steps:
87+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
88+
8789
- name: Lighthouse audit
8890
uses: treosh/lighthouse-ci-action@2f8dda6cf4de7d73b29853c3f29e73a01e297bd8 # v12.1.0
8991
with:

apps/web/src/components/DiffViewer.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,10 @@
490490
<!-- Change history -->
491491
<h3 class="mb-2 text-base font-semibold text-gray-800 dark:text-gray-200">Change History</h3>
492492
{#if commits.length === 0}
493-
<p class="text-gray-500">No history yet for this section.</p>
493+
<div class="rounded-lg bg-warm-gray p-4 text-center text-[13px] leading-relaxed text-gray-500 dark:bg-gray-800/50 dark:text-gray-400">
494+
<p>No amendments tracked yet for this section.</p>
495+
<p class="mt-1 text-[11px]">Change history will appear here automatically when the pipeline detects modifications between US Code release points.</p>
496+
</div>
494497
{:else}
495498
{#if tags.length === 0}
496499
<p class="mb-3 rounded bg-teal/10 p-2 text-xs text-teal-700 dark:text-teal-300">Version timeline available when release point tags are published.</p>

apps/web/src/pages/404.astro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
import BaseLayout from '../layouts/BaseLayout.astro';
3+
const base = import.meta.env.BASE_URL;
4+
---
5+
6+
<BaseLayout title="Page not found">
7+
<div class="not-prose mx-auto max-w-md py-16 text-center font-sans">
8+
<h1 class="mb-3 text-2xl font-medium text-navy dark:text-amber">Section not found</h1>
9+
<p class="mb-6 text-gray-500 leading-relaxed">
10+
The section you're looking for may have been renumbered, repealed,
11+
or the URL may be incorrect.
12+
</p>
13+
<a
14+
href={`${base}browse/`}
15+
class="inline-block rounded-lg bg-navy px-5 py-2.5 text-sm font-medium text-white transition-colors hover:bg-navy-dark dark:bg-teal dark:hover:bg-teal/80"
16+
>
17+
Browse all titles &rarr;
18+
</a>
19+
<p class="mt-8 text-sm text-gray-400">Or use the search bar above to find what you need.</p>
20+
</div>
21+
</BaseLayout>

apps/web/src/pages/statute/[...slug].astro

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,23 @@ const readingTimeMin = Math.max(1, Math.round(wordCount / 200));
105105

106106
<BaseLayout
107107
title={entry.data.title}
108-
description={`${classification} - current through ${current_through}`}
108+
description={`Full text of ${classification} with change history. Current through ${current_through}.`}
109109
>
110+
<!-- Schema.org Legislation structured data -->
111+
<script type="application/ld+json" set:html={JSON.stringify({
112+
"@context": "https://schema.org",
113+
"@type": "Legislation",
114+
"name": classification,
115+
"headline": entry.data.title.replace(/^Section \S+ - /, ''),
116+
"legislationIdentifier": classification,
117+
"legislationJurisdiction": "US",
118+
"legislationLegalForce": status === 'repealed' ? 'NotInForce' : 'InForce',
119+
"isPartOf": { "@type": "Legislation", "name": "United States Code", "legislationIdentifier": "USC" },
120+
"url": `https://civic-source.github.io${base}statute/${entry.id}/`,
121+
"dateModified": generated_at?.split('T')[0] ?? "2026-03-30",
122+
"publisher": { "@type": "Organization", "name": "Office of the Law Revision Counsel", "url": "https://uscode.house.gov/" }
123+
})} />
124+
110125
<!-- Breadcrumbs -->
111126
<Breadcrumbs items={[
112127
{ label: 'Home', href: base },
@@ -155,6 +170,14 @@ const readingTimeMin = Math.max(1, Math.round(wordCount / 200));
155170
>
156171
Print
157172
</button>
173+
<button
174+
class="rounded bg-gray-100 px-2 py-1 text-gray-600 hover:bg-gray-200 transition-colors dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
175+
aria-label="Copy citation to clipboard"
176+
data-citation={classification}
177+
onclick="navigator.clipboard.writeText(this.dataset.citation).then(()=>{this.textContent='Copied!';setTimeout(()=>this.textContent='Copy cite',2000)}).catch(()=>{})"
178+
>
179+
Copy cite
180+
</button>
158181
</div>
159182
</div>
160183

@@ -382,4 +405,29 @@ const readingTimeMin = Math.max(1, Math.round(wordCount / 200));
382405
});
383406
})();
384407
</script>
408+
409+
<!-- Keyboard shortcuts: j/k navigate sections -->
410+
<script is:inline>
411+
document.addEventListener('keydown', function (e) {
412+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
413+
var nav = document.querySelector('nav[aria-label="Section navigation"]');
414+
if (!nav) return;
415+
var links = nav.querySelectorAll('a');
416+
if (e.key === 'k' || (e.key === 'ArrowLeft' && !e.metaKey)) {
417+
if (links[0]) window.location.href = links[0].href;
418+
} else if (e.key === 'j' || (e.key === 'ArrowRight' && !e.metaKey)) {
419+
if (links[1]) window.location.href = links[1].href;
420+
else if (links[0] && links.length === 1) window.location.href = links[0].href;
421+
}
422+
});
423+
</script>
424+
425+
<!-- Keyboard hints (desktop only) -->
426+
<div class="not-prose mt-4 text-center text-[11px] text-gray-300 dark:text-gray-700 hidden lg:block" aria-hidden="true">
427+
<kbd class="rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] dark:border-gray-700 dark:bg-gray-800">j</kbd>
428+
<kbd class="rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] dark:border-gray-700 dark:bg-gray-800">k</kbd>
429+
navigate sections
430+
<kbd class="ml-2 rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] dark:border-gray-700 dark:bg-gray-800">/</kbd>
431+
search
432+
</div>
385433
</BaseLayout>

0 commit comments

Comments
 (0)