Skip to content

fix: align eager, lazy, and LCP image hints with HTML standard - #141

Merged
seonghobae merged 6 commits into
mainfrom
bolt-lcp-svg-optimization-4787273986862805491
Aug 7, 2026
Merged

fix: align eager, lazy, and LCP image hints with HTML standard#141
seonghobae merged 6 commits into
mainfrom
bolt-lcp-svg-optimization-4787273986862805491

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What

  • Removes forced decoding="async" from eager first-viewport images and the single declared LCP candidate, leaving the standards-defined missing-value default (auto) to the user agent.
  • Retains decoding="async" for explicitly lazy-loaded images.
  • Adds non-vacuous regression contracts for eager, lazy, and high-priority LCP-candidate image sets.
  • Records the decision, limitations, measurement plan, and APA 7th references in docs/doctoring/image-rendering-hints.md.

Why

The HTML Living Standard defines decoding as a preference hint whose missing-value default is auto. It does not guarantee a main-thread or background-thread execution path. fetchpriority independently influences fetch priority. This PR therefore avoids claiming that removing decoding="async" alone universally improves LCP.

Verification required at the exact current head

  • python3 -m pytest tests/
  • all required security, static-analysis, and repository-policy checks
  • independent non-author approval on the exact current head
  • deployment performance measured separately under representative device and network conditions

References

  • WHATWG. (2026, July 20). HTML living standard: The img element.
  • Osmani, A., Sohoni, L., Meenan, P., & Pollard, B. (2023, November 14). Optimize resource loading with the Fetch Priority API. web.dev.

This PR supersedes the stale duplicate #100 by carrying forward its valid standards reasoning and stronger non-vacuous tests on the current base.

Summary by CodeRabbit

  • 성능 개선

    • 첫 화면의 주요 이미지가 브라우저 기본 디코딩 전략을 사용하도록 조정했습니다.
    • 지연 로딩 이미지는 비동기 디코딩을 유지하고, 단일 LCP 후보에는 높은 우선순위를 적용했습니다.
  • 문서

    • 이미지 렌더링 힌트와 성능 측정 기준을 문서화했습니다.
  • 테스트

    • 이미지 로딩·디코딩 동작과 주요 섹션의 레이아웃 안정성 검증을 강화했습니다.

@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

홈페이지 이미지 렌더링 힌트 계약을 정리했습니다. eager 이미지는 기본 auto 디코딩을 사용합니다. lazy 이미지는 decoding="async"를 유지합니다. 단일 LCP 후보의 fetchpriority="high" 규칙, 관련 문서, 마크업, 정적 회귀 테스트를 함께 갱신했습니다.

Changes

이미지 렌더링 힌트

Layer / File(s) Summary
렌더링 힌트 계약
docs/doctoring/image-rendering-hints.md, .jules/bolt.md, CHANGELOG.md
이미지 로딩, fetchpriority, decoding 계약을 문서화했습니다. 단일 LCP 후보는 fetchpriority="high"를 사용합니다. eager 이미지는 decoding을 생략합니다. lazy 이미지는 decoding="async"를 유지합니다. 정적 테스트 계약과 배포 후 측정 한계도 기록했습니다.
홈페이지 이미지 마크업
index.html
브랜드 로고와 히어로 이미지에서 decoding="async" 속성을 제거했습니다. 다른 로딩 동작과 속성은 유지됩니다.
이미지 회귀 검증
tests/test_styles.py
홈페이지 img 속성을 수집하는 헬퍼와 타입 힌트를 추가했습니다. eager 이미지의 decoding 생략, lazy 이미지의 decoding="async", 단일 LCP 후보의 eager·high-priority 조건을 각각 검증합니다. 기존 CSS 회귀 테스트는 유지하고, tall section의 contain-intrinsic-size 검증을 추가했습니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 eager, lazy 및 LCP 이미지 힌트 정합성 개선이라는 주요 변경 사항을 간결하고 정확하게 설명합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bolt-lcp-svg-optimization-4787273986862805491

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

@opencode-agent

opencode-agent Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: 1511c06d3e4a1dba9b6207ffb1e9f60cd3001507
  • Workflow run: 31189203709
  • Workflow attempt: 1
  • Gate result: APPROVE (exit 0)

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Changed file (3 files)"]
  S1 --> I1["repository behavior"]
  I1 --> R1["Review risk: Changed file (3 files)"]
  R1 --> V1["required checks"]
  Evidence --> S2["Docs: image-rendering-hints.md"]
  S2 --> I2["operator or user guidance"]
  I2 --> R2["Review risk: Docs: image-rendering-hints.md"]
  R2 --> V2["docs review"]
  Evidence --> S3["Test: test_styles.py"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test: test_styles.py"]
  R3 --> V3["targeted test run"]
Loading

@opencode-agent opencode-agent 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.

Pull request overview

OpenCode reviewed the current-head bounded evidence and found no blocking issues.

Findings

No blocking findings.

Summary

APPROVE for PR #141 head 1511c06. Changed files inspected as evidence: index.html (decoding="async" removed from the eager brand avatar and hero context-art img; fetchpriority="high" and lazy hints retained), tests/test_styles.py (non-vacuous eager/lazy/LCP contracts), docs/doctoring/image-rendering-hints.md (new decision doc with WHATWG/web.dev references), CHANGELOG.md, .jules/bolt.md. Approval sufficiency: diff, tests, docs, and changelog all agree with the PR intent; failed-check evidence reports no completed failed checks and no unresolved review threads. Verification posture: trusted Coverage execution evidence reports Result: PASS with supported repository test suites passed at head; no OPENCODE_EXECUTION_RECEIPT runtime lines exist in bounded evidence, so no browser/runtime execution claim is made. Linter/static: no active failed checks; lint_commands names only hadolint Dockerfile (unchanged by this PR). TDD/regression: new assertions guard against vacuous passes (tests/test_styles.py:69 eager non-empty, :79 lazy non-empty and decoding==async, :91-94 exactly one fetchpriority=high candidate that is not lazy and has no decoding). Coverage: cites Coverage execution evidence Result: PASS (supported repository test suites passed). Docstring coverage: configured repository docstring gates passed or docstring coverage was advisory per Coverage execution evidence. DAG: flowchart 'index.html img attributes -> _homepage_images() -> eager/lazy/LCP assertions -> image-rendering-hints.md contract' reflects head-state static-markup flow (CodeGraph has no node for the HTML surface, so it is source-backed). PoC/execution: the coverage gate is the only trusted test outcome supplied; no standalone PoC receipts exist. DDD/domain: static-site domain unchanged, no domain-model impact. CDD/context: no new context or data boundary introduced. Similar issues: prior commits 9d54cd8 (preserve async decoding) and 00acc72 (Bolt LCP optimization) are superseded by the corrected hint policy, consistent with the new .jules/bolt.md entry. Claim/concept check: omitting decoding selects the HTML Standard auto missing-value default (preference hint, not guaranteed thread path), matching the cited WHATWG spec; docs disclaim a guaranteed LCP gain. Standards search: WHATWG HTML img element and web.dev Fetch Priority reference cited at docs/doctoring/image-rendering-hints.md:26. Compatibility/convention: no new identifiers or reserved words introduced; removed attributes do not alter tests/test_index_security.py CSP/inline-content checks. Breaking-change/backcompat: static markup only; a missing attribute is the valid default in all browsers; no API or backcompat surface. Implementation completeness: markup, tests, docs, and changelog are all updated; no placeholder or TODO-only code introduced. Performance: hint removal on two eager SVGs; real-world LCP claims are deliberately bound to post-deployment measurement. Developer experience: .jules/bolt.md learning entry and the decision doc record corrected reasoning for future maintainers. User experience: no visible rendering change; alt/aria attributes preserved. Visual/DOM: static attribute change reviewed via focused hunks; no Playwright/DOM/ARIA receipts exist in bounded evidence. Accessibility/i18n: hero img retains aria-hidden="true" and the brand avatar retains alt=""; data-i18n nodes untouched. Supply-chain/license: zero dependency changes. Packaging: unpackaged_source_surfaces lists pre-existing Python/Node test surfaces; the changed tests are executed by the trusted coverage gate, so the missing manifest predates this PR and adds no new risk. Security/privacy: no new sinks, no inline content, CSP-adjacent surface unchanged. Non-blocking nit noted in prose: inline comments at index.html:32/:69 still say 'for SVG/LCP optimization', slightly overstating the corrected claim.

Approval sufficiency: bounded evidence supplied affirmative approval evidence for changed files, coverage/docstring posture, risk surfaces, and current-head verification; approval is not based merely on the absence of known blockers.
Verification posture: CodeGraph evidence was initialized and bounded current-head evidence reviewed for changed-file evidence including .jules/bolt.md, CHANGELOG.md, docs/doctoring/image-rendering-hints.md, index.html, tests/test_styles.py.
Linter/static: workflow/static review evidence is bounded by the current-head GitHub Checks gate and changed-file evidence.
TDD/regression: coverage execution evidence and focused changed hunks were reviewed from bounded-review-evidence.md.
Coverage: coverage execution evidence reports supported repository test suites passed.
Docstring coverage: coverage execution evidence reports configured repository docstring gates passed or docstring coverage was advisory.
DAG: CodeGraph/source-backed behavior map connects .jules/bolt.md to the affected review, runtime, or workflow path and required checks.
PoC/execution: coverage-evidence job executed on the current head and reported PASS.
DDD/domain: workflow and repository-governance invariants were reviewed against changed files in bounded evidence.
CDD/context: CodeGraph evidence, changed-file history, and focused hunks were reviewed from bounded-review-evidence.md.
Similar issues: changed-file history evidence was reviewed for comparable local precedents.
Claim/concept check: bounded evidence, repository source, current-head workflow evidence, and, where numeric, scientific, statistical, or literature-backed claims are affected, original-paper/formula evidence and parameter-recovery expectations were used for claims.
Standards search: standards and external-source claims require trusted bounded source evidence prepared outside the isolated model process; no evidence-backed standards blocker is present in bounded evidence.
Compatibility/convention: changed workflow/script conventions, object naming, and reserved-word safety for schema/API/config/code surfaces were checked in bounded evidence.
Breaking-change/backcompat: deployment evidence and changed-file history were checked for backward-compatibility risk.
Performance: changed surfaces were checked for performance risk in bounded evidence.
Developer experience: changed automation, review, test, setup, and maintenance surfaces were checked for helpful or obstructive DX impact in bounded evidence.
User experience: connected user, operator, API, CLI, documentation, review-comment, status-check, rendering, and workflow-reader behavior was checked for contradictions against code, docs, and tests in bounded evidence.
Visual/DOM: deterministic repair does not infer browser runtime execution; source-backed DOM/UI evidence and trusted workflow receipts were reviewed when present, and non-web surfaces used API/CLI/log/docs/workflow evidence instead.
Accessibility/i18n: accessibility, localization, and human-readable text surfaces were checked where UI, CLI, API message, docs, logs, or review text changed.
Supply-chain/license: dependency, package, model, container, and external-tool changes were checked in bounded evidence.
Packaging: package, build, test, lint, and security contracts were checked in bounded evidence.
Security/privacy: workflow-token, review-gate, and repository-automation security/privacy boundaries were checked in bounded evidence.

Adversarial validation

{"status":"passed","probes":[{"path":"index.html","line":32,"hypothesis":"The eager-image decode contract could be broken or pass vacuously: if the patch had stripped decoding from below-the-fold lazy images or left decoding on the eager brand avatar, test_eager_images_leave_decoding_to_the_user_agent / test_lazy_images_decode_asynchronously would fail or silently pass on an empty set.","attack_or_counterexample":"Removed decoding attribute from the wrong image set (all images, or lazy ones) or left the eager set empty so assertions never executed.","evidence":"Trusted Coverage execution evidence for head 1511c06d3e4a1dba9b6207ffb1e9f60cd3001507 reports Result: PASS with supported repository test suites passed, and the CodeGraph verbatim source of tests/test_styles.py:69,79-80 shows the eager set must be non-empty and every lazy image must keep decoding==async; the focused diff removes decoding only from the two eager imgs (brand avatar and hero) and the source trace at index.html:32 shows the eager brand avatar img now omits the attribute exactly as the executed contract requires. source-line-sha256=0c189ea83be403e5803e357b561292edb1f9594ead7a0c2836f4f8606abe1be5","outcome":"falsified"},{"path":"index.html","line":69,"hypothesis":"The single-LCP-candidate contract is not actually enforced: if the hero img had lost fetchpriority=\"high\" or a second img had gained fetchpriority=\"high\", test_lcp_candidate_is_eager_and_high_priority would fail or pass vacuously.","attack_or_counterexample":"Mutated markup where the hero context-art img drops fetchpriority or another img carries fetchpriority=high, breaking the len(lcp_candidates)==1 and decoding-is-None assertions.","evidence":"Trusted Coverage execution evidence PASS at head confirms the suite (including tests/test_styles.py:91-94: exactly one fetchpriority=high candidate, loading != lazy, decoding is None) executed green; the focused diff at index.html:69 shows the hero context-art img retains fetchpriority=\"high\" and drops only decoding with no loading=\"lazy\" added, and no other img attribute changes appear in the diff, so the single-candidate and eager contract is enforced by the executed tests rather than by accident. source-line-sha256=2d353b4da37fe79a7fdeb94337b786ffafde13797e39073fa68f1f362d8ecd1b","outcome":"falsified"}],"residual_risk":"The real-world LCP effect of dropping decoding=\"async\" is intentionally unmeasured at head (deferred to post-deployment field/lab measurement per docs/doctoring/image-rendering-hints.md); the inline HTML comments at index.html:32 and :69 still carry the older 'SVG/LCP optimization' phrasing that slightly overstates the corrected, evidence-bounded claim. Both items are cosmetic or measurement-bound with no functional, security, or compatibility risk."}
  • Result: APPROVE
  • Reason: Static image-hint change aligns with the HTML Standard's auto missing-value default, is covered by non-vacuous regression tests that passed the trusted coverage gate, is documented with evidence-bounded claims, and has no failed checks or unresolved threads.
  • Head SHA: 1511c06d3e4a1dba9b6207ffb1e9f60cd3001507
  • Workflow run: 31189203709
  • Workflow attempt: 1

@seonghobae
seonghobae merged commit 4a0fcc6 into main Aug 7, 2026
29 checks passed
@seonghobae
seonghobae deleted the bolt-lcp-svg-optimization-4787273986862805491 branch August 7, 2026 15:34
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.

1 participant