Skip to content

Fix 404 errors on category pages via explicit permalink configuration#9

Draft
Copilot wants to merge 6 commits intomainfrom
copilot/fix-404-errors-tools-categories-again
Draft

Fix 404 errors on category pages via explicit permalink configuration#9
Copilot wants to merge 6 commits intomainfrom
copilot/fix-404-errors-tools-categories-again

Conversation

Copy link
Contributor

Copilot AI commented Jan 31, 2026

Category pages were generating URLs without trailing slashes (/categories/green) while navigation links expected them with trailing slashes (/categories/green/), causing 404s.

Changes

Category permalinks (8 files)

  • Added explicit permalink: /categories/{category}/ to all category markdown frontmatter
  • Ensures Jekyll generates /categories/{category}/index.html structure

Category layout (_layouts/category.html)

  • Changed data source from site.tools collection to site.data.tools YAML
  • Filters tools by page.category and renders cards inline
  • Was only showing 1 tool from sparse collection; now displays all 12 tools from data file

Build configuration

  • Commented out jekyll-theme-minimal in _config.yml (using custom layouts)
  • Added missing gems to Gemfile: jekyll-feed, jekyll-seo-tag, jekyll-sitemap, kramdown-parser-gfm
  • Created .gitignore for vendor/, _site/, .bundle/

Example

Before:

---
layout: category
title: Green Infrastructure Tools
category: green
description: Tools for planning and implementing green infrastructure
---

After:

---
layout: category
title: Green Infrastructure Tools
category: green
permalink: /categories/green/
description: Tools for planning and implementing green infrastructure
---

This matches the expected URL pattern in categories/index.md which links to /categories/green/.

Original prompt

Continue fixing the 404 errors for all tools and category pages in the 'Urban-Climate-Tools' repository.

Tasks:

  1. Complete 404 Fixes:
  2. Test All Links:
    • Verify functional links for all tools and category pages to their target destinations.
    • Check for broken internal and external links throughout the site.
  3. Enhance Pages:
    • Implement filtered tools tables or lists in each category page to showcase tools by their specific category.
  4. Local Testing:
    • Utilize Jekyll local server to test the site's build and navigation for proper functionality.
  5. Update Documentation:
    • Revise the repository's README and configuration details to include these changes.

Testing

  • Perform a thorough local site test.
  • Deploy and ensure the live GitHub Pages site is fully functional with no 404 errors.

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 5 commits January 31, 2026 01:16
Co-authored-by: mk-ws-git <235080064+mk-ws-git@users.noreply.github.com>
Co-authored-by: mk-ws-git <235080064+mk-ws-git@users.noreply.github.com>
Co-authored-by: mk-ws-git <235080064+mk-ws-git@users.noreply.github.com>
Co-authored-by: mk-ws-git <235080064+mk-ws-git@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix 404 errors for tools and category pages Fix 404 errors on category pages via explicit permalink configuration Jan 31, 2026
Copilot AI requested a review from mk-ws-git January 31, 2026 01:23
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes 404 errors on category pages by adding explicit permalink configuration and updating the category layout to use the correct data source. The changes address navigation issues where links with trailing slashes were generating 404s because Jekyll was creating URLs without them.

Changes:

  • Added explicit permalink: /categories/{category}/ to all 8 category markdown files
  • Updated category layout to use site.data.tools instead of the sparse site.tools collection
  • Commented out Jekyll theme configuration to use custom layouts
  • Added missing Jekyll plugins to Gemfile (jekyll-feed, jekyll-seo-tag, jekyll-sitemap, kramdown-parser-gfm)
  • Created .gitignore for build artifacts and vendor directory
  • Added comprehensive documentation (TESTING_SUMMARY, DEPLOYMENT_GUIDE, IMPLEMENTATION_COMPLETE)

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
categories/planning.md Added permalink configuration for consistent URL structure
categories/other.md Added permalink configuration for consistent URL structure
categories/heat.md Added permalink configuration for consistent URL structure
categories/green.md Added permalink configuration for consistent URL structure
categories/flood.md Added permalink configuration for consistent URL structure
categories/energy.md Added permalink configuration for consistent URL structure
categories/data.md Added permalink configuration for consistent URL structure
categories/coastal.md Added permalink configuration for consistent URL structure
_layouts/category.html Changed data source to site.data.tools and added inline tool card rendering with embedded styles
_config.yml Commented out theme configuration to avoid conflicts with custom layouts
Gemfile Added missing Jekyll plugin dependencies with version constraints
.gitignore Added standard Jekyll build artifacts and vendor directories
TESTING_SUMMARY.md Comprehensive testing documentation showing all 8 category pages working
DEPLOYMENT_GUIDE.md Step-by-step deployment instructions for GitHub Pages
README.md Updated with recent changes section and corrected local server instructions
IMPLEMENTATION_COMPLETE.md Complete implementation summary with deployment checklist

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

</div>

<div class="card-footer">
<a href="{{ tool.url }}" target="_blank" class="btn btn-sm btn-primary">View Tool →</a>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The external tool links open in a new tab with target="_blank" but are missing the rel="noopener noreferrer" attribute. This is a security best practice to prevent the opened page from accessing the opener window via window.opener, which could be exploited for phishing attacks. Add rel="noopener noreferrer" to the anchor tag.

Suggested change
<a href="{{ tool.url }}" target="_blank" class="btn btn-sm btn-primary">View Tool →</a>
<a href="{{ tool.url }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-primary">View Tool →</a>

Copilot uses AI. Check for mistakes.

<style>
.category-page {
padding: var(--spacing-3xl) 0;
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS variable --spacing-3xl is used but is not defined in the main stylesheet (assets/css/style.css). The stylesheet only defines spacing variables up to --spacing-2xl. This will cause the padding to fall back to the initial value, which may not match the intended design. Consider either defining --spacing-3xl in the stylesheet or using an existing variable like --spacing-2xl.

Suggested change
padding: var(--spacing-3xl) 0;
padding: var(--spacing-2xl) 0;

Copilot uses AI. Check for mistakes.
.card-body h3 {
font-size: 20px;
margin-bottom: var(--spacing-sm);
color: var(--text-dark);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS variable --text-dark is used but is not defined in any of the stylesheets. The main stylesheet defines --dark-gray instead. This will cause the color to fall back to the initial value (likely black). Replace --text-dark with --dark-gray to match the existing color system, or define --text-dark in the stylesheet.

Suggested change
color: var(--text-dark);
color: var(--dark-gray);

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +142
.meta-item .value {
color: var(--text-dark);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS variable --text-dark is used but is not defined in any of the stylesheets. The main stylesheet defines --dark-gray instead. This will cause the color to fall back to the initial value. Replace with --dark-gray to match the existing color system.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +45
<div class="tool-card" data-cost="{{ tool.cost }}" data-skill="{{ tool.skillLevel }}" data-coverage="{{ tool.coverage | join: ',' }}">
<div class="card-header">
<div class="card-badges">
{% for category in tool.categories %}
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
{% endfor %}
</div>
</div>

<div class="card-body">
<h3>{{ tool.name }}</h3>
<p class="developer">{{ tool.developer }}</p>
<p class="description">{{ tool.description }}</p>

<div class="card-meta">
<span class="meta-item">
<span class="label">Cost:</span>
<span class="value">{{ tool.cost }}</span>
</span>
<span class="meta-item">
<span class="label">Skill Level:</span>
<span class="value">{{ tool.skillLevel }}</span>
</span>
</div>
</div>

<div class="card-footer">
<a href="{{ tool.url }}" target="_blank" class="btn btn-sm btn-primary">View Tool →</a>
</div>
</div>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This layout duplicates the tool card HTML that already exists in _includes/tool-card.html. However, there's a data structure mismatch: the include expects fields like tool.access_model, tool.skill_level, tool.geographic_coverage, while this implementation uses tool.cost, tool.skillLevel, tool.coverage. The data in _data/tools.yml uses the latter naming convention. To maintain consistency and avoid duplication, consider updating the include to match the data structure, then using it here with {% include tool-card.html tool=tool %}.

Suggested change
<div class="tool-card" data-cost="{{ tool.cost }}" data-skill="{{ tool.skillLevel }}" data-coverage="{{ tool.coverage | join: ',' }}">
<div class="card-header">
<div class="card-badges">
{% for category in tool.categories %}
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
{% endfor %}
</div>
</div>
<div class="card-body">
<h3>{{ tool.name }}</h3>
<p class="developer">{{ tool.developer }}</p>
<p class="description">{{ tool.description }}</p>
<div class="card-meta">
<span class="meta-item">
<span class="label">Cost:</span>
<span class="value">{{ tool.cost }}</span>
</span>
<span class="meta-item">
<span class="label">Skill Level:</span>
<span class="value">{{ tool.skillLevel }}</span>
</span>
</div>
</div>
<div class="card-footer">
<a href="{{ tool.url }}" target="_blank" class="btn btn-sm btn-primary">View Tool →</a>
</div>
</div>
{% include tool-card.html tool=tool %}

Copilot uses AI. Check for mistakes.
Comment on lines +51 to +172
<style>
.category-page {
padding: var(--spacing-3xl) 0;
max-width: 1200px;
margin: 0 auto;
}

.category-page h1 {
font-size: 32px;
margin-bottom: var(--spacing-lg);
color: var(--primary-blue);
}

.category-intro {
margin-bottom: var(--spacing-2xl);
font-size: 16px;
line-height: 1.6;
color: #666;
}

.tool-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--spacing-lg);
}

.tool-card {
background: white;
border: 1px solid var(--border-gray);
border-radius: var(--radius-md);
padding: var(--spacing-lg);
transition: all 0.3s ease;
}

.tool-card:hover {
box-shadow: 0 4px 12px rgba(0, 81, 186, 0.1);
transform: translateY(-2px);
}

.card-header {
margin-bottom: var(--spacing-md);
}

.card-badges {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-sm);
}

.badge {
display: inline-block;
padding: 4px 12px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
border-radius: 12px;
background: var(--secondary-blue);
color: var(--primary-blue);
}

.card-body h3 {
font-size: 20px;
margin-bottom: var(--spacing-sm);
color: var(--text-dark);
}

.developer {
font-size: 13px;
color: #999;
margin-bottom: var(--spacing-md);
}

.description {
font-size: 14px;
line-height: 1.6;
margin-bottom: var(--spacing-md);
color: #666;
}

.card-meta {
display: flex;
gap: var(--spacing-md);
font-size: 12px;
margin-bottom: var(--spacing-md);
}

.meta-item .label {
color: #999;
}

.meta-item .value {
color: var(--text-dark);
font-weight: 600;
}

.card-footer {
padding-top: var(--spacing-md);
border-top: 1px solid var(--border-gray);
}

.btn-primary {
display: inline-block;
padding: 8px 16px;
background: var(--primary-blue);
color: white;
text-decoration: none;
border-radius: var(--radius-sm);
font-size: 13px;
font-weight: 600;
transition: background 0.3s ease;
}

.btn-primary:hover {
background: #003d8f;
}

@media (max-width: 768px) {
.tool-grid {
grid-template-columns: 1fr;
}
}
</style>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embedding CSS styles within layout files reduces maintainability and violates the separation of concerns principle. The site already has a dedicated stylesheet structure (assets/css/cards.css, style.css). These styles should be moved to an appropriate CSS file, likely assets/css/cards.css or a new assets/css/category.css file. This would also resolve the issue with undefined CSS variables and make the styles reusable across different layouts.

Copilot uses AI. Check for mistakes.
<div class="card-header">
<div class="card-badges">
{% for category in tool.categories %}
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The badge class names generated by badge-{{ category | downcase }} won't match the CSS class names defined in the stylesheet for some categories. For example, the 'green' category will generate .badge-green, but the stylesheet defines .badge-greeninfra. Similarly, 'data' generates .badge-data but the stylesheet has .badge-dataplat. This will cause these badges to lose their styling and appear unstyled. Either update the stylesheet to add .badge-green and .badge-data classes, or change the category values in the data file to match the existing CSS classes.

Suggested change
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
{% assign category_class = category | downcase %}
{% if category_class == 'green' %}
{% assign category_class = 'greeninfra' %}
{% elsif category_class == 'data' %}
{% assign category_class = 'dataplat' %}
{% endif %}
<span class="badge badge-{{ category_class }}">{{ category }}</span>

Copilot uses AI. Check for mistakes.
<div class="card-header">
<div class="card-badges">
{% for category in tool.categories %}
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'other' category used in the data file doesn't have a corresponding .badge-other style defined in the stylesheet. This will cause badges for the 'other' category to appear unstyled. Add a .badge-other style definition to assets/css/style.css to ensure consistent badge styling across all categories.

Suggested change
<span class="badge badge-{{ category | downcase }}">{{ category }}</span>
{% assign category_class = category | downcase %}
{% if category_class == "other" %}
{% assign category_class = "default" %}
{% endif %}
<span class="badge badge-{{ category_class }}">{{ category }}</span>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants