Files
open-design/.gitignore
T
lefarcenandGitHub 101a42cb0d perf(landing): cut homepage LCP — defer JS + below-fold art, shrink backdrops, early locale redirect (#4987)
* perf(landing): load matter-js + cobe on demand, not inlined

The homepage inlined the full matter-js physics engine (~83KB) and the
cobe globe runtime (~13KB) into every document, even though both are
below-the-fold decorations. That shipped ~96KB of decorative JavaScript
in the critical HTML to every visitor — phones and reduced-motion
readers included — inflating the document the browser must download and
parse before the page settles.

Vendor both runtimes to public/enhancers/*.js (regenerated from
node_modules on every build/dev, so they always match the installed
versions) and inject each one via IntersectionObserver only when its
section nears the viewport. Reduced-motion readers skip the matter-js
download entirely and just get the server-rendered banner.

The site still ships zero Astro-bundled / ES-module JavaScript — these
are hand-vendored classic scripts injected by a runtime-created script
element, so the `Verify zero external JavaScript` gate stays green.

Homepage document: 358KB -> 264KB (brotli wire 94KB -> 51KB); the two
runtimes now load lazily, cached immutably per versioned URL.

* perf(landing): defer below-the-fold homepage art off the LCP path

The Cloudflare RUM network trace showed homepage LCP (~2.4s, the hero
webp) was starved of bandwidth: ~1.9MB of images — most of it below the
fold — was fetched in the first ~700ms and competed with the hero for
the connection. Culprits: two full-bleed section backdrops loaded via
CSS `::before` (lab-stage-art 454KB, cta-bg 335KB), the Labs dock
preloading ~500KB of preview stills on init, the contributor orbit
building ~350KB of avatars on init, and a few raw eager <img>.

Defer all of it until its section nears the viewport:

- precise-lazyload gains a `data-precise-bg` mode (adds `.precise-bg-in`,
  which the CSS uses to gate the `::before` background-image — you can't
  set a pseudo-element's background from JS) and a configurable
  `imgRootMargin`. The homepage mounts it at 600px instead of the catalog
  default 1500px so its heavy art doesn't join the initial burst.
- A shared `__whenNear(selector, cb)` helper gates the Labs dock
  (`enhanceLabSwitch`) and contributor orbit (`enhanceContributorOrbit`)
  init behind an IntersectionObserver.
- `cta-window` gets `loading="lazy"`; the near-fold hero product shot gets
  `fetchpriority="low"` so the hero-bg (the LCP element) wins the pipe.

Verified in a headless browser against the built site: initial requests
drop 50 -> 20, the ~1.76MB of below-fold art is withheld until scrolled
to (then loads on cue), CLS stays 0.001, and every enhancer still works
(section backdrops paint, Labs dock switches, falling-text physics runs
23/23, globe renders, 16 contributor avatars build).

* perf(landing): shrink oversized homepage backdrops to display size

The two full-bleed section backdrops shipped far more pixels than they
ever render: cta-bg was 2776×1554 (335KB) and lab-stage-art 2262×1358
(454KB), but both display at roughly 960px CSS — 1920px covers even a 2×
retina panel. Re-encoded at 1920px wide, q82 webp:

  cta-bg.webp        335KB → 174KB  (-48%)
  lab-stage-art.webp 454KB → 276KB  (-39%)

~340KB saved with no visible quality loss (verified at 2× device scale —
the painterly murals stay crisp). Only the pixel dimensions dropped; the
quality factor is high. The homepage's gated `background-image` URLs bump
their `?v=` so the immutable edge cache serves the new files immediately.

* perf(landing): redirect locale from <head> so the wrong-locale first load aborts

A non-English visitor hitting the English root ran the locale
auto-redirect from a <script> late in <body>, so the browser had already
streamed most of the document (and kicked off its head resources) before
bouncing to /zh/ etc. — a wasted near-full first load.

Move the auto-redirect decision to the top of <head> (first thing after
the viewport/theme-color meta) so it fires as the document starts
streaming and aborts the rest of the wrong-locale load. The switcher UI
wiring, which needs the DOM, now defers itself to DOMContentLoaded, so it
works whether the script runs in <head> (homepage) or late in <body>
(other layouts). All existing guards are untouched — canonical-only pages,
autoredirect-off pages, and already-localized roots still no-op, so
English and crawler traffic pays only a tiny inline read.

Verified: root / still redirects to /zh/ for a zh browser with no loop,
and all 11 language-switcher links bind correctly post-DOMContentLoaded.
2026-07-01 06:49:03 +00:00

100 lines
2.4 KiB
Plaintext

node_modules/
dist/
out/
.next/
.next-*/
.tmp/
.DS_Store
*.log
/OpenDesign.exe
.vite
.astro/
.wrangler/
.vscode
# Local runtime data — auto-created by the daemon on first start.
# Holds app.sqlite (project metadata), projects/<id>/ (per-project artifacts,
# the agent's CWD), and artifacts/ (one-off renders). Never commit.
.od
.od-e2e
test-results
playwright-report
e2e/.od-data
e2e/playwright-report
e2e/reports/html
e2e/reports/playwright-html-report
e2e/reports/test-results
e2e/reports/results.json
e2e/reports/junit.xml
e2e/reports/latest.md
e2e/ui/.od-data
e2e/ui/reports
e2e/ui/test-results
apps/web/playwright/
# Playwright MCP capture output (YAML page snapshots + PNG screenshots).
# Written ad-hoc during local visual checks; never commit — these hold local
# UI/session state and are not a maintained test fixture.
.playwright-mcp/
# Legacy folder name from before the rename; keep ignored so existing
# clones don't accidentally stage stale runtime data.
.ocd
tsconfig.tsbuildinfo
# Generated from the checked-in compressed vendor source during postinstall.
apps/desktop/vendor/dom-to-pptx/dom-to-pptx.bundle.js
**/.claude-sessions/*
.cursor/
.agents/
.opencode/
.claude/*
# Exception: od-contribute skill ships with the repo so the OD app can mount it
# for non-coder contributors. Personal Claude state (sessions, settings, etc.) stays ignored.
!.claude/skills/
.claude/skills/*
!.claude/skills/od-contribute/
!.claude/commands/
.claude/commands/*
!.claude/commands/od-contribute.md
.codex/
.deepseek/
.antigravitycli/
# Commander task scratchpad; keep local task notes out of git by default.
.task/
task.md
specs/change/active
.ralph/
docs/superpowers/
# Nix and direnv
.direnv/
.envrc
# Local secrets (PostHog keys, BYOK creds for local testing, etc.)
.env.local
.env.*.local
# Local design assistant context
.impeccable.md
# Landing-page preview thumbnails — regenerated by CI from
# `skills/<slug>/example.html` and `templates/live-artifacts/<slug>/`
# on every deploy. Should not be committed (~70MB of PNGs).
apps/landing-page/public/previews/
# On-demand enhancement runtimes (matter-js / cobe), regenerated from
# node_modules by `scripts/vendor-enhancers.ts` on every build/dev. Generated
# artifact, not source — must not be committed.
apps/landing-page/public/enhancers/
# Ad-hoc local e2e scripts and their screenshots
e2e/scripts/test-fal-webui.ts
e2e/scripts/fal-webui-*.png
growth/**
# Agent long-running task memory
.task/