docs: comprehensive SEO and AEO improvements#79
Conversation
SEO/AEO Improvements: - Add Schema.org JSON-LD structured data to SEO component - Add og:type, og:image, og:site_name, twitter:card meta tags - Add AI crawler guidance to robots.txt (GPTBot, Claude-Web, etc.) - Add llms.txt link references to index.html - Update sitemap.xml lastmod dates to 2026-02-11 - Expand keywords in index.html Introduction Page Enhancements: - Add compelling hero section with headline "Stop Rewriting IAP Code" - Add stats badges (4K+ Combined Stars, 5+ Framework Libraries) - Add CTA buttons (Get Started, View on GitHub) - Add key benefits cards (1x Learn Once, 0 Runtime Errors, 100% Platform Features) - Add visual problem section with color-coded pain point cards - Add solution section with feature grid and ecosystem diagram - Add supported platforms grid (iOS/macOS/Vision Pro, Android, Meta Quest) - Add implementations list with all 5 framework libraries - Remove duplicate "Why Teams Choose" section Home Page: - Update hero subtitle with clearer value proposition - Add matching stats badges Other Pages: - Add SEO component to 404 page - Improve keywords for tutorials, sponsors, languages pages - Add godot-iap to languages description Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughUpdated docs site SEO metadata and JSON‑LD, added LLM context links and AI-crawler rules, extended the SEO React component with image and SoftwareApplication schema support, refreshed SEO across many pages, and substantially redesigned the Home and Introduction pages with new content and styles. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser
participant Docs as Docs Site
participant Crawler as AI/Search Crawler
participant LLMS as LLMS Service
Browser->>Docs: GET /
Docs-->>Browser: HTML with meta, Open Graph, JSON‑LD, alternate links (/llms.txt, /llms-full.txt)
Crawler->>Docs: GET /robots.txt
Docs-->>Crawler: robots.txt (disallow/allow rules + allow /llms*.txt)
Crawler->>Docs: GET /llms-full.txt
Docs-->>Crawler: llms-full.txt (LLM context file)
Crawler->>Docs: GET / (fetch page for indexing)
Docs-->>Crawler: page + JSON‑LD + OG + linked LLM context for agent indexing
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 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. Comment |
Summary of ChangesHello @hyochan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly upgrades the OpenIAP documentation website's discoverability and user engagement. It focuses on improving how search engines and AI crawlers understand and present the content, while also revamping the introductory experience for human visitors. The changes aim to provide a more structured, informative, and accessible overview of the OpenIAP project and its benefits. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/docs/index.html (1)
44-79:⚠️ Potential issue | 🟠 MajorDuplicate SoftwareApplication JSON-LD: this block and SEO.tsx both emit it.
index.htmlembeds aSoftwareApplicationschema inside theWebSiteJSON-LD (line 68-77), andSEO.tsxinjects a separate top-levelSoftwareApplicationJSON-LD on every page viareact-helmet-async(SEO.tsx lines 32-50, 76). This results in two competingSoftwareApplicationstructured data blocks on every page.Consider removing the
SoftwareApplicationfrom one location — either here (keep theWebSite-only schema inindex.html) or fromSEO.tsx— to avoid duplicate/conflicting structured data that could confuse Google's rich results parser.
🤖 Fix all issues with AI agents
In `@packages/docs/public/robots.txt`:
- Around line 8-15: The User-agent blocks for GPTBot, ChatGPT-User, Claude-Web,
Anthropic-AI, PerplexityBot and Google-Extended only include "Allow: /llms.txt"
which causes those crawlers to treat all other paths as disallowed; update each
of those User-agent sections (the lines containing "User-agent: GPTBot",
"User-agent: ChatGPT-User", "User-agent: Claude-Web", "User-agent:
Anthropic-AI", "User-agent: PerplexityBot", "User-agent: Google-Extended") to
include "Allow: /" so they can crawl the full site for SEO, or if the true
intent is to restrict them, add "Disallow: /" explicitly and remove the
misleading comment block that implies they should have access.
In `@packages/docs/src/components/SEO.tsx`:
- Around line 32-50: The SoftwareApplication JSON-LD (schemaOrg) is being
injected by the SEO component on every page; change SEO to accept a new boolean
prop (e.g., includeAppSchema, default false) and render the schemaOrg <script>
only when includeAppSchema is true (leave all other meta/head behavior intact);
update the place(s) that should expose the app schema (e.g., the
homepage/introduction page) to pass includeAppSchema={true} when rendering the
SEO component so only those pages emit the SoftwareApplication JSON-LD.
In `@packages/docs/src/pages/introduction.tsx`:
- Around line 111-150: The inline style uses transition ('all 0.2s ease') on the
Link component and the anchor ("Get Started" and "View on GitHub") but inline
styles cannot define :hover, so move the transition and hover styles into a CSS
class (or your CSS-in-JS styles) and apply that class to the Link and <a>
elements (also update the implementation cards and native module cards
referenced) so the transition actually triggers on :hover; alternatively remove
the transition entries from the inline style objects if you prefer not to add
hover behavior.
- Around line 390-446: The three callout blocks using hardcoded hex colors (the
divs containing the headings "Wasted Time", "Production Bugs", and "Duplicated
Effort") should be updated to use CSS custom properties instead of raw hex
values; replace occurrences of '#e74c3c', '#e67e22', and '#9b59b6' in both
borderLeft and strong color with variables like var(--color-danger),
var(--color-warning), and var(--color-accent), and add those token definitions
to the theme tokens alongside existing variables so they respond to dark/light
theme switching.
- Around line 60-83: Change the misleading single-repo link and hardcoded label:
update the anchor (the <a> with href
"https://github.com/dooboolab-community/react-native-iap") to point to a more
representative destination (e.g., the org page or a collective topic URL) or
else change the label text "Combined Stars" to explicitly reference the repo
(e.g., "★ on react-native-iap"); also add an inline code comment next to the
"4K+" display div reminding maintainers that the hardcoded count requires
periodic updates or replace it later with a dynamic value.
🧹 Nitpick comments (6)
packages/docs/public/sitemap.xml (1)
5-5: Hardcoded lastmod dates will become stale.All
lastmodvalues are manually set to2026-02-11. These will drift out of date as content changes. Consider generating the sitemap at build time (e.g., via a script or plugin) so thatlastmodreflects actual file modification timestamps.packages/docs/src/components/SEO.tsx (1)
18-25: Missing JSDoc comment for exportedSEOfunction.As per coding guidelines, "Add JSDoc comments for public functions and exported APIs".
Proposed fix
+/** Renders page-level SEO metadata including Open Graph, Twitter cards, and optional Schema.org JSON-LD. */ function SEO({ title, description, path = '', keywords, type = 'website', image, }: SEOProps) {packages/docs/src/pages/home.tsx (1)
60-83: Hardcoded stats will become stale; "Combined Stars" link targets a single repo.Two concerns:
- "4K+" and "5+" are hardcoded and will silently become inaccurate over time.
- The "Combined Stars" label implies an aggregate across repos, but the link points only to
react-native-iap. A user clicking through may see a different star count and feel misled.Consider either linking to the GitHub org/topic page that shows all repos, or adding a comment noting when these values should be updated.
packages/docs/src/pages/introduction.tsx (3)
4-4: Missing JSDoc on exported component.The coding guidelines require JSDoc comments for public functions and exported APIs. A brief description of this page component would help.
📝 Suggested JSDoc
+/** Marketing-focused introduction page showcasing OpenIAP's value proposition, supported platforms, and implementations. */ function Introduction() {As per coding guidelines, "Add JSDoc comments for public functions and exported APIs".
Also applies to: 936-936
163-237: Extensive inline-style duplication — extract shared styles into CSS classes or constants.The card pattern (
padding,background,borderRadius,border) is copy-pasted ~15 times across benefit cards, feature cards, risk cards, implementation cards, and platform cards. The same grid container style appears 5+ times as well.This makes future visual tweaks error-prone (change one card, miss the rest) and inflates the file to 930+ lines.
Consider either:
- CSS classes in the existing stylesheet (preferred for a docs site).
- At minimum, local style constants at the top of the component.
♻️ Example: extract style constants
function Introduction() { + const cardStyle: React.CSSProperties = { + padding: '1.5rem', + background: 'var(--bg-secondary)', + borderRadius: '12px', + border: '1px solid var(--border-color)', + }; + + const gridStyle: React.CSSProperties = { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', + gap: '1.25rem', + }; + + const cardTextStyle: React.CSSProperties = { + margin: 0, + fontSize: '0.9rem', + color: 'var(--text-secondary)', + lineHeight: '1.6', + }; + return (Then replace each repeated object with the shared constant:
- <div - style={{ - padding: '1.5rem', - background: 'var(--bg-secondary)', - borderRadius: '12px', - border: '1px solid var(--border-color)', - }} - > + <div style={cardStyle}>Also applies to: 511-629, 734-862
658-658: Add accessible labels to emoji icons.Emoji like 🍎, 🤖, and 🎮 are read literally by screen readers (e.g., "red apple" instead of "Apple"). Wrap them with
role="img"andaria-labelfor clarity.♿ Example fix
- <span style={{ fontSize: '1.5rem' }}>🍎</span> + <span style={{ fontSize: '1.5rem' }} role="img" aria-label="Apple">🍎</span>Also applies to: 684-684, 708-708
- Rewrite introduction.tsx as "Why OpenIAP" with technical documentation style - Architecture diagram and code generation explanation - API design with naming conventions table - Type safety examples with code blocks - Purchase flow code example - Platform support and framework implementation tables - Add marketing benefits section to home.tsx - 1x Learn Once, 0 Runtime Errors, 100% Platform Features cards Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive set of SEO and AEO improvements across the documentation site. The changes include enhancing meta tags, adding structured data (Schema.org JSON-LD), and supporting AI crawlers via llms.txt. The introduction page has been completely redesigned for better user engagement.
Overall, these are excellent improvements for search visibility and content presentation. I have a few suggestions to improve maintainability and correctness:
- The
sitemap.xmlis using future dates forlastmod, which should be corrected. - The new components in
home.tsxandintroduction.tsxmake heavy use of inline styles. These should be extracted to CSS files to improve code maintainability.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@packages/docs/src/pages/home.tsx`:
- Around line 60-83: The "4K+ Combined Stars" anchor currently points to the
react-native-iap repo; update its href to a URL that actually represents the
combined OpenIAP ecosystem (e.g., the GitHub org or the OpenIAP aggregate repo)
and ensure the anchor text/label remains accurate; locate the <a
href="https://github.com/dooboolab-community/react-native-iap" ...> element in
home.tsx and replace the href with the org or aggregate repo URL (or change the
label to reflect a single-repo link) so the link destination matches the
"Combined Stars" claim.
In `@packages/docs/src/pages/introduction.tsx`:
- Around line 163-171: The <img> element rendering the ecosystem image (src
"/ecosystem.png") lacks explicit dimensions causing layout shift; update the
<img> tag in introduction.tsx to include either width and height attributes
matching the natural image size or apply a CSS aspect-ratio (and corresponding
maxWidth/border styles) so the browser can reserve space (e.g., add width and
height attributes on the img or add style: { aspectRatio: 'WIDTH/HEIGHT', width:
'100%', ... }) to eliminate CLS.
- Around line 231-298: Replace the outer anchor wrappers for the native module
cards with non-anchor containers so you don't nest anchors: change the outer <a>
elements that wrap the "openiap-apple" and "openiap-google" card blocks into
<div> elements (keep their inline style objects, classNames and any ARIA/role
attributes you need) and remove href/target/rel from those outer wrappers; leave
the inner <a> links to StoreKit and Play Billing intact so only the inner
anchors are real links and click behavior is predictable.
🧹 Nitpick comments (5)
packages/docs/src/pages/home.tsx (3)
215-316: Extract repeated inline styles into shared constants or CSS classes.The three benefit cards repeat the same inline style objects verbatim (card wrapper, stat number, and description paragraph). This adds ~60 lines of duplication. Consider extracting them:
♻️ Example refactor
+const benefitCardStyle: React.CSSProperties = { + padding: '2rem', + background: 'var(--bg-secondary)', + borderRadius: '12px', + border: '1px solid var(--border-color)', + textAlign: 'center' as const, +}; + +const benefitStatStyle: React.CSSProperties = { + fontSize: '2.5rem', + fontWeight: '700', + color: 'var(--primary-color)', + marginBottom: '0.5rem', +}; + +const benefitDescStyle: React.CSSProperties = { + margin: 0, + fontSize: '0.9rem', + color: 'var(--text-secondary)', + lineHeight: '1.6', +};Then each card becomes:
<div style={benefitCardStyle}> <div style={benefitStatStyle}>1x</div> <h3 style={{ margin: '0 0 0.5rem', fontSize: '1.1rem' }}>Learn Once</h3> <p style={benefitDescStyle}> Master one API instead of learning different patterns for iOS, Android, and every framework </p> </div>Or better yet, extract a small
BenefitCardcomponent and map over a data array, similar to theLIBRARIES.mappattern already used on line 497.
579-588: Prefer CSS:hoverover imperative style manipulation.The
onMouseEnter/onMouseLeavehandlers imperatively settransformandboxShadow. A CSS class with a:hoverpseudo-class would be simpler, more maintainable, and handle edge cases (e.g., rapid hover toggling) more reliably. This is consistent with how other interactive elements in this file already use CSS classes (e.g.,btn btn-primary).
50-103: Consider making the Quick Stats data-driven too.The two stat items share identical styling for the number and label. A small array +
.map()would reduce duplication and make it easy to add/update stats later — consistent with theLIBRARIES.mappattern used on line 497.packages/docs/src/pages/introduction.tsx (2)
4-4: Missing JSDoc on exported component.As per coding guidelines: "Add JSDoc comments for public functions and exported APIs."
A brief JSDoc would satisfy the guideline:
/** Why OpenIAP — marketing-focused introduction page. */ function Introduction() {
310-365: Heavily duplicated inline style objects throughout the file.The same style literals are repeated dozens of times — table cell padding (
padding: '0.75rem 1rem') appears ~30 times, the code-block container style appears 3 times, and the card style appears 6+ times. Each render creates new object references too.Extract shared styles into constants at the top of the file, or better yet, use CSS classes from your existing stylesheet:
const styles = { cell: { padding: '0.75rem 1rem' } as const, codeBlock: { background: 'var(--bg-secondary)', borderRadius: '8px', padding: '1rem', fontFamily: 'var(--font-mono)', fontSize: '0.875rem', overflowX: 'auto' as const, marginBottom: '1.5rem', border: '1px solid var(--border-color)', }, card: { display: 'block' as const, padding: '1rem', background: 'var(--bg-secondary)', borderRadius: '8px', border: '1px solid var(--border-color)', textDecoration: 'none', color: 'var(--text-primary)', }, } as const;
- Increase font sizes in introduction page for better readability - Remove WIP tag from XR-Compatible (Horizon OS, Vision Pro supported) - Update code block font size to 0.9375rem Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- robots.txt: add Allow: / for AI crawlers to access full site - SEO.tsx: make JSON-LD schema conditional (only home/introduction) - home.tsx: update Combined Stars link to /languages page - introduction.tsx: add width/height to ecosystem image for CLS - introduction.tsx: fix nested anchor elements (invalid HTML) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move Quick Stats and Key Benefits styles from home.tsx to home.css - Move all inline styles from introduction.tsx to pages.css - Add new CSS classes: intro-lead, intro-text, intro-list, intro-table, native-module-grid, getting-started-grid, etc. - Reduce inline styles for better maintainability Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/docs/src/styles/pages.css`:
- Around line 513-516: The box-shadow in the .getting-started-card:hover rule
uses legacy rgba() notation; update it to the modern color function form. Locate
the .getting-started-card:hover selector and replace rgba(0, 0, 0, 0.08) with
the modern syntax rgb(0 0 0 / 0.08) (so the box-shadow uses 0 4px 12px rgb(0 0 0
/ 0.08)).
🧹 Nitpick comments (5)
packages/docs/src/styles/code.css (1)
12-13: Hardcoded font-size replaces CSS variable.The previous value
var(--font-size-sm)allowed global theming control;0.9375remis now a magic number. If this was intentional for precise code-block sizing, consider defining a--font-size-codevariable for maintainability.packages/docs/src/components/SEO.tsx (2)
19-27: Missing JSDoc on exportedSEOcomponent.The exported
SEOfunction lacks a JSDoc comment documenting its purpose and props. This is a public API used across multiple pages.Suggested JSDoc
+/** Renders SEO meta tags, Open Graph / Twitter cards, and optional JSON-LD structured data. */ function SEO({ title, description,As per coding guidelines: "Add JSDoc comments for public functions and exported APIs".
33-52:schemaOrgobject is always constructed, even when unused.When
includeAppSchemaisfalse(the default), theschemaOrgobject is still built on every render. Consider moving it inside the conditional or wrapping it with a lazy check to avoid unnecessary object allocation.Suggested approach
- // Schema.org structured data for SoftwareApplication - const schemaOrg = { - '@context': 'https://schema.org', - '@type': 'SoftwareApplication', - ... - }; ... {/* Schema.org JSON-LD */} - {includeAppSchema && ( - <script type="application/ld+json">{JSON.stringify(schemaOrg)}</script> - )} + {includeAppSchema && ( + <script type="application/ld+json"> + {JSON.stringify({ + '@context': 'https://schema.org', + '@type': 'SoftwareApplication', + name: 'OpenIAP', + description: pageDescription, + url: canonicalUrl, + applicationCategory: 'DeveloperApplication', + operatingSystem: 'iOS, Android, visionOS, Horizon OS', + offers: { + '@type': 'Offer', + price: '0', + priceCurrency: 'USD', + }, + author: { + '@type': 'Organization', + name: 'OpenIAP', + url: BASE_URL, + }, + })} + </script> + )}packages/docs/src/pages/home.tsx (2)
427-454: Inline hover handlers should be replaced with a CSS class.The
onMouseEnter/onMouseLeavehandlers replicate a simple:hovereffect that CSS handles natively. This is especially notable given the PR objective of extracting inline styles into CSS classes.Suggested approach
Define a CSS class (e.g.,
.btn-sponsor) inhome.css:.btn-sponsor { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.75rem 2rem; background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); color: white; border-radius: 2rem; text-decoration: none; font-weight: 600; font-size: 0.95rem; box-shadow: 0 4px 14px rgba(164, 116, 101, 0.3); transition: all 0.3s ease; border: 2px solid transparent; } .btn-sponsor:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(164, 116, 101, 0.4); }Then replace the inline styles and handlers:
<Link to="/sponsors" - style={{ - display: 'inline-flex', - ... - }} - onMouseEnter={(e) => { ... }} - onMouseLeave={(e) => { ... }} + className="btn-sponsor" >
28-46: Version badge still uses inline styles.The PR objective includes extracting inline styles to CSS classes, but this
Link(and a few others on this page) still carries a full inline style object. Consider extracting to a.version-badgeclass for consistency with the refactoring effort.
- Update rgba(0, 0, 0, 0.08) to rgb(0 0 0 / 8%) for stylelint compliance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
Changes
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Style