Skip to content

Commit 436a8a0

Browse files
committed
fix(seo): improve comparison page trust and accessibility
1 parent c2ffdbf commit 436a8a0

17 files changed

Lines changed: 244 additions & 186 deletions

File tree

src/app/[locale]/clis/comparison/page.client.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ export default function CLIComparisonPageClient({ locale: _locale }: Props) {
318318
columns={columns}
319319
itemLinkPrefix={`/clis`}
320320
nameColumnLabel={tShared('labels.name')}
321+
caption={tPage('clis.title')}
322+
scrollHint={tPage('table.scrollHint')}
321323
/>
322324
</div>
323325
</section>

src/app/[locale]/extensions/comparison/page.client.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ export default function ExtensionComparisonPageClient({ locale: _locale }: Props
301301
columns={columns}
302302
itemLinkPrefix={`/extensions`}
303303
nameColumnLabel={tShared('labels.name')}
304+
caption={tPage('extensions.title')}
305+
scrollHint={tPage('table.scrollHint')}
304306
/>
305307
</div>
306308
</section>

src/app/[locale]/ides/comparison/page.client.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ export default function IDEComparisonPageClient({ locale: _locale }: Props) {
315315
columns={columns}
316316
itemLinkPrefix={`/ides`}
317317
nameColumnLabel={tShared('labels.name')}
318+
caption={tPage('ides.title')}
319+
scrollHint={tPage('table.scrollHint')}
318320
/>
319321
</div>
320322
</section>

src/components/product/ComparisonTable.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export interface ComparisonTableProps {
2222
itemIdKey?: string
2323
stickyTopOffset?: number
2424
nameColumnLabel?: string
25+
caption: string
26+
scrollHint: string
2527
}
2628

2729
export default function ComparisonTable({
@@ -32,6 +34,8 @@ export default function ComparisonTable({
3234
itemIdKey = 'id',
3335
stickyTopOffset,
3436
nameColumnLabel = 'Name',
37+
caption,
38+
scrollHint,
3539
}: ComparisonTableProps) {
3640
const containerRef = useRef<HTMLDivElement>(null)
3741
const tableRef = useRef<HTMLTableElement>(null)
@@ -119,6 +123,7 @@ export default function ComparisonTable({
119123
{/* Fixed header wrapper with clipping */}
120124
{isFixed && containerRef.current && (
121125
<div
126+
aria-hidden="true"
122127
className="fixed z-30 overflow-hidden"
123128
style={{
124129
top: `${calculatedOffset}px`,
@@ -170,9 +175,13 @@ export default function ComparisonTable({
170175
</div>
171176
)}
172177

178+
<p className="mb-[var(--spacing-xs)] text-xs text-[var(--color-text-muted)] md:hidden">
179+
{scrollHint}
180+
</p>
173181
<div ref={containerRef} className="w-full overflow-x-auto relative">
174182
<div className="min-w-[1200px]">
175183
<table ref={tableRef} className="w-full border-collapse">
184+
<caption className="sr-only">{caption}</caption>
176185
{/* Placeholder to prevent content jump when thead becomes fixed */}
177186
{isFixed && (
178187
<thead aria-hidden="true" style={{ visibility: 'hidden' }}>
@@ -215,12 +224,16 @@ export default function ComparisonTable({
215224
className={!isFixed ? 'relative z-10 bg-[var(--color-bg)]' : 'sr-only'}
216225
>
217226
<tr className="border-b border-t border-[var(--color-border-strong)]">
218-
<th className="sticky left-0 z-50 bg-[var(--color-bg)] px-[var(--spacing-md)] py-[var(--spacing-sm)] text-left text-sm font-semibold uppercase tracking-wider text-[var(--color-text-muted)] border-r border-[var(--color-border)]">
227+
<th
228+
scope="col"
229+
className="sticky left-0 z-50 bg-[var(--color-bg)] px-[var(--spacing-md)] py-[var(--spacing-sm)] text-left text-sm font-semibold uppercase tracking-wider text-[var(--color-text-muted)] border-r border-[var(--color-border)]"
230+
>
219231
{nameColumnLabel}
220232
</th>
221233
{columns.map(column => (
222234
<th
223235
key={column.key}
236+
scope="col"
224237
className="pl-[var(--spacing-md)] pr-0 py-[var(--spacing-sm)] text-left text-sm font-semibold uppercase tracking-wider text-[var(--color-text-muted)] whitespace-nowrap"
225238
style={{
226239
...(column.minWidth && { minWidth: column.minWidth }),
@@ -241,7 +254,10 @@ export default function ComparisonTable({
241254
index % 2 === 0 ? 'bg-[var(--color-bg)]' : 'bg-[var(--color-hover)]'
242255
}`}
243256
>
244-
<td className="sticky left-0 z-10 bg-inherit px-[var(--spacing-md)] py-[var(--spacing-sm)] font-medium border-r border-[var(--color-border)] whitespace-nowrap">
257+
<th
258+
scope="row"
259+
className="sticky left-0 z-10 bg-inherit px-[var(--spacing-md)] py-[var(--spacing-sm)] text-left font-medium border-r border-[var(--color-border)] whitespace-nowrap"
260+
>
245261
<div className="inline-flex items-center gap-[var(--spacing-xs)]">
246262
<Link
247263
href={`${itemLinkPrefix}/${item[itemIdKey] as string}`}
@@ -251,7 +267,7 @@ export default function ComparisonTable({
251267
</Link>
252268
{item.deprecated === true && <DeprecatedBadge />}
253269
</div>
254-
</td>
270+
</th>
255271
{columns.map(column => (
256272
<td
257273
key={column.key}

src/lib/metadata/generators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ export async function generateComparisonMetadata(
378378
const tPage = await getTranslations({ locale, namespace: 'pages.comparison' })
379379

380380
// Build title and description using category-specific translations
381-
const title = `${tPage(`${category}.title`)} ${METADATA_DEFAULTS.currentYear}`
381+
const title = tPage(`${category}.title`)
382382
const description = tPage(`${category}.subtitle`)
383383

384384
// Build keywords

translations/de/pages/comparison.json

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"title": "Funktionieren sie unter Windows, macOS und Linux?"
3939
}
4040
],
41-
"intro": "Vergleichen Sie {count} KI-Coding-CLIs anhand derselben Kriterien: Plattformen, Preise, Lizenz, Projektaktivität und offizielle Quellen. Definieren Sie Ihre Anforderungen, erstellen Sie eine Auswahlliste und prüfen Sie veränderliche Produktdetails direkt beim Anbieter.",
41+
"intro": "Vergleichen Sie {count} KI-Coding-CLIs anhand derselben Kriterien: Plattformen, Preise, Lizenz, GitHub-Sterne und Projektlinks sowie offizielle Quellen. Definieren Sie Ihre Anforderungen, erstellen Sie eine Auswahlliste und prüfen Sie veränderliche Produktdetails direkt beim Anbieter.",
4242
"profiles": [
4343
{
4444
"description": "Plattformen, Preise und offizielle Quellen des Terminal-Agenten von Anthropic.",
@@ -61,8 +61,8 @@
6161
"title": "OpenCode"
6262
}
6363
],
64-
"profilesIntro": "Diese Profile sind Ausgangspunkte, keine Rangliste. Jeder Eintrag zeigt aktuelle Manifestdaten und Primärquellen.",
65-
"profilesTitle": "Repräsentative CLI-Profile prüfen",
64+
"profilesIntro": "Die folgenden Links führen direkt zu bereits in der Tabelle aufgeführten Produktdatensätzen; sie sind keine Rangliste oder Empfehlung. Öffnen Sie jeden Datensatz, um Manifestdaten und Primärquellen zu prüfen.",
65+
"profilesTitle": "CLI-Produktprofile öffnen",
6666
"shortlistIntro": "Nutzen Sie einen einheitlichen Prozess, damit Popularität nicht die Workflow-Eignung ersetzt.",
6767
"shortlistTitle": "CLI-Auswahl in drei Schritten",
6868
"steps": [
@@ -108,7 +108,7 @@
108108
},
109109
{
110110
"title": "Lizenz und Wartung",
111-
"description": "Nutzen Sie Lizenz, GitHub-Aktivität und das Veraltet-Kennzeichen, um aktive Open-Source-Projekte von proprietären oder eingestellten Produkten zu unterscheiden."
111+
"description": "Nutzen Sie Lizenz, GitHub-Sterne und Projektlinks sowie das Veraltet-Kennzeichen, um Open-Source-Projekte von proprietären oder eingestellten Produkten zu unterscheiden."
112112
},
113113
{
114114
"title": "Prüfpfad",
@@ -138,12 +138,12 @@
138138
"description": "Die Kompatibilität variiert. Prüfen Sie die Spalte „Unterstützte IDEs“ und bestätigen Sie anschließend die genaue Editor-Version und Betriebssystemanforderungen im offiziellen Marketplace oder in der Installationsanleitung."
139139
}
140140
],
141-
"intro": "Vergleichen Sie {count} AI-Coding-Erweiterungen anhand derselben Signale: unterstützte IDEs, Preise, Lizenz, Projektaktivität, Lebenszyklus und offizielle Quellen. Filtern Sie zuerst nach Kompatibilität und prüfen Sie veränderliche Details vor der Installation an der Quelle.",
141+
"intro": "Vergleichen Sie {count} AI-Coding-Erweiterungen anhand derselben Signale: unterstützte IDEs, Preise, Lizenz, GitHub-Sterne und Projektlinks, Lebenszyklus und offizielle Quellen. Filtern Sie zuerst nach Kompatibilität und prüfen Sie veränderliche Details vor der Installation an der Quelle.",
142142
"profiles": [
143143
{
144144
"title": "GitHub Copilot",
145145
"href": "/extensions/github-copilot",
146-
"description": "Prüfen Sie unterstützte IDEs, Preise, Lizenz, Projektaktivität und offizielle Quellen."
146+
"description": "Prüfen Sie unterstützte IDEs, Preise, Lizenz, GitHub-Sterne und Projektlinks sowie offizielle Quellen."
147147
},
148148
{
149149
"title": "Cline",
@@ -161,8 +161,8 @@
161161
"description": "Prüfen Sie Host-Kompatibilität, Preise, Projektlinks und offizielle Quellen."
162162
}
163163
],
164-
"profilesIntro": "Diese Profile sind Startpunkte, keine Rangliste. Öffnen Sie jeden Datensatz, um aktuelle Manifest-Daten, Lebenszyklus und Primärquellen zu prüfen.",
165-
"profilesTitle": "Repräsentative Erweiterungsprofile prüfen",
164+
"profilesIntro": "Die folgenden Links führen direkt zu bereits in der Tabelle aufgeführten Produktdatensätzen; sie sind keine Rangliste oder Empfehlung. Öffnen Sie jeden Datensatz, um Manifestdaten, Lebenszyklusstatus und Primärquellen zu prüfen.",
165+
"profilesTitle": "Erweiterungsprofile öffnen",
166166
"shortlistIntro": "Nutzen Sie einen einheitlichen Ablauf, damit Popularität nicht wichtiger wird als Editor-Kompatibilität und Wartungsstatus.",
167167
"shortlistTitle": "Erweiterungen in drei Schritten auswählen",
168168
"steps": [
@@ -172,7 +172,7 @@
172172
},
173173
{
174174
"title": "Einheitlich filtern",
175-
"description": "Entfernen Sie inkompatible oder veraltete Produkte und vergleichen Sie den Rest nach Preis, Lizenz, Aktivität und offiziellen Quellen."
175+
"description": "Entfernen Sie inkompatible oder veraltete Produkte und vergleichen Sie den Rest nach Preis, Lizenz, GitHub-Sternen, Projektlinks und offiziellen Quellen."
176176
},
177177
{
178178
"title": "Prüfen und testen",
@@ -181,7 +181,7 @@
181181
],
182182
"title": "AI-Coding-Erweiterungen vergleichen"
183183
},
184-
"subtitle": "Vergleichen Sie AI-Coding-Erweiterungen nach unterstützten IDEs, Preisen, Lizenz, GitHub-Aktivität, Lebenszyklus und offiziellen Quellen.",
184+
"subtitle": "Vergleichen Sie AI-Coding-Erweiterungen nach unterstützten IDEs, Preisen, Lizenz, GitHub-Sternen und Projektlinks, Lebenszyklus und offiziellen Quellen.",
185185
"title": "AI-Coding-Erweiterungs-Vergleich"
186186
},
187187
"ides": {
@@ -227,12 +227,12 @@
227227
"description": "Die Plattformunterstützung variiert. Prüfen Sie die Symbole für macOS, Windows und Linux in der Tabelle und bestätigen Sie Systemanforderungen im offiziellen Download oder in der Installationsanleitung."
228228
}
229229
],
230-
"intro": "Vergleichen Sie {count} AI-Coding-IDEs anhand einheitlicher Signale: Plattformunterstützung, Preise, Lizenz, Projektaktivität und offizielle Quellen. Beginnen Sie mit Teamvorgaben, erstellen Sie eine Auswahl und bestätigen Sie aktuelle Details vor dem Wechsel an der Quelle.",
230+
"intro": "Vergleichen Sie {count} AI-Coding-IDEs anhand einheitlicher Signale: Plattformunterstützung, Preise, Lizenz, GitHub-Sterne und Projektlinks sowie offizielle Quellen. Beginnen Sie mit Teamvorgaben, erstellen Sie eine Auswahl und bestätigen Sie aktuelle Details vor dem Wechsel an der Quelle.",
231231
"profiles": [
232232
{
233233
"title": "Cursor",
234234
"href": "/ides/cursor",
235-
"description": "Prüfen Sie unterstützte Plattformen, Preise, Lizenz, Projektaktivität und offizielle Quellen."
235+
"description": "Prüfen Sie unterstützte Plattformen, Preise, Lizenz, GitHub-Sterne und Projektlinks sowie offizielle Quellen."
236236
},
237237
{
238238
"title": "Windsurf Editor",
@@ -250,8 +250,8 @@
250250
"description": "Prüfen Sie unterstützte Plattformen, Preise, Lizenz, Projektlinks und Dokumentation."
251251
}
252252
],
253-
"profilesIntro": "Diese Profile sind Startpunkte, keine Rangliste. Öffnen Sie jeden Datensatz, um aktuelle Manifest-Daten und Primärquellen zu prüfen.",
254-
"profilesTitle": "Repräsentative IDE-Profile prüfen",
253+
"profilesIntro": "Die folgenden Links führen direkt zu bereits in der Tabelle aufgeführten Produktdatensätzen; sie sind keine Rangliste oder Empfehlung. Öffnen Sie jeden Datensatz, um Manifestdaten und Primärquellen zu prüfen.",
254+
"profilesTitle": "IDE-Produktprofile öffnen",
255255
"shortlistIntro": "Nutzen Sie einen einheitlichen Ablauf, damit Popularität nicht Plattform- und Workflow-Eignung ersetzt.",
256256
"shortlistTitle": "IDE-Auswahl in drei Schritten",
257257
"steps": [
@@ -270,7 +270,7 @@
270270
],
271271
"title": "AI-Coding-IDEs vergleichen"
272272
},
273-
"subtitle": "Vergleichen Sie AI-Coding-IDEs nach Preisen, Plattformen, Lizenz, GitHub-Aktivität und offiziellen Quellen, bevor Sie eine Entwicklungsumgebung wählen.",
273+
"subtitle": "Vergleichen Sie AI-Coding-IDEs nach Preisen, Plattformen, Lizenz, GitHub-Sternen und Projektlinks sowie offiziellen Quellen, bevor Sie eine Entwicklungsumgebung wählen.",
274274
"title": "AI-Coding-IDE-Vergleich"
275275
},
276276
"modelCompareDescription": "Vergleichen Sie AI-Coding-Modelle nebeneinander: Spezifikationen, Preise, Funktionen und Benchmarks.",
@@ -279,5 +279,8 @@
279279
"backTo": "Zurück zu Modellen",
280280
"subtitle": "Vergleichen Sie Spezifikationen und Preise von AI-Coding-Modellen nebeneinander.",
281281
"title": "AI-Coding-Modell-Vergleich"
282+
},
283+
"table": {
284+
"scrollHint": "Scrollen Sie horizontal, um alle Spalten anzuzeigen."
282285
}
283286
}

0 commit comments

Comments
 (0)