@softspark/ai-toolkit 2.4.1 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/AGENTS.md +33 -20
  2. package/CHANGELOG.md +57 -0
  3. package/README.md +29 -13
  4. package/app/.claude-plugin/plugin.json +3 -2
  5. package/app/ARCHITECTURE.md +11 -0
  6. package/app/agents/code-reviewer.md +6 -7
  7. package/app/agents/frontend-specialist.md +33 -2
  8. package/app/agents/seo-specialist.md +1 -1
  9. package/app/personas/frontend-lead.md +48 -5
  10. package/app/skills/a11y-validate/SKILL.md +377 -0
  11. package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
  12. package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
  13. package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
  14. package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
  15. package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
  16. package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
  17. package/app/skills/clean-code/reference/python.md +3 -3
  18. package/app/skills/design-engineering/SKILL.md +2 -5
  19. package/app/skills/hipaa-validate/SKILL.md +39 -23
  20. package/app/skills/hipaa-validate/scripts/hipaa_scan.py +64 -7
  21. package/app/skills/review/SKILL.md +30 -6
  22. package/app/skills/seo-validate/SKILL.md +460 -0
  23. package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
  24. package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
  25. package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
  26. package/app/skills/seo-validate/reference/schema-types.md +465 -0
  27. package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
  28. package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
  29. package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
  30. package/bin/ai-toolkit.js +24 -9
  31. package/kb/reference/architecture-overview.md +1 -1
  32. package/kb/reference/comparison.md +1 -1
  33. package/kb/reference/opencode-compatibility.md +161 -0
  34. package/kb/reference/skills-catalog.md +3 -1
  35. package/llms-full.txt +177 -6
  36. package/llms.txt +1 -0
  37. package/manifest.json +3 -3
  38. package/package.json +6 -3
  39. package/scripts/config_cli.py +4 -10
  40. package/scripts/doctor.py +3 -3
  41. package/scripts/generate_opencode.py +117 -0
  42. package/scripts/generate_opencode_agents.py +126 -0
  43. package/scripts/generate_opencode_commands.py +158 -0
  44. package/scripts/generate_opencode_json.py +133 -0
  45. package/scripts/generate_opencode_plugin.py +169 -0
  46. package/scripts/install_steps/ai_tools.py +117 -1
  47. package/scripts/install_steps/install_state.py +1 -1
  48. package/scripts/plugin.py +1 -1
@@ -0,0 +1,248 @@
1
+ # GEO — Generative Engine Optimization
2
+
3
+ Reference for `seo-validate` Category 6. Structuring content so AI answer engines (ChatGPT, Perplexity, Google AI Overviews, Bing Copilot, Claude) can extract, cite, and quote it accurately.
4
+
5
+ GEO is emerging practice, not a ranking algorithm with known penalties. **All findings in Category 6 are severity `INFO`** — guidance, not enforcement.
6
+
7
+ ## Core Principles
8
+
9
+ 1. **Structure over prose.** LLMs extract facts from structured blocks (headings, lists, tables, Q&A, schema) much better than from long paragraphs.
10
+ 2. **Be citable.** Provide explicit sources, authors, dates, and facts that can be quoted verbatim.
11
+ 3. **Answer the question.** Lead with the answer, then elaborate. "Inverted pyramid" journalism works for LLMs too.
12
+ 4. **Semantic HTML over div soup.** AI parsers rely on semantic structure just like screen readers.
13
+ 5. **Don't hide content behind JS.** Crawler-level LLM ingestion sees initial DOM; hidden tabs/accordions lose their content in extraction.
14
+
15
+ ---
16
+
17
+ ## Content Structure Patterns
18
+
19
+ ### Lead with the answer
20
+
21
+ LLMs often extract the first 1–2 sentences after a heading as the "answer" to the question that heading implies.
22
+
23
+ ```html
24
+ <!-- Good -->
25
+ <h2>How long does shipping take?</h2>
26
+ <p>Standard shipping takes 3–5 business days in the US and 7–14 days internationally. Express options are 1–2 and 3–7 days respectively.</p>
27
+ <p>Orders placed before 2pm ET ship same-day. Delivery dates are confirmed at checkout based on your address.</p>
28
+
29
+ <!-- Bad (LLM extracts "we care about fast delivery" as the "answer") -->
30
+ <h2>How long does shipping take?</h2>
31
+ <p>At ExampleCo we care deeply about fast delivery, which is why we've invested in our logistics network...</p>
32
+ <p>(Buried eventually: 3–5 business days standard.)</p>
33
+ ```
34
+
35
+ ### Explicit Q&A blocks
36
+
37
+ Q&A structure is the single highest-signal pattern for LLM extraction:
38
+
39
+ ```html
40
+ <section class="faq">
41
+ <h2>Frequently Asked Questions</h2>
42
+ <div>
43
+ <h3>What is X?</h3>
44
+ <p>X is ...</p>
45
+ </div>
46
+ <div>
47
+ <h3>How much does X cost?</h3>
48
+ <p>X costs $... per ...</p>
49
+ </div>
50
+ </section>
51
+ ```
52
+
53
+ Pair with `FAQPage` JSON-LD (see [schema-types.md](schema-types.md)).
54
+
55
+ ### Tables for comparative data
56
+
57
+ Plans, pricing, specs, feature matrices:
58
+
59
+ ```html
60
+ <table>
61
+ <caption>Plan comparison</caption>
62
+ <thead>
63
+ <tr><th>Feature</th><th>Free</th><th>Pro</th><th>Enterprise</th></tr>
64
+ </thead>
65
+ <tbody>
66
+ <tr><th scope="row">Users</th><td>1</td><td>10</td><td>Unlimited</td></tr>
67
+ <tr><th scope="row">Storage</th><td>5GB</td><td>100GB</td><td>Unlimited</td></tr>
68
+ </tbody>
69
+ </table>
70
+ ```
71
+
72
+ LLMs excel at extracting tabular data but struggle when the same info is written as prose.
73
+
74
+ ### Lists for steps
75
+
76
+ Use `<ol>` for ordered steps, `<ul>` for unordered sets:
77
+
78
+ ```html
79
+ <h2>How to reset your password</h2>
80
+ <ol>
81
+ <li>Go to the login page.</li>
82
+ <li>Click "Forgot password".</li>
83
+ <li>Enter your email. A reset link arrives within 2 minutes.</li>
84
+ <li>Click the link and choose a new password.</li>
85
+ </ol>
86
+ ```
87
+
88
+ Pair with `HowTo` schema for critical procedures.
89
+
90
+ ### Definition lists for glossaries
91
+
92
+ ```html
93
+ <dl>
94
+ <dt>LCP</dt>
95
+ <dd>Largest Contentful Paint — time to render the largest visible element. Target: under 2.5 seconds.</dd>
96
+
97
+ <dt>INP</dt>
98
+ <dd>Interaction to Next Paint — longest interaction latency in a session. Target: under 200ms.</dd>
99
+ </dl>
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Attribution & Citations
105
+
106
+ LLM answer engines (especially Perplexity, Google AI Overviews, Bing Copilot) prefer citable sources.
107
+
108
+ ### Author bylines
109
+
110
+ ```html
111
+ <article>
112
+ <h1>Article title</h1>
113
+ <p class="byline">
114
+ By <a rel="author" href="/authors/jane-doe">Jane Doe</a>,
115
+ <time datetime="2026-04-09">April 9, 2026</time>
116
+ </p>
117
+ <p>...</p>
118
+ </article>
119
+ ```
120
+
121
+ Pair with `Article` schema including `author` and `datePublished`.
122
+
123
+ ### Inline citations
124
+
125
+ ```html
126
+ <p>
127
+ Google's Core Web Vitals include LCP, INP, and CLS
128
+ <cite><a href="https://web.dev/vitals/">web.dev/vitals</a></cite>.
129
+ </p>
130
+
131
+ <p>
132
+ According to the 2024 State of JS report, React adoption
133
+ is at 76%<sup><a href="#ref-1">[1]</a></sup>.
134
+ </p>
135
+ ```
136
+
137
+ ### Quotes
138
+
139
+ Use `<blockquote>` with `cite` attribute, or `<q>` for inline:
140
+
141
+ ```html
142
+ <blockquote cite="https://example.com/source">
143
+ <p>Direct quote here.</p>
144
+ <footer>— <cite>Source Author, Source Publication</cite></footer>
145
+ </blockquote>
146
+
147
+ <p>As Crockford put it, <q cite="https://example.com">JavaScript is the world's most misunderstood language</q>.</p>
148
+ ```
149
+
150
+ ---
151
+
152
+ ## speakable Schema
153
+
154
+ Marks portions of a page suited for audio/voice answer engines (Google Assistant, Alexa, Siri).
155
+
156
+ ```html
157
+ <script type="application/ld+json">
158
+ {
159
+ "@context": "https://schema.org",
160
+ "@type": "WebPage",
161
+ "speakable": {
162
+ "@type": "SpeakableSpecification",
163
+ "cssSelector": [".article-summary", "h1"]
164
+ }
165
+ }
166
+ </script>
167
+ ```
168
+
169
+ - `cssSelector`: CSS selectors for speakable content.
170
+ - `xpath`: XPath selectors (alternative).
171
+
172
+ Best for: news article summaries, FAQ answers, definitions.
173
+
174
+ ---
175
+
176
+ ## JS-Gated Content Anti-Patterns
177
+
178
+ LLM crawlers typically ingest the initial DOM. Content revealed by clicks/hovers/scrolls may not be captured.
179
+
180
+ ### Avoid for critical content
181
+
182
+ ```jsx
183
+ // Bad — key FAQ answers hidden
184
+ <Accordion>
185
+ <AccordionItem title="What does X cost?">
186
+ <p>X costs $...</p> {/* Hidden until clicked */}
187
+ </AccordionItem>
188
+ </Accordion>
189
+
190
+ // Good — content visible by default, collapsed via CSS (still in DOM)
191
+ <details>
192
+ <summary>What does X cost?</summary>
193
+ <p>X costs $...</p> {/* In DOM always — <details> is semantic */}
194
+ </details>
195
+ ```
196
+
197
+ The native `<details>`/`<summary>` element is a rare exception — content is in the DOM, just visually collapsed.
198
+
199
+ ### Tabs
200
+
201
+ Similar issue. If tab content is only rendered when active, hidden tabs are lost:
202
+
203
+ ```jsx
204
+ // Bad — only active tab rendered
205
+ {activeTab === 'pricing' && <PricingContent />}
206
+
207
+ // Better — all rendered, hidden via CSS
208
+ <div hidden={activeTab !== 'pricing'}>
209
+ <PricingContent />
210
+ </div>
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Per-Framework Notes
216
+
217
+ ### Next.js / Remix / SvelteKit / Astro / Gatsby (SSR/SSG)
218
+
219
+ These render content server-side. GEO concerns are mostly about content structure, not rendering mode.
220
+
221
+ ### SPAs without SSR (Vue, React CRA, Vite-SPA, Angular without Universal)
222
+
223
+ These don't render content for crawlers at all. **Category 7 (rendering) supersedes GEO** — fix the SPA crawlability first; GEO is moot if nothing is visible.
224
+
225
+ ---
226
+
227
+ ## Checklist (Category 6 findings)
228
+
229
+ - [ ] FAQ-style content uses `FAQPage` schema.
230
+ - [ ] Summary content uses `speakable` schema.
231
+ - [ ] Paragraphs >400 words are broken up with sub-headings.
232
+ - [ ] Citations use `<cite>` and author bylines.
233
+ - [ ] Quoted content uses `<blockquote>`/`<q>` with `cite` attr.
234
+ - [ ] How-to content uses `<ol>` + `HowTo` schema.
235
+ - [ ] Comparative data uses `<table>` with proper headers.
236
+ - [ ] Glossary content uses `<dl>`/`<dt>`/`<dd>`.
237
+ - [ ] Semantic HTML (`<article>`, `<section>`, `<main>`, `<nav>`, `<aside>`) is used over `<div>`.
238
+ - [ ] Critical content is NOT hidden behind tabs/accordions (except native `<details>`).
239
+
240
+ ---
241
+
242
+ ## References
243
+
244
+ - Princeton/Georgia Tech GEO paper (Aggarwal et al., 2023): https://arxiv.org/abs/2311.09735
245
+ - Schema.org speakable: https://schema.org/speakable
246
+ - Schema.org FAQPage: https://schema.org/FAQPage
247
+ - Schema.org HowTo: https://schema.org/HowTo
248
+ - Google Search Central (structured data guidelines): https://developers.google.com/search/docs/appearance/structured-data
@@ -0,0 +1,465 @@
1
+ # Schema.org JSON-LD Templates
2
+
3
+ Reference for `seo-validate` Category 3 (Structured Data). Required properties for the rich-result-eligible types, with JSON-LD templates.
4
+
5
+ Source: Schema.org vocabulary + Google Search Central rich-results requirements.
6
+
7
+ ## Base Format
8
+
9
+ All JSON-LD is embedded in `<script type="application/ld+json">` blocks, usually in `<head>` or the top of `<body>`:
10
+
11
+ ```html
12
+ <script type="application/ld+json">
13
+ {
14
+ "@context": "https://schema.org",
15
+ "@type": "...",
16
+ ...
17
+ }
18
+ </script>
19
+ ```
20
+
21
+ **Universal required properties** (checked by Category 3):
22
+ - `@context` — always `https://schema.org`.
23
+ - `@type` — valid Schema.org type name.
24
+
25
+ Multiple types on one page: use an array:
26
+
27
+ ```html
28
+ <script type="application/ld+json">
29
+ [
30
+ { "@context": "https://schema.org", "@type": "Organization", ... },
31
+ { "@context": "https://schema.org", "@type": "WebSite", ... }
32
+ ]
33
+ </script>
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Article
39
+
40
+ For blog posts, news, editorial content.
41
+
42
+ ```json
43
+ {
44
+ "@context": "https://schema.org",
45
+ "@type": "Article",
46
+ "headline": "Article headline (max 110 chars for Google rich results)",
47
+ "image": [
48
+ "https://example.com/16x9.jpg",
49
+ "https://example.com/4x3.jpg",
50
+ "https://example.com/1x1.jpg"
51
+ ],
52
+ "datePublished": "2026-04-09T08:00:00+00:00",
53
+ "dateModified": "2026-04-10T12:00:00+00:00",
54
+ "author": [{
55
+ "@type": "Person",
56
+ "name": "Jane Doe",
57
+ "url": "https://example.com/authors/jane-doe"
58
+ }],
59
+ "publisher": {
60
+ "@type": "Organization",
61
+ "name": "ExampleCo",
62
+ "logo": {
63
+ "@type": "ImageObject",
64
+ "url": "https://example.com/logo.png"
65
+ }
66
+ },
67
+ "mainEntityOfPage": {
68
+ "@type": "WebPage",
69
+ "@id": "https://example.com/blog/article-slug"
70
+ },
71
+ "description": "Short description of the article"
72
+ }
73
+ ```
74
+
75
+ **Required** (Category 3 flags absence): `headline`, `author`, `datePublished`, `image`.
76
+ **Strongly recommended**: `dateModified`, `publisher`, `mainEntityOfPage`.
77
+
78
+ Subtypes: `NewsArticle`, `BlogPosting`, `TechArticle`, `OpinionNewsArticle`.
79
+
80
+ ---
81
+
82
+ ## FAQPage
83
+
84
+ Triggers rich FAQ results in SERP.
85
+
86
+ ```json
87
+ {
88
+ "@context": "https://schema.org",
89
+ "@type": "FAQPage",
90
+ "mainEntity": [
91
+ {
92
+ "@type": "Question",
93
+ "name": "What is X?",
94
+ "acceptedAnswer": {
95
+ "@type": "Answer",
96
+ "text": "X is ..."
97
+ }
98
+ },
99
+ {
100
+ "@type": "Question",
101
+ "name": "How much does X cost?",
102
+ "acceptedAnswer": {
103
+ "@type": "Answer",
104
+ "text": "X costs $..."
105
+ }
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ **Required**: `mainEntity` array with ≥2 `Question` items, each with `name` + `acceptedAnswer.text`.
112
+
113
+ Google restriction: FAQ rich results only shown for authoritative government/health sites as of August 2023 — but the markup still helps LLM extraction (GEO).
114
+
115
+ ---
116
+
117
+ ## HowTo
118
+
119
+ For step-by-step instructional content.
120
+
121
+ ```json
122
+ {
123
+ "@context": "https://schema.org",
124
+ "@type": "HowTo",
125
+ "name": "How to reset your password",
126
+ "description": "Steps to reset your account password",
127
+ "totalTime": "PT5M",
128
+ "supply": [
129
+ { "@type": "HowToSupply", "name": "Email access" }
130
+ ],
131
+ "tool": [
132
+ { "@type": "HowToTool", "name": "Web browser" }
133
+ ],
134
+ "step": [
135
+ {
136
+ "@type": "HowToStep",
137
+ "name": "Go to the login page",
138
+ "text": "Navigate to https://example.com/login",
139
+ "url": "https://example.com/login"
140
+ },
141
+ {
142
+ "@type": "HowToStep",
143
+ "name": "Click 'Forgot password'",
144
+ "text": "On the login page, click the 'Forgot password' link."
145
+ }
146
+ ]
147
+ }
148
+ ```
149
+
150
+ **Required**: `name`, `step` array with ≥2 `HowToStep` items.
151
+
152
+ ---
153
+
154
+ ## BreadcrumbList
155
+
156
+ Breadcrumb trails in SERP.
157
+
158
+ ```json
159
+ {
160
+ "@context": "https://schema.org",
161
+ "@type": "BreadcrumbList",
162
+ "itemListElement": [
163
+ {
164
+ "@type": "ListItem",
165
+ "position": 1,
166
+ "name": "Home",
167
+ "item": "https://example.com/"
168
+ },
169
+ {
170
+ "@type": "ListItem",
171
+ "position": 2,
172
+ "name": "Blog",
173
+ "item": "https://example.com/blog"
174
+ },
175
+ {
176
+ "@type": "ListItem",
177
+ "position": 3,
178
+ "name": "Article Title"
179
+ }
180
+ ]
181
+ }
182
+ ```
183
+
184
+ **Required**: `itemListElement` array with `position`, `name`, and `item` (except last). Last item omits `item` since it's the current page.
185
+
186
+ ---
187
+
188
+ ## Organization
189
+
190
+ Defines your company/brand for Knowledge Graph.
191
+
192
+ ```json
193
+ {
194
+ "@context": "https://schema.org",
195
+ "@type": "Organization",
196
+ "name": "ExampleCo",
197
+ "url": "https://example.com",
198
+ "logo": "https://example.com/logo.png",
199
+ "sameAs": [
200
+ "https://twitter.com/exampleco",
201
+ "https://www.linkedin.com/company/exampleco",
202
+ "https://github.com/exampleco"
203
+ ],
204
+ "contactPoint": [{
205
+ "@type": "ContactPoint",
206
+ "telephone": "+1-555-123-4567",
207
+ "contactType": "customer service",
208
+ "areaServed": "US",
209
+ "availableLanguage": ["en", "es"]
210
+ }]
211
+ }
212
+ ```
213
+
214
+ **Required**: `name`, `url`, `logo`.
215
+
216
+ Subtypes for specific kinds: `Corporation`, `NGO`, `EducationalOrganization`, `LocalBusiness` (see below).
217
+
218
+ Typically placed once on the site, in a shared layout. `WebSite` schema can reference it via `publisher`.
219
+
220
+ ---
221
+
222
+ ## Product
223
+
224
+ For e-commerce product pages.
225
+
226
+ ```json
227
+ {
228
+ "@context": "https://schema.org",
229
+ "@type": "Product",
230
+ "name": "Product name",
231
+ "image": [
232
+ "https://example.com/product-1x1.jpg",
233
+ "https://example.com/product-4x3.jpg",
234
+ "https://example.com/product-16x9.jpg"
235
+ ],
236
+ "description": "Product description",
237
+ "brand": {
238
+ "@type": "Brand",
239
+ "name": "Brand Name"
240
+ },
241
+ "sku": "SKU-12345",
242
+ "mpn": "MPN-12345",
243
+ "gtin13": "0123456789012",
244
+ "offers": {
245
+ "@type": "Offer",
246
+ "url": "https://example.com/products/sku-12345",
247
+ "priceCurrency": "USD",
248
+ "price": "49.99",
249
+ "priceValidUntil": "2027-01-01",
250
+ "availability": "https://schema.org/InStock",
251
+ "itemCondition": "https://schema.org/NewCondition"
252
+ },
253
+ "aggregateRating": {
254
+ "@type": "AggregateRating",
255
+ "ratingValue": "4.6",
256
+ "reviewCount": "142"
257
+ },
258
+ "review": [{
259
+ "@type": "Review",
260
+ "author": { "@type": "Person", "name": "Jane" },
261
+ "reviewRating": { "@type": "Rating", "ratingValue": "5" },
262
+ "reviewBody": "Great product."
263
+ }]
264
+ }
265
+ ```
266
+
267
+ **Required**: `name`, `image`, `offers` (with `price` + `priceCurrency` + `availability`).
268
+ **Strongly recommended for rich results**: `aggregateRating`, `review`, `brand`.
269
+
270
+ ---
271
+
272
+ ## LocalBusiness
273
+
274
+ For brick-and-mortar businesses (local SEO).
275
+
276
+ ```json
277
+ {
278
+ "@context": "https://schema.org",
279
+ "@type": "LocalBusiness",
280
+ "@id": "https://example.com/#local-business",
281
+ "name": "ExampleCo Store",
282
+ "image": "https://example.com/store.jpg",
283
+ "url": "https://example.com",
284
+ "telephone": "+1-555-123-4567",
285
+ "address": {
286
+ "@type": "PostalAddress",
287
+ "streetAddress": "123 Main St",
288
+ "addressLocality": "San Francisco",
289
+ "addressRegion": "CA",
290
+ "postalCode": "94110",
291
+ "addressCountry": "US"
292
+ },
293
+ "geo": {
294
+ "@type": "GeoCoordinates",
295
+ "latitude": 37.7749,
296
+ "longitude": -122.4194
297
+ },
298
+ "openingHoursSpecification": [
299
+ {
300
+ "@type": "OpeningHoursSpecification",
301
+ "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
302
+ "opens": "09:00",
303
+ "closes": "18:00"
304
+ },
305
+ {
306
+ "@type": "OpeningHoursSpecification",
307
+ "dayOfWeek": "Saturday",
308
+ "opens": "10:00",
309
+ "closes": "16:00"
310
+ }
311
+ ],
312
+ "priceRange": "$$"
313
+ }
314
+ ```
315
+
316
+ **Required**: `name`, `address`, `telephone`.
317
+ **Strongly recommended**: `openingHoursSpecification`, `geo`, `url`, `image`.
318
+
319
+ Use more specific subtypes when applicable: `Restaurant`, `Hotel`, `MedicalClinic`, `Store`, `AutoDealer`, etc.
320
+
321
+ ---
322
+
323
+ ## WebSite (with SearchAction)
324
+
325
+ Enables sitelinks search box in SERP.
326
+
327
+ ```json
328
+ {
329
+ "@context": "https://schema.org",
330
+ "@type": "WebSite",
331
+ "name": "ExampleCo",
332
+ "url": "https://example.com",
333
+ "potentialAction": {
334
+ "@type": "SearchAction",
335
+ "target": {
336
+ "@type": "EntryPoint",
337
+ "urlTemplate": "https://example.com/search?q={search_term_string}"
338
+ },
339
+ "query-input": "required name=search_term_string"
340
+ }
341
+ }
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Event
347
+
348
+ For conferences, concerts, webinars.
349
+
350
+ ```json
351
+ {
352
+ "@context": "https://schema.org",
353
+ "@type": "Event",
354
+ "name": "Event name",
355
+ "startDate": "2026-06-15T09:00:00-07:00",
356
+ "endDate": "2026-06-15T17:00:00-07:00",
357
+ "eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
358
+ "eventStatus": "https://schema.org/EventScheduled",
359
+ "location": {
360
+ "@type": "Place",
361
+ "name": "Venue Name",
362
+ "address": { "@type": "PostalAddress", ... }
363
+ },
364
+ "image": ["https://example.com/event.jpg"],
365
+ "description": "...",
366
+ "offers": {
367
+ "@type": "Offer",
368
+ "url": "https://example.com/event",
369
+ "price": "99.00",
370
+ "priceCurrency": "USD",
371
+ "availability": "https://schema.org/InStock",
372
+ "validFrom": "2026-01-01T00:00:00-08:00"
373
+ },
374
+ "organizer": {
375
+ "@type": "Organization",
376
+ "name": "ExampleCo",
377
+ "url": "https://example.com"
378
+ }
379
+ }
380
+ ```
381
+
382
+ **Required**: `name`, `startDate`, `location` (or `eventAttendanceMode: OnlineEventAttendanceMode`).
383
+
384
+ ---
385
+
386
+ ## Recipe
387
+
388
+ For food recipes.
389
+
390
+ ```json
391
+ {
392
+ "@context": "https://schema.org",
393
+ "@type": "Recipe",
394
+ "name": "Recipe name",
395
+ "image": ["..."],
396
+ "author": { "@type": "Person", "name": "Jane Doe" },
397
+ "datePublished": "2026-04-09",
398
+ "description": "...",
399
+ "prepTime": "PT15M",
400
+ "cookTime": "PT1H",
401
+ "totalTime": "PT1H15M",
402
+ "recipeYield": "8 servings",
403
+ "recipeIngredient": ["2 cups flour", "1 tsp salt"],
404
+ "recipeInstructions": [
405
+ { "@type": "HowToStep", "text": "..." }
406
+ ],
407
+ "nutrition": {
408
+ "@type": "NutritionInformation",
409
+ "calories": "250 calories"
410
+ },
411
+ "aggregateRating": {
412
+ "@type": "AggregateRating",
413
+ "ratingValue": "4.8",
414
+ "reviewCount": "42"
415
+ }
416
+ }
417
+ ```
418
+
419
+ ---
420
+
421
+ ## VideoObject
422
+
423
+ For video content.
424
+
425
+ ```json
426
+ {
427
+ "@context": "https://schema.org",
428
+ "@type": "VideoObject",
429
+ "name": "Video title",
430
+ "description": "...",
431
+ "thumbnailUrl": ["https://example.com/thumb-16x9.jpg"],
432
+ "uploadDate": "2026-04-09T08:00:00+00:00",
433
+ "duration": "PT5M30S",
434
+ "contentUrl": "https://example.com/video.mp4",
435
+ "embedUrl": "https://example.com/embed/video"
436
+ }
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Common Mistakes (flagged by Category 3)
442
+
443
+ 1. **Missing `@context`** or wrong value (e.g., `http://` instead of `https://`).
444
+ 2. **Wrong `@type`** (e.g., `article` lowercase — must be `Article`).
445
+ 3. **Dates not in ISO 8601** (`2026-04-09` good; `April 9, 2026` bad).
446
+ 4. **Relative URLs** for `image`, `logo`, `url` — must be absolute.
447
+ 5. **Missing required properties** per type (see each section above).
448
+ 6. **Markup doesn't match visible content** — violates Google guidelines.
449
+ 7. **Duplicate JSON-LD blocks** with conflicting data — consolidate into one.
450
+ 8. **Fake reviews / made-up aggregate ratings** — violates Google guidelines.
451
+
452
+ ---
453
+
454
+ ## Testing
455
+
456
+ - Google Rich Results Test: https://search.google.com/test/rich-results
457
+ - Schema.org validator: https://validator.schema.org/
458
+
459
+ ---
460
+
461
+ ## References
462
+
463
+ - Schema.org: https://schema.org/
464
+ - Google rich results: https://developers.google.com/search/docs/appearance/structured-data
465
+ - JSON-LD 1.1: https://www.w3.org/TR/json-ld11/