Skip to content

fix(website): improve seo foundations - #470

Merged
macalbert merged 7 commits into
mainfrom
macalbert-seo-action-plan
Jul 31, 2026
Merged

fix(website): improve seo foundations#470
macalbert merged 7 commits into
mainfrom
macalbert-seo-action-plan

Conversation

@macalbert

@macalbert macalbert commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

Improves share previews, changelog navigation, canonical localized routing, delivery caching, and static SEO safeguards without changing Envilder product behavior.
Adds build-output regression checks so broken anchors, hreflang clusters, metadata, and eager media loading are caught before deployment.

Changes

  • Add a 1200x630 Open Graph image with absolute Open Graph and Twitter image metadata.
  • Fix changelog fragment IDs and duplicate headings, normalize trailing-slash links, and model supported localized routes.
  • Mark localized changelog shells as noindex, remove them from the sitemap, and avoid false language-switcher targets.
  • Defer demo video loading with a poster and transcript, add CloudFront cache and security header policies, and enable HTTP/2 plus HTTP/3.
  • Align package metadata, remove unused assets, and add static-site SEO and CloudFront regression coverage.

Testing

  • pnpm lint passes
  • pnpm test passes (Docker/Testcontainers unavailable; 349 tests passed, 18 skipped, and 4 suites were blocked)
  • pnpm --filter @envilder/website-tests test passes
  • pnpm --filter @envilder/iac-tests test passes

Related

N/A

Summary by CodeRabbit

  • New Features

    • Added clearer homepage messaging, demo transcripts, and an updated video poster.
    • Demo videos now load near the viewport and respect reduced-motion preferences.
    • Added enhanced security and caching headers, asset-specific caching, and HTTP/2 and HTTP/3 support.
    • Improved localized navigation, language switching, metadata, documentation links, and sponsor image performance.
  • Bug Fixes

    • Corrected localized routes, trailing slashes, sitemap exclusions, and changelog indexing.
    • Added clearer changelog language notices and improved product-specific section links.

Improve social previews, changelog navigation, canonical routing, delivery
headers, and static-site SEO regressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 28, 2026 13:02
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added npm Pull requests that update npm/pnpm packages iac Infrastructure as Code (AWS CDK) size/XL Very large change — 400+ lines / 25+ files; should be split bug Something isn't working labels Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

CloudFront delivery policies add response headers and caching rules. Website localization gains route-aware trailing-slash handling. Changelog rendering, homepage content, metadata, deferred video loading, SEO validation, and package metadata are updated.

Changes

CloudFront delivery configuration

Layer / File(s) Summary
CloudFront policies, behaviors, and tests
src/iac/lib/stacks/staticWebsiteStack.ts, tests/iac/lib/stacks/staticWebsiteStack.test.ts
Adds response header policies, asset caching rules, five-minute error caching, HTTP/2 and HTTP/3 support, and matching tests.

Website localization and SEO

Layer / File(s) Summary
Localized routing contracts and helpers
src/website/src/i18n/*, tests/website/i18n/utils.test.ts
Adds route language mappings, translation fields, locale-aware path helpers, trailing-slash normalization, and fragment preservation.
Layout and navigation with locale awareness
src/website/src/layouts/BaseLayout.astro, src/website/src/components/{Footer,Navbar,HowItWorks,Sdks}.astro
Updates localized links, language switching, alternate metadata, Open Graph data, Twitter metadata, and structured data.
Homepage UI, translations, and deferred media
src/website/src/components/{DemoVideo,Hero,Sponsors}.astro, src/website/src/i18n/{en,ca,es}.ts, src/website/public/Envilder-demo-poster.webp, src/website/src/styles/global.css
Adds localized homepage copy, hero text, sponsor attributes, deferred video loading, a poster image, and caret styling.
Changelog rendering, noindex, and sitemap
src/website/astro.config.mjs, src/website/src/pages/{changelog,ca/changelog,es/changelog}.astro, src/website/src/utils/markdown.ts, tests/website/utils/markdown.test.ts
Filters sitemap URLs, enforces trailing slashes, prefixes changelog heading IDs, adds language notices and noindex metadata, and changes document-title headings to styled h2 elements.
Built-site SEO and metadata validation
tests/website/static-site.test.ts
Adds validation for SEO markup, localized alternates, JSON-LD, sitemap contents, noindex pages, robots metadata, trailing slashes, and deferred media.

Node.js package metadata

Layer / File(s) Summary
Package homepage metadata
src/sdks/nodejs/package.json
Adds the npm package homepage field.

Estimated code review effort: 4 (Complex) | ~50 minutes

Possibly related PRs

Suggested labels: documentation

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main website SEO improvements.
Description check ✅ Passed The description explains the changes, testing status, and related issues, but does not use every template heading.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch macalbert-seo-action-plan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR strengthens Envilder’s website SEO baseline and regression protection by (1) making localized routing/canonical/hreflang behavior explicit, (2) improving share previews and media loading, and (3) tightening CDN caching + security headers, while adding build-output checks to catch SEO regressions pre-deploy.

Changes:

  • Add Open Graph/Twitter image metadata (absolute URLs) + JSON-LD screenshot, and defer demo video loading with poster + transcript.
  • Normalize i18n routing (trailing slashes, supported localized routes) and harden changelog navigation (stable fragment IDs, noindex localized changelog shells, sitemap filtering).
  • Add CloudFront cache/security header policies + HTTP/2+HTTP/3, with new static-site + infra regression tests.

Reviewed changes

Copilot reviewed 27 out of 36 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/website/utils/markdown.test.ts Updates changelog HTML tests for prefixed fragment IDs and doc-title rendering.
tests/website/static-site.test.ts Adds dist-based SEO regression tests (canonical/hreflang/anchors/OG/Twitter/robots/sitemap/video).
tests/website/i18n/utils.test.ts Updates localizedPath expectations (trailing slash, unsupported localization fallback, fragment preservation).
tests/iac/lib/stacks/staticWebsiteStack.test.ts Extends CDK assertions for error caching TTL, HTTP version, and response header policies.
tests/iac/lib/stacks/snapshots/staticWebsiteStack.test.ts.snap Updates synthesized template snapshot for new CloudFront behaviors/policies.
src/website/src/utils/markdown.ts Adds changelog heading id prefixing and renders markdown document titles as <h2> for changelog pages.
src/website/src/pages/changelog.astro Uses prefixed changelog anchors and adds language notice copy.
src/website/src/pages/ca/changelog.astro Adds noindex for localized changelog shell + prefix-based anchors + language notice.
src/website/src/pages/es/changelog.astro Adds noindex for localized changelog shell + prefix-based anchors + language notice.
src/website/src/layouts/BaseLayout.astro Implements canonical localized routing, OG/Twitter image metadata, and hreflang clusters driven by localized route support.
src/website/src/i18n/utils.ts Adds path normalization, locale stripping, supported-language lookup, and localizedPath fragment preservation.
src/website/src/i18n/types.ts Extends translation types for new SEO/media/changelog strings.
src/website/src/i18n/localized-routes.ts Defines indexable routes and their supported locales as the source of truth for localization.
src/website/src/i18n/en.ts Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title).
src/website/src/i18n/ca.ts Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title).
src/website/src/i18n/es.ts Adds/updates SEO and UI copy (OG alt, positioning, transcript, release language notice, docs title).
src/website/src/components/Sponsors.astro Marks sponsor link as sponsored and adds image perf attributes (dimensions/lazy/async).
src/website/src/components/Sdks.astro Normalizes docs links to trailing-slash routes.
src/website/src/components/Navbar.astro Normalizes changelog/docs links to trailing-slash routes.
src/website/src/components/HowItWorks.astro Normalizes docs links to trailing-slash routes.
src/website/src/components/Hero.astro Adds positioning copy under the main hero description.
src/website/src/components/Footer.astro Updates language switcher to only show supported locales per route; normalizes links with trailing slashes and fragments.
src/website/src/components/DemoVideo.astro Defers loading demo MP4 until near-viewport, adds poster + transcript, respects reduced motion.
src/website/astro.config.mjs Filters localized changelog shells out of sitemap; enforces trailing slashes.
src/website/public/localstack-logo-vertical-Light.svg Removes unused asset.
src/website/public/localstack-logo-vertical-dark.svg Removes unused asset.
src/website/public/localstack-logo-vertical-color.svg Removes unused asset.
src/website/public/localstack-logo-icon-light.svg Removes unused asset.
src/website/public/localstack-logo-icon-dark.svg Removes unused asset.
src/website/public/localstack-logo-icon-color.svg Removes unused asset.
src/website/public/localstack-logo-horizontal-color.svg Removes unused asset.
src/sdks/nodejs/package.json Adds homepage metadata.
src/iac/lib/stacks/staticWebsiteStack.ts Adds CloudFront response header policies, asset behavior, longer error TTL, and HTTP/2+HTTP/3.
package.json Updates root package description and adjusts test script to build website before running Vitest.

Comment thread package.json Outdated
Comment thread tests/website/static-site.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/iac/lib/stacks/staticWebsiteStack.test.ts (1)

81-142: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assertions don't verify which behavior references which policy.

The test checks that a _assets/* CacheBehavior exists and that two ResponseHeadersPolicy resources exist with the expected Cache-Control values, but never links the two — if defaultResponseHeadersPolicy and assetResponseHeadersPolicy were accidentally swapped between defaultBehavior and _assets/*, this test would still pass.

♻️ Example using Capture to link behavior → policy id
+		const assetPolicyIdCapture = new Capture();
 		actual.hasResourceProperties("AWS::CloudFront::Distribution", {
 			DistributionConfig: Match.objectLike({
 				HttpVersion: "http2and3",
 				CacheBehaviors: Match.arrayWith([
 					Match.objectLike({
 						PathPattern: "_assets/*",
+						ResponseHeadersPolicyId: { Ref: assetPolicyIdCapture },
 					}),
 				]),
 			}),
 		});
+		// then assert assetPolicyIdCapture.asString() matches the asset (not default) policy's logical id
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/iac/lib/stacks/staticWebsiteStack.test.ts` around lines 81 - 142,
Update Should_ApplyCacheAndSecurityHeaders_When_WebsiteIsCreated to capture the
CloudFront ResponseHeadersPolicy resource IDs and assert that the
defaultBehavior and the _assets/* CacheBehavior reference the correct policy
IDs. Keep the existing policy-content assertions, but explicitly link each
CacheBehavior’s ResponseHeadersPolicyId to its corresponding
defaultResponseHeadersPolicy or assetResponseHeadersPolicy.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/website/src/layouts/BaseLayout.astro`:
- Around line 128-138: Update the og:locale meta tag in BaseLayout to emit a
full Open Graph locale with language and territory, rather than the bare lang
code. Reuse the existing locale or language-region mapping used by the layout so
values resolve to forms such as en_US, ca_ES, and es_ES.

---

Nitpick comments:
In `@tests/iac/lib/stacks/staticWebsiteStack.test.ts`:
- Around line 81-142: Update
Should_ApplyCacheAndSecurityHeaders_When_WebsiteIsCreated to capture the
CloudFront ResponseHeadersPolicy resource IDs and assert that the
defaultBehavior and the _assets/* CacheBehavior reference the correct policy
IDs. Keep the existing policy-content assertions, but explicitly link each
CacheBehavior’s ResponseHeadersPolicyId to its corresponding
defaultResponseHeadersPolicy or assetResponseHeadersPolicy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 506dbbbd-c696-462a-97fd-626ab8234eba

📥 Commits

Reviewing files that changed from the base of the PR and between f5cc504 and 871012b.

⛔ Files ignored due to path filters (10)
  • package.json is excluded by none and included by none
  • src/website/public/localstack-logo-horizontal-color.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-icon-color.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-icon-dark.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-icon-light.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-vertical-Light.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-vertical-color.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/localstack-logo-vertical-dark.svg is excluded by !**/*.svg and included by src/**
  • src/website/public/og-image.png is excluded by !**/*.png and included by src/**
  • tests/iac/lib/stacks/__snapshots__/staticWebsiteStack.test.ts.snap is excluded by !**/*.snap and included by tests/**
📒 Files selected for processing (26)
  • src/iac/lib/stacks/staticWebsiteStack.ts
  • src/sdks/nodejs/package.json
  • src/website/astro.config.mjs
  • src/website/public/Envilder-demo-poster.webp
  • src/website/src/components/DemoVideo.astro
  • src/website/src/components/Footer.astro
  • src/website/src/components/Hero.astro
  • src/website/src/components/HowItWorks.astro
  • src/website/src/components/Navbar.astro
  • src/website/src/components/Sdks.astro
  • src/website/src/components/Sponsors.astro
  • src/website/src/i18n/ca.ts
  • src/website/src/i18n/en.ts
  • src/website/src/i18n/es.ts
  • src/website/src/i18n/localized-routes.ts
  • src/website/src/i18n/types.ts
  • src/website/src/i18n/utils.ts
  • src/website/src/layouts/BaseLayout.astro
  • src/website/src/pages/ca/changelog.astro
  • src/website/src/pages/changelog.astro
  • src/website/src/pages/es/changelog.astro
  • src/website/src/utils/markdown.ts
  • tests/iac/lib/stacks/staticWebsiteStack.test.ts
  • tests/website/i18n/utils.test.ts
  • tests/website/static-site.test.ts
  • tests/website/utils/markdown.test.ts

Comment thread src/website/src/layouts/BaseLayout.astro
@macalbert macalbert self-assigned this Jul 28, 2026
Copilot AI review requested due to automatic review settings July 29, 2026 16:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 36 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

package.json:34

  • test:ci is used in CI (.github/workflows/tests.yml) but it does not build the website first. Since this PR adds tests that read src/website/dist (e.g., tests/website/static-site.test.ts), CI runs will fail unless the website build happens before Vitest (as it now does for pnpm test).
    "test": "pnpm --filter @envilder/website build && vitest run --reporter=verbose --coverage",
    "test:ci": "vitest run --reporter=verbose --reporter=junit --coverage --outputFile=coverage/junit/test-results.xml",

tests/website/static-site.test.ts:89

  • JSON.parse(jsonLd) can throw, which would crash the test run instead of reporting a readable SEO failure. This makes the regression test less actionable (a parse error stacktrace rather than ${file}: invalid JSON-LD).

Marçal Albert Castellví and others added 4 commits July 31, 2026 09:22
Build the website before static output tests run in CI.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Return an actionable SEO issue instead of an uncaught parse error.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Emit language and territory codes that Open Graph consumers recognize.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Prevent default and asset response header policies from being swapped.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 31, 2026 07:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 27 out of 36 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/website/src/i18n/localized-routes.ts:5

  • localizedRoutes is typed as Record<string, readonly string[]>, and localizedLanguages() later casts these strings to Lang. That means a typo like 'eng' would compile and only fail at runtime (e.g., producing invalid hreflang/routes). Tighten the type here so only the supported locale codes are allowed.
export const localizedRoutes = {
  '/': ['en', 'ca', 'es'],
  '/docs/': ['en', 'ca', 'es'],
  '/changelog/': ['en'],
} as const satisfies Record<string, readonly string[]>;

Copilot AI review requested due to automatic review settings July 31, 2026 08:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/website/src/styles/global.css`:
- Around line 166-167: Update the global caret-color rule near the existing
transparent caret declaration so editable elements retain a visible insertion
caret. Scope the transparent styling to non-editable selectable text, or add a
reset covering inputs, textareas, and [contenteditable] elements without
changing the intended Chromium/Firefox behavior elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 31ed3269-23ae-452a-adeb-4e5564e65563

📥 Commits

Reviewing files that changed from the base of the PR and between 0826bbf and 165bfb7.

📒 Files selected for processing (1)
  • src/website/src/styles/global.css

Comment thread src/website/src/styles/global.css

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 28 out of 37 changed files in this pull request and generated no new comments.

Suppressed comments (2)

tests/website/static-site.test.ts:137

  • getHreflangIssues() only compares the href values of <link rel="alternate"> tags. That can miss regressions where the set/order of hreflang attributes is wrong (e.g., missing x-default or incorrect locale codes) while hrefs still match. Consider asserting both href and hreflang sequences.
    tests/website/static-site.test.ts:54
  • getExpectedAlternateUrls() hard-codes locale prefixes (ca|es) and will throw a non-actionable Cannot read properties of undefined (reading 'map') if a new indexable route is added but not listed in localizedRoutes. Deriving supported locale prefixes from localizedRoutes and throwing a clearer error for missing routes will make these regression tests easier to maintain as locales/routes evolve.

@macalbert
macalbert merged commit 8c4be06 into main Jul 31, 2026
27 checks passed
@macalbert
macalbert deleted the macalbert-seo-action-plan branch July 31, 2026 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working iac Infrastructure as Code (AWS CDK) npm Pull requests that update npm/pnpm packages size/XL Very large change — 400+ lines / 25+ files; should be split

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants