Skip to content

feat(website): add SEO, sitemap, redirects, CI workflow, and Vercel config#10156

Open
christian-byrne wants to merge 2 commits intowebsite/04-secondary-pagesfrom
website/05-seo-polish
Open

feat(website): add SEO, sitemap, redirects, CI workflow, and Vercel config#10156
christian-byrne wants to merge 2 commits intowebsite/04-secondary-pagesfrom
website/05-seo-polish

Conversation

@christian-byrne
Copy link
Contributor

@christian-byrne christian-byrne commented Mar 17, 2026

Summary

Changes

  • What:
  • Breaking:
  • Dependencies:

Review Focus

Screenshots (if applicable)

┆Issue is synchronized with this Notion page by Unito

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c22e6a9e-6c2a-43d1-8d35-7644671985d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch website/05-seo-polish
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Contributor Author

christian-byrne commented Mar 17, 2026

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🎭 Playwright: ✅ 592 passed, 0 failed · 6 flaky

📊 Browser Reports
  • chromium: View Report (✅ 579 / ❌ 0 / ⚠️ 6 / ⏭️ 10)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 10 / ❌ 0 / ⚠️ 0 / ⏭️ 0)

@github-actions
Copy link

github-actions bot commented Mar 17, 2026

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 03/17/2026, 02:26:23 PM UTC

Links

@christian-byrne christian-byrne marked this pull request as ready for review March 17, 2026 08:16
@dosubot dosubot bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Mar 17, 2026
@christian-byrne christian-byrne requested review from dante01yoon and removed request for dante01yoon March 17, 2026 09:53
…onfig

- seo-01: Add Organization + WebSite JSON-LD structured data to BaseLayout
- seo-03: Add @astrojs/sitemap integration, robots.txt (14 URLs generated)
- seo-04: Add redirects: /pricing, /enterprise, /blog, /contact, /press
- deploy-01: Add vercel.json for Vercel project configuration
- deploy-02: Add ci-website-build.yaml GitHub Actions workflow
- OG/Twitter meta already present in BaseLayout from Wave 2 (seo-02)
@christian-byrne christian-byrne force-pushed the website/04-secondary-pages branch from 59ad4d7 to 7ddc7c0 Compare March 17, 2026 14:21
@github-actions
Copy link

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

No baseline found — showing absolute values.

Metric Value
canvas-idle: style recalcs 10
canvas-idle: layouts 0
canvas-idle: task duration 360ms
canvas-idle: DOM nodes 21
canvas-idle: script duration 21ms
canvas-idle: event listeners 6
canvas-idle: TBT 0ms
canvas-idle: frame duration 17ms
canvas-idle: heap delta 1.6 MB
canvas-mouse-sweep: style recalcs 77
canvas-mouse-sweep: layouts 12
canvas-mouse-sweep: task duration 795ms
canvas-mouse-sweep: DOM nodes 61
canvas-mouse-sweep: script duration 137ms
canvas-mouse-sweep: event listeners 13
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: frame duration 17ms
canvas-mouse-sweep: heap delta -2.4 MB
canvas-zoom-sweep: style recalcs 31
canvas-zoom-sweep: layouts 6
canvas-zoom-sweep: task duration 304ms
canvas-zoom-sweep: DOM nodes 78
canvas-zoom-sweep: script duration 25ms
canvas-zoom-sweep: event listeners 20
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: frame duration 17ms
canvas-zoom-sweep: heap delta 6.0 MB
dom-widget-clipping: style recalcs 14
dom-widget-clipping: layouts 0
dom-widget-clipping: task duration 359ms
dom-widget-clipping: DOM nodes 23
dom-widget-clipping: script duration 71ms
dom-widget-clipping: event listeners 10
dom-widget-clipping: TBT 0ms
dom-widget-clipping: frame duration 17ms
dom-widget-clipping: heap delta 12.9 MB
large-graph-idle: style recalcs 11
large-graph-idle: layouts 0
large-graph-idle: task duration 506ms
large-graph-idle: DOM nodes 23
large-graph-idle: script duration 100ms
large-graph-idle: event listeners 21
large-graph-idle: TBT 0ms
large-graph-idle: frame duration 17ms
large-graph-idle: heap delta -9.8 MB
large-graph-pan: style recalcs 69
large-graph-pan: layouts 0
large-graph-pan: task duration 1053ms
large-graph-pan: DOM nodes 18
large-graph-pan: script duration 404ms
large-graph-pan: event listeners 5
large-graph-pan: TBT 0ms
large-graph-pan: frame duration 17ms
large-graph-pan: heap delta 2.6 MB
minimap-idle: style recalcs 10
minimap-idle: layouts 0
minimap-idle: task duration 488ms
minimap-idle: DOM nodes 19
minimap-idle: script duration 92ms
minimap-idle: event listeners 5
minimap-idle: TBT 0ms
minimap-idle: frame duration 17ms
minimap-idle: heap delta -10.8 MB
subgraph-dom-widget-clipping: style recalcs 48
subgraph-dom-widget-clipping: layouts 0
subgraph-dom-widget-clipping: task duration 359ms
subgraph-dom-widget-clipping: DOM nodes 22
subgraph-dom-widget-clipping: script duration 123ms
subgraph-dom-widget-clipping: event listeners 8
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: frame duration 17ms
subgraph-dom-widget-clipping: heap delta 4.5 MB
subgraph-idle: style recalcs 10
subgraph-idle: layouts 0
subgraph-idle: task duration 346ms
subgraph-idle: DOM nodes 19
subgraph-idle: script duration 18ms
subgraph-idle: event listeners 6
subgraph-idle: TBT 0ms
subgraph-idle: frame duration 17ms
subgraph-idle: heap delta 826.7 KB
subgraph-mouse-sweep: style recalcs 85
subgraph-mouse-sweep: layouts 16
subgraph-mouse-sweep: task duration 878ms
subgraph-mouse-sweep: DOM nodes 72
subgraph-mouse-sweep: script duration 105ms
subgraph-mouse-sweep: event listeners 13
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: frame duration 17ms
subgraph-mouse-sweep: heap delta -6.8 MB
workflow-execution: style recalcs 18
workflow-execution: layouts 5
workflow-execution: task duration 126ms
workflow-execution: DOM nodes 159
workflow-execution: script duration 29ms
workflow-execution: event listeners 55
workflow-execution: TBT 0ms
workflow-execution: frame duration 17ms
workflow-execution: heap delta 4.2 MB
Raw data
{
  "timestamp": "2026-03-17T14:34:47.905Z",
  "gitSha": "b28bf45ddc1a3da0c25dc37f3e72a070db0ad8d0",
  "branch": "website/05-seo-polish",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2031.1360000000036,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.339,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 361.63900000000007,
      "heapDeltaBytes": 1981076,
      "domNodes": 20,
      "jsHeapTotalBytes": 15466496,
      "scriptDurationMs": 20.213,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "canvas-idle",
      "durationMs": 2026.6369999999938,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.634999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 364.541,
      "heapDeltaBytes": 1448188,
      "domNodes": 22,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 21.845000000000002,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-idle",
      "durationMs": 2048.78199999996,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.294,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 354.421,
      "heapDeltaBytes": 1612776,
      "domNodes": 20,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 20.782000000000004,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1952.274000000017,
      "styleRecalcs": 79,
      "styleRecalcDurationMs": 48.07899999999999,
      "layouts": 12,
      "layoutDurationMs": 4.632000000000001,
      "taskDurationMs": 863.4759999999999,
      "heapDeltaBytes": -1659912,
      "domNodes": 66,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 144.195,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1844.5539999999596,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 38.262,
      "layouts": 12,
      "layoutDurationMs": 3.6850000000000005,
      "taskDurationMs": 765.85,
      "heapDeltaBytes": -2668532,
      "domNodes": 60,
      "jsHeapTotalBytes": 15990784,
      "scriptDurationMs": 132.528,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1772.7150000000051,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 36.868,
      "layouts": 12,
      "layoutDurationMs": 3.2909999999999995,
      "taskDurationMs": 755.15,
      "heapDeltaBytes": -3221904,
      "domNodes": 58,
      "jsHeapTotalBytes": 17825792,
      "scriptDurationMs": 133.55999999999997,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1764.363000000003,
      "styleRecalcs": 29,
      "styleRecalcDurationMs": 16.86,
      "layouts": 6,
      "layoutDurationMs": 0.755,
      "taskDurationMs": 306.95399999999995,
      "heapDeltaBytes": 6126688,
      "domNodes": 77,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 26.723,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1767.078999999967,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 15.814000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.6579999999999999,
      "taskDurationMs": 295.86600000000004,
      "heapDeltaBytes": 6145588,
      "domNodes": 77,
      "jsHeapTotalBytes": 17301504,
      "scriptDurationMs": 24.687,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1745.346999999981,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 16.991,
      "layouts": 6,
      "layoutDurationMs": 0.5239999999999999,
      "taskDurationMs": 308.29,
      "heapDeltaBytes": 6507144,
      "domNodes": 79,
      "jsHeapTotalBytes": 15204352,
      "scriptDurationMs": 23.149,
      "eventListeners": 21,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 614.8570000000007,
      "styleRecalcs": 14,
      "styleRecalcDurationMs": 10.892,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 367.60900000000004,
      "heapDeltaBytes": 13129812,
      "domNodes": 23,
      "jsHeapTotalBytes": 15466496,
      "scriptDurationMs": 75.282,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 587.551000000019,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 7.695,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 354.782,
      "heapDeltaBytes": 13730336,
      "domNodes": 20,
      "jsHeapTotalBytes": 12845056,
      "scriptDurationMs": 70.713,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 612.7139999999827,
      "styleRecalcs": 15,
      "styleRecalcDurationMs": 11.236999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 355.75100000000003,
      "heapDeltaBytes": 13580740,
      "domNodes": 26,
      "jsHeapTotalBytes": 13369344,
      "scriptDurationMs": 66.50399999999999,
      "eventListeners": 26,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2032.4269999999842,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 12.987000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 530.858,
      "heapDeltaBytes": -10337084,
      "domNodes": 23,
      "jsHeapTotalBytes": 9056256,
      "scriptDurationMs": 109.70600000000002,
      "eventListeners": 28,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2010.423000000003,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 11.277000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 485.90000000000003,
      "heapDeltaBytes": -10397812,
      "domNodes": 23,
      "jsHeapTotalBytes": 8507392,
      "scriptDurationMs": 95.447,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2021.6519999999605,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.859999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 500.2989999999999,
      "heapDeltaBytes": -10026536,
      "domNodes": 22,
      "jsHeapTotalBytes": 9056256,
      "scriptDurationMs": 95.20500000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2099.3690000000242,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 16.53,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1127.4109999999998,
      "heapDeltaBytes": -203816,
      "domNodes": 16,
      "jsHeapTotalBytes": 11329536,
      "scriptDurationMs": 433.28,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2132.729999999981,
      "styleRecalcs": 71,
      "styleRecalcDurationMs": 17.619999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1015.593,
      "heapDeltaBytes": 6534604,
      "domNodes": 22,
      "jsHeapTotalBytes": 10543104,
      "scriptDurationMs": 395.738,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2070.945999999992,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 15.227000000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1015.1119999999997,
      "heapDeltaBytes": 1872064,
      "domNodes": 16,
      "jsHeapTotalBytes": 10285056,
      "scriptDurationMs": 383.815,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000073
    },
    {
      "name": "minimap-idle",
      "durationMs": 2018.0420000000368,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 10.297999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 512.134,
      "heapDeltaBytes": -10705620,
      "domNodes": 20,
      "jsHeapTotalBytes": 8245248,
      "scriptDurationMs": 97.54599999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "minimap-idle",
      "durationMs": 2016.9399999999769,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 9.629999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 477.843,
      "heapDeltaBytes": -11976752,
      "domNodes": 18,
      "jsHeapTotalBytes": 6410240,
      "scriptDurationMs": 87.64899999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "minimap-idle",
      "durationMs": 2001.6290000000367,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.521,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 473.29799999999994,
      "heapDeltaBytes": -11209636,
      "domNodes": 20,
      "jsHeapTotalBytes": 9056256,
      "scriptDurationMs": 89.519,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 560.3990000000181,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 12.759000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 370.442,
      "heapDeltaBytes": 12496500,
      "domNodes": 22,
      "jsHeapTotalBytes": 15728640,
      "scriptDurationMs": 126.41000000000003,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 555.6670000000281,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 12.232999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 350.032,
      "heapDeltaBytes": 13099736,
      "domNodes": 22,
      "jsHeapTotalBytes": 14680064,
      "scriptDurationMs": 124.28200000000001,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 553.9039999999886,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 11.354,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 357.478,
      "heapDeltaBytes": -11497116,
      "domNodes": 22,
      "jsHeapTotalBytes": 22282240,
      "scriptDurationMs": 118.27799999999999,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1994.416000000001,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.136999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 341.02299999999997,
      "heapDeltaBytes": 862908,
      "domNodes": 22,
      "jsHeapTotalBytes": 17039360,
      "scriptDurationMs": 17.720999999999997,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.6060000000002,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.924999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 347.024,
      "heapDeltaBytes": 858992,
      "domNodes": 22,
      "jsHeapTotalBytes": 17039360,
      "scriptDurationMs": 17.815,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1998.3899999999721,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.623999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 350.777,
      "heapDeltaBytes": 817792,
      "domNodes": 14,
      "jsHeapTotalBytes": 17039360,
      "scriptDurationMs": 18.638999999999996,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.659999999999947
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 2007.2999999999865,
      "styleRecalcs": 90,
      "styleRecalcDurationMs": 51.733000000000004,
      "layouts": 16,
      "layoutDurationMs": 4.423,
      "taskDurationMs": 967.1940000000001,
      "heapDeltaBytes": -7026544,
      "domNodes": 78,
      "jsHeapTotalBytes": 17825792,
      "scriptDurationMs": 108.55600000000001,
      "eventListeners": 30,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.680000000000017
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1771.09999999999,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 40.049,
      "layouts": 16,
      "layoutDurationMs": 4.5649999999999995,
      "taskDurationMs": 709.0970000000001,
      "heapDeltaBytes": -7095164,
      "domNodes": 65,
      "jsHeapTotalBytes": 17039360,
      "scriptDurationMs": 98.081,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1982.9859999999826,
      "styleRecalcs": 87,
      "styleRecalcDurationMs": 49.47899999999999,
      "layouts": 16,
      "layoutDurationMs": 4.539000000000001,
      "taskDurationMs": 958.2649999999999,
      "heapDeltaBytes": -7112480,
      "domNodes": 72,
      "jsHeapTotalBytes": 17563648,
      "scriptDurationMs": 108.849,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.660000000000036
    },
    {
      "name": "workflow-execution",
      "durationMs": 448.8839999999641,
      "styleRecalcs": 17,
      "styleRecalcDurationMs": 28.944000000000006,
      "layouts": 6,
      "layoutDurationMs": 2.028,
      "taskDurationMs": 140.21699999999998,
      "heapDeltaBytes": 4709680,
      "domNodes": 164,
      "jsHeapTotalBytes": 4194304,
      "scriptDurationMs": 34.125,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    },
    {
      "name": "workflow-execution",
      "durationMs": 427.59699999999157,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 24.717999999999996,
      "layouts": 5,
      "layoutDurationMs": 1.4529999999999998,
      "taskDurationMs": 120.67500000000003,
      "heapDeltaBytes": 4298788,
      "domNodes": 157,
      "jsHeapTotalBytes": 4456448,
      "scriptDurationMs": 26.050000000000004,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000027
    },
    {
      "name": "workflow-execution",
      "durationMs": 435.46099999997523,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 24.054,
      "layouts": 5,
      "layoutDurationMs": 1.435,
      "taskDurationMs": 116.71199999999999,
      "heapDeltaBytes": 4315700,
      "domNodes": 156,
      "jsHeapTotalBytes": 4456448,
      "scriptDurationMs": 25.865000000000002,
      "eventListeners": 55,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.65999999999999
    }
  ]
}

Copy link
Member

@viva-jinyi viva-jinyi left a comment

Choose a reason for hiding this comment

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

SEO Suggestions (non-blocking)

Nice additions — sitemap, robots.txt, and JSON-LD cover the essentials. Here are a few low-effort improvements to consider:

1. OG image dimensions — BaseLayout.astro

Adding explicit width/height lets social crawlers render the preview layout without downloading the image first.

<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

2. Twitter site tag — BaseLayout.astro

Links shared cards back to the Comfy account automatically.

<meta name="twitter:site" content="@comaboratory" />

3. External link rel consistency — SiteFooter.vue

Social icons already have target="_blank" rel="noopener noreferrer", but the column links (Comfy Cloud, ComfyHub, Docs, Blog, GitHub) are external URLs without them. This is a tabnabbing vector and inconsistent with the social links.

<a
  v-for="link in column.links"
  :key="link.href"
  :href="link.href"
  :target="link.href.startsWith('http') ? '_blank' : undefined"
  :rel="link.href.startsWith('http') ? 'noopener noreferrer' : undefined"
>

4. Preconnect hint for GTM — BaseLayout.astro

Saves a DNS lookup + TLS handshake before the GTM script loads, helps LCP.

<link rel="preconnect" href="https://www.googletagmanager.com" />
<link rel="dns-prefetch" href="https://www.googletagmanager.com" />

5. Per-page noindex prop — BaseLayout.astro

Pages like Terms of Service and Privacy Policy don't need indexing. Currently there's no way to control this per-page.

interface Props {
  title: string
  description?: string
  ogImage?: string
  noindex?: boolean
}
{noindex && <meta name="robots" content="noindex, nofollow" />}

Copy link
Collaborator

@dante01yoon dante01yoon left a comment

Choose a reason for hiding this comment

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

Description is empty?

redirects: {
'/pricing': '/cloud/pricing',
'/enterprise': '/cloud/enterprise',
'/blog': 'https://blog.comfy.org/',
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: Astro generates HTML page with for this. That's a client-side redirect — worse for SEO than a proper HTTP 301. Since the site is deployed to Vercel, all redirects (especially external ones) would be
better served from vercel.json where they become edge-level 301s:

  "redirects": [
    { "source": "/blog", "destination": "https://blog.comfy.org/", "permanent": true },
    { "source": "/pricing", "destination": "/cloud/pricing", "permanent": true },
    // ...
  ]

This is ironic given the PR's purpose is SEO improvement? would better moving all redirects to vercel.json.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants