@zoyth/simple-site-framework 1.0.0

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 (166) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +572 -0
  3. package/bin/create-simple-site.js +390 -0
  4. package/bin/simple-site.js +664 -0
  5. package/dist/client.js +135 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/client.mjs +107 -0
  8. package/dist/client.mjs.map +1 -0
  9. package/dist/components/index.d.mts +3936 -0
  10. package/dist/components/index.d.ts +3936 -0
  11. package/dist/components/index.js +38265 -0
  12. package/dist/components/index.js.map +1 -0
  13. package/dist/components/index.mjs +38173 -0
  14. package/dist/components/index.mjs.map +1 -0
  15. package/dist/config/index.d.mts +298 -0
  16. package/dist/config/index.d.ts +298 -0
  17. package/dist/config/index.js +19 -0
  18. package/dist/config/index.js.map +1 -0
  19. package/dist/config/index.mjs +1 -0
  20. package/dist/config/index.mjs.map +1 -0
  21. package/dist/index.d.mts +2184 -0
  22. package/dist/index.d.ts +2184 -0
  23. package/dist/index.js +1713 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/index.mjs +1605 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/lib/i18n/index.js +665 -0
  28. package/dist/lib/i18n/index.js.map +1 -0
  29. package/dist/lib/i18n/index.mjs +621 -0
  30. package/dist/lib/i18n/index.mjs.map +1 -0
  31. package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
  32. package/docs/EXPORTS.md +125 -0
  33. package/docs/PERFORMANCE.md +757 -0
  34. package/docs/POLICY-PAGES.md +867 -0
  35. package/docs/ROADMAP.md +334 -0
  36. package/docs/SEO.md +455 -0
  37. package/docs/SITEMAP.md +708 -0
  38. package/docs/STRUCTURED-DATA.md +671 -0
  39. package/docs/accessibility/common-patterns.md +529 -0
  40. package/docs/accessibility/keyboard-navigation.md +263 -0
  41. package/docs/accessibility/overview.md +122 -0
  42. package/docs/accessibility/screen-readers.md +311 -0
  43. package/docs/accessibility/wcag-compliance.md +159 -0
  44. package/docs/api/README.md +164 -0
  45. package/docs/api/components/Accessibility.md +356 -0
  46. package/docs/api/components/Button.md +240 -0
  47. package/docs/api/components/HeroSection.md +306 -0
  48. package/docs/architecture/decisions.md +449 -0
  49. package/docs/components/AnalyticsTracker.md +58 -0
  50. package/docs/components/AnimatedCounter.md +48 -0
  51. package/docs/components/AnimatedSection.md +56 -0
  52. package/docs/components/BlogCard.md +42 -0
  53. package/docs/components/Checkbox.md +56 -0
  54. package/docs/components/CodeBlock.md +52 -0
  55. package/docs/components/ComparisonTable.md +40 -0
  56. package/docs/components/ComponentDemo.md +38 -0
  57. package/docs/components/CountdownTimer.md +51 -0
  58. package/docs/components/ExitIntentModal.md +56 -0
  59. package/docs/components/FAQAccordion.md +66 -0
  60. package/docs/components/FeaturesGrid.md +55 -0
  61. package/docs/components/FileUpload.md +54 -0
  62. package/docs/components/I18nMetaTags.md +55 -0
  63. package/docs/components/Icon.md +53 -0
  64. package/docs/components/LazySection.md +46 -0
  65. package/docs/components/LiveProof.md +53 -0
  66. package/docs/components/LoadingSpinner.md +46 -0
  67. package/docs/components/MultiStepForm.md +48 -0
  68. package/docs/components/PolicyLayout.md +55 -0
  69. package/docs/components/PricingTable.md +49 -0
  70. package/docs/components/Radio.md +59 -0
  71. package/docs/components/SEOMetaTags.md +58 -0
  72. package/docs/components/ScriptInjector.md +50 -0
  73. package/docs/components/Select.md +72 -0
  74. package/docs/components/Skeleton.md +47 -0
  75. package/docs/components/StatsSection.md +48 -0
  76. package/docs/components/StickyBar.md +62 -0
  77. package/docs/components/StructuredData.md +99 -0
  78. package/docs/components/StyleGuide.md +46 -0
  79. package/docs/components/TableOfContents.md +47 -0
  80. package/docs/components/TestimonialCarousel.md +42 -0
  81. package/docs/components/Timeline.md +51 -0
  82. package/docs/components/Toast.md +59 -0
  83. package/docs/components/TrackedLink.md +62 -0
  84. package/docs/components/TrustBadges.md +44 -0
  85. package/docs/components/conversion/MobileCTA.md +363 -0
  86. package/docs/components/forms/ContactForm.md +75 -0
  87. package/docs/components/forms/FormField.md +74 -0
  88. package/docs/components/layout/Footer.md +601 -0
  89. package/docs/components/layout/Header.md +549 -0
  90. package/docs/components/layout/LanguageSelector.md +54 -0
  91. package/docs/components/layout/LanguageSwitcher.md +24 -0
  92. package/docs/components/overview.md +447 -0
  93. package/docs/components/sections/AboutSection.md +48 -0
  94. package/docs/components/sections/CTASection.md +596 -0
  95. package/docs/components/sections/CaseStudySection.md +47 -0
  96. package/docs/components/sections/ContactSection.md +599 -0
  97. package/docs/components/sections/FeatureSection.md +44 -0
  98. package/docs/components/sections/HeroSection.md +404 -0
  99. package/docs/components/sections/LogosSection.md +47 -0
  100. package/docs/components/sections/PersonalTaxesSection.md +23 -0
  101. package/docs/components/sections/RecruitingSection.md +23 -0
  102. package/docs/components/sections/SecurePortalSection.md +23 -0
  103. package/docs/components/sections/ServicePageLayout.md +52 -0
  104. package/docs/components/sections/ServicesSection.md +49 -0
  105. package/docs/components/sections/TestimonialSection.md +44 -0
  106. package/docs/components/sections/WhyChooseUsSection.md +54 -0
  107. package/docs/components/ui/Breadcrumb.md +70 -0
  108. package/docs/components/ui/Button.md +514 -0
  109. package/docs/components/ui/Card.md +501 -0
  110. package/docs/components/ui/Input.md +54 -0
  111. package/docs/components/ui/MobileLinks.md +43 -0
  112. package/docs/components/ui/Modal.md +60 -0
  113. package/docs/components/ui/Tabs.md +62 -0
  114. package/docs/components/ui/Textarea.md +52 -0
  115. package/docs/core-concepts/configuration-driven.md +552 -0
  116. package/docs/core-concepts/overview.md +351 -0
  117. package/docs/features/accessibility/README.md +73 -0
  118. package/docs/features/accessibility/aria-support.md +177 -0
  119. package/docs/features/accessibility/color-contrast.md +155 -0
  120. package/docs/features/accessibility/focus-management.md +187 -0
  121. package/docs/features/accessibility/testing.md +196 -0
  122. package/docs/features/analytics/README.md +51 -0
  123. package/docs/features/analytics/ab-testing.md +171 -0
  124. package/docs/features/analytics/conversion-tracking.md +207 -0
  125. package/docs/features/analytics/custom-events.md +219 -0
  126. package/docs/features/analytics/privacy.md +198 -0
  127. package/docs/features/analytics/setup.md +114 -0
  128. package/docs/features/analytics/tracking-events.md +224 -0
  129. package/docs/features/i18n/README.md +51 -0
  130. package/docs/features/i18n/best-practices.md +273 -0
  131. package/docs/features/i18n/configuration.md +84 -0
  132. package/docs/features/i18n/formatting.md +133 -0
  133. package/docs/features/i18n/locale-detection.md +122 -0
  134. package/docs/features/i18n/routing.md +99 -0
  135. package/docs/features/i18n/rtl-support.md +191 -0
  136. package/docs/features/i18n/translations.md +129 -0
  137. package/docs/features/internationalization.md +595 -0
  138. package/docs/features/performance/README.md +77 -0
  139. package/docs/features/performance/bundle-size.md +134 -0
  140. package/docs/features/performance/caching.md +131 -0
  141. package/docs/features/performance/code-splitting.md +121 -0
  142. package/docs/features/performance/image-optimization.md +110 -0
  143. package/docs/features/performance/lazy-loading.md +92 -0
  144. package/docs/features/performance/monitoring.md +148 -0
  145. package/docs/features/seo/README.md +51 -0
  146. package/docs/features/seo/best-practices.md +184 -0
  147. package/docs/features/seo/canonical-urls.md +182 -0
  148. package/docs/features/seo/meta-tags.md +126 -0
  149. package/docs/features/seo/open-graph.md +166 -0
  150. package/docs/features/seo/robots-txt.md +146 -0
  151. package/docs/features/seo/sitemaps.md +162 -0
  152. package/docs/features/seo/structured-data.md +166 -0
  153. package/docs/getting-started/installation.md +292 -0
  154. package/docs/getting-started/introduction.md +195 -0
  155. package/docs/getting-started/quick-start.md +460 -0
  156. package/docs/guides/analytics-setup.md +616 -0
  157. package/docs/i18n/CONFIGURATION.md +353 -0
  158. package/docs/i18n/EXAMPLES.md +402 -0
  159. package/docs/i18n/MIGRATION.md +260 -0
  160. package/docs/i18n/SEO.md +392 -0
  161. package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
  162. package/docs/migration/changelog.md +136 -0
  163. package/docs/migration/overview.md +233 -0
  164. package/docs/recipes/adding-animations.md +475 -0
  165. package/docs/recipes/forms-with-validation.md +393 -0
  166. package/package.json +152 -0
@@ -0,0 +1,867 @@
1
+
2
+
3
+ # Policy Pages Guide
4
+
5
+ Comprehensive guide for creating and managing policy and legal document pages using markdown files.
6
+
7
+ ## Overview
8
+
9
+ Policy pages (privacy policies, terms of service, legal documents) are perfect candidates for markdown because they:
10
+
11
+ - ✅ Are primarily text-based content
12
+ - ✅ Change occasionally but not frequently
13
+ - ✅ Need to be readable and editable by non-technical people
14
+ - ✅ Benefit from clean version control
15
+ - ✅ Require proper SEO optimization
16
+ - ✅ Often exist in multiple languages
17
+
18
+ The framework provides components and utilities to transform markdown files into beautifully formatted, SEO-optimized policy pages with automatic table of contents and proper styling.
19
+
20
+ ---
21
+
22
+ ## Quick Start
23
+
24
+ ### 1. Install Dependencies
25
+
26
+ ```bash
27
+ npm install next-mdx-remote
28
+ npm install -D @tailwindcss/typography
29
+ ```
30
+
31
+ ### 2. Configure Tailwind Typography
32
+
33
+ ```javascript
34
+ // tailwind.config.js
35
+ module.exports = {
36
+ plugins: [
37
+ require('@tailwindcss/typography'),
38
+ ],
39
+ };
40
+ ```
41
+
42
+ ### 3. Create Policy Directory
43
+
44
+ ```bash
45
+ mkdir -p src/content/policies
46
+ ```
47
+
48
+ ### 4. Create Markdown File
49
+
50
+ ```markdown
51
+ <!-- src/content/policies/privacy-policy.en.md -->
52
+ ---
53
+ title: "Privacy Policy"
54
+ lastUpdated: "2026-02-03"
55
+ description: "How we protect your personal data"
56
+ ---
57
+
58
+ # Privacy Policy
59
+
60
+ Last updated: February 3, 2026
61
+
62
+ ## 1. Information We Collect
63
+
64
+ We collect the following types of information...
65
+
66
+ ## 2. How We Use Your Information
67
+
68
+ We use your information to...
69
+ ```
70
+
71
+ ### 5. Create Page Component
72
+
73
+ ```typescript
74
+ // app/[locale]/privacy/page.tsx
75
+ import { loadPolicy } from 'simple-site-framework/lib/content';
76
+ import { PolicyLayout } from 'simple-site-framework';
77
+
78
+ export default async function PrivacyPage({
79
+ params
80
+ }: {
81
+ params: { locale: string }
82
+ }) {
83
+ const { content, metadata } = await loadPolicy('privacy-policy', params.locale);
84
+
85
+ return (
86
+ <PolicyLayout
87
+ title={metadata.title}
88
+ lastUpdated={metadata.lastUpdated}
89
+ locale={params.locale}
90
+ >
91
+ {content}
92
+ </PolicyLayout>
93
+ );
94
+ }
95
+
96
+ // Generate static pages for all locales
97
+ export async function generateStaticParams() {
98
+ return [
99
+ { locale: 'en' },
100
+ { locale: 'fr' },
101
+ ];
102
+ }
103
+
104
+ // SEO metadata
105
+ export async function generateMetadata({ params }: { params: { locale: string } }) {
106
+ const { metadata } = await loadPolicy('privacy-policy', params.locale);
107
+
108
+ return {
109
+ title: metadata.title,
110
+ description: metadata.description,
111
+ };
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## File Structure
118
+
119
+ ### Recommended Directory Structure
120
+
121
+ ```
122
+ project/
123
+ ├── src/
124
+ │ ├── content/
125
+ │ │ └── policies/
126
+ │ │ ├── privacy-policy.en.md
127
+ │ │ ├── privacy-policy.fr.md
128
+ │ │ ├── terms-of-service.en.md
129
+ │ │ ├── terms-of-service.fr.md
130
+ │ │ ├── cookie-policy.en.md
131
+ │ │ ├── cookie-policy.fr.md
132
+ │ │ ├── dpa.en.md # Data Processing Agreement
133
+ │ │ └── dpa.fr.md
134
+ │ └── app/
135
+ │ └── [locale]/
136
+ │ ├── privacy/
137
+ │ │ └── page.tsx
138
+ │ ├── terms/
139
+ │ │ └── page.tsx
140
+ │ └── cookies/
141
+ │ └── page.tsx
142
+ ```
143
+
144
+ ### File Naming Convention
145
+
146
+ **Format:** `{slug}.{locale}.md`
147
+
148
+ - **slug**: Kebab-case identifier (e.g., `privacy-policy`, `terms-of-service`)
149
+ - **locale**: Language code (e.g., `en`, `fr`, `es`, `en-US`)
150
+ - **extension**: `.md` for markdown, `.mdx` for MDX (markdown + components)
151
+
152
+ **Examples:**
153
+ - ✅ `privacy-policy.en.md`
154
+ - ✅ `terms-of-service.fr.md`
155
+ - ✅ `cookie-policy.en-US.mdx`
156
+ - ❌ `privacy_policy.md` (missing locale)
157
+ - ❌ `Privacy Policy.en.md` (spaces not allowed)
158
+
159
+ ---
160
+
161
+ ## Markdown Format
162
+
163
+ ### Frontmatter (Required)
164
+
165
+ Every policy markdown file must start with YAML frontmatter:
166
+
167
+ ```markdown
168
+ ---
169
+ title: "Privacy Policy"
170
+ lastUpdated: "2026-02-03"
171
+ description: "How we protect your personal data"
172
+ ---
173
+ ```
174
+
175
+ **Required Fields:**
176
+ - `title` (string): Policy title displayed in header
177
+ - `lastUpdated` (string): ISO date or formatted date string
178
+
179
+ **Optional Fields:**
180
+ - `description` (string): SEO meta description
181
+ - Custom fields: Add any additional metadata
182
+
183
+ ### Markdown Content
184
+
185
+ Use standard markdown syntax:
186
+
187
+ ```markdown
188
+ # Main Heading (H1)
189
+
190
+ This is a paragraph with **bold text** and *italic text*.
191
+
192
+ ## Section Heading (H2)
193
+
194
+ ### Subsection (H3)
195
+
196
+ - Bullet point 1
197
+ - Bullet point 2
198
+ - Nested bullet
199
+
200
+ 1. Numbered item 1
201
+ 2. Numbered item 2
202
+
203
+ [Link text](https://example.com)
204
+
205
+ > Blockquote for important notices
206
+
207
+ `Inline code` for technical terms
208
+
209
+ \```
210
+ Code block
211
+ \```
212
+
213
+ | Column 1 | Column 2 |
214
+ |----------|----------|
215
+ | Data 1 | Data 2 |
216
+ ```
217
+
218
+ ### Heading Hierarchy Best Practices
219
+
220
+ - Use only ONE H1 (`#`) for the main title
221
+ - Use H2 (`##`) for main sections
222
+ - Use H3 (`###`) for subsections
223
+ - Use H4 (`####`) sparingly for sub-subsections
224
+ - Don't skip levels (H2 → H4 is bad)
225
+
226
+ **Good:**
227
+ ```markdown
228
+ # Privacy Policy
229
+
230
+ ## 1. Information We Collect
231
+
232
+ ### 1.1 Account Information
233
+
234
+ ### 1.2 Usage Data
235
+
236
+ ## 2. How We Use Your Information
237
+ ```
238
+
239
+ **Bad:**
240
+ ```markdown
241
+ # Privacy Policy
242
+
243
+ #### 1. Information ← Don't skip to H4
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Components
249
+
250
+ ### PolicyLayout
251
+
252
+ Main layout component for policy pages.
253
+
254
+ ```typescript
255
+ import { PolicyLayout } from 'simple-site-framework';
256
+
257
+ <PolicyLayout
258
+ title={metadata.title}
259
+ lastUpdated={metadata.lastUpdated}
260
+ locale={locale}
261
+ showToc={true} // Optional, default: true
262
+ contactText="Questions?" // Optional
263
+ contactHref="/contact" // Optional
264
+ >
265
+ {content}
266
+ </PolicyLayout>
267
+ ```
268
+
269
+ **Props:**
270
+
271
+ | Prop | Type | Default | Description |
272
+ |------|------|---------|-------------|
273
+ | `title` | string | Required | Policy title |
274
+ | `lastUpdated` | string | Required | Last updated date |
275
+ | `locale` | string | Required | Locale for date formatting |
276
+ | `children` | ReactNode | Required | Policy content (from MDX) |
277
+ | `showToc` | boolean | `true` | Show table of contents |
278
+ | `contactText` | string | `"Questions about this policy?"` | Footer contact text |
279
+ | `contactHref` | string | `"/{locale}/contact"` | Footer contact link |
280
+ | `className` | string | - | Additional CSS classes |
281
+
282
+ ### TableOfContents
283
+
284
+ Automatically generated navigation sidebar.
285
+
286
+ ```typescript
287
+ import { TableOfContents } from 'simple-site-framework';
288
+
289
+ <TableOfContents
290
+ title="On This Page"
291
+ includeLevels={[2, 3]}
292
+ containerSelector="article"
293
+ />
294
+ ```
295
+
296
+ **Props:**
297
+
298
+ | Prop | Type | Default | Description |
299
+ |------|------|---------|-------------|
300
+ | `title` | string | `"Table of Contents"` | TOC heading |
301
+ | `includeLevels` | number[] | `[2, 3]` | Heading levels to include |
302
+ | `containerSelector` | string | `"article"` | CSS selector for content |
303
+ | `className` | string | - | Additional CSS classes |
304
+
305
+ **Features:**
306
+ - Auto-extracts headings from page
307
+ - Smooth scroll to sections
308
+ - Scroll spy (highlights current section)
309
+ - Sticky positioning
310
+ - Responsive (collapses on mobile)
311
+
312
+ ---
313
+
314
+ ## Utility Functions
315
+
316
+ ### loadPolicy()
317
+
318
+ Load and compile a policy markdown file.
319
+
320
+ ```typescript
321
+ import { loadPolicy } from 'simple-site-framework/lib/content';
322
+
323
+ const { content, metadata, slug, locale } = await loadPolicy(
324
+ 'privacy-policy', // slug
325
+ 'en', // locale
326
+ 'src/content/policies' // optional: custom directory
327
+ );
328
+ ```
329
+
330
+ **Parameters:**
331
+ - `slug` (string): Policy slug (filename without locale/extension)
332
+ - `locale` (string): Locale code
333
+ - `contentDir` (string, optional): Custom directory path, default: `'src/content/policies'`
334
+
335
+ **Returns:**
336
+ ```typescript
337
+ {
338
+ content: React.ReactElement, // Compiled MDX content
339
+ metadata: PolicyMetadata, // Frontmatter data
340
+ slug: string, // Policy slug
341
+ locale: string // Locale
342
+ }
343
+ ```
344
+
345
+ **Throws:**
346
+ - Error if file not found
347
+ - Error if required frontmatter missing
348
+
349
+ ---
350
+
351
+ ### getPolicySlugs()
352
+
353
+ Get all available policy slugs.
354
+
355
+ ```typescript
356
+ import { getPolicySlugs } from 'simple-site-framework/lib/content';
357
+
358
+ const slugs = getPolicySlugs();
359
+ // Returns: ['privacy-policy', 'terms-of-service', 'cookie-policy']
360
+ ```
361
+
362
+ **Use case:** Generate static paths for all policies
363
+
364
+ ```typescript
365
+ export async function generateStaticParams() {
366
+ const slugs = getPolicySlugs();
367
+ const locales = ['en', 'fr'];
368
+
369
+ return slugs.flatMap(slug =>
370
+ locales.map(locale => ({ slug, locale }))
371
+ );
372
+ }
373
+ ```
374
+
375
+ ---
376
+
377
+ ### getAllPolicies()
378
+
379
+ Get metadata for all policies in a specific locale.
380
+
381
+ ```typescript
382
+ import { getAllPolicies } from 'simple-site-framework/lib/content';
383
+
384
+ const policies = await getAllPolicies('en');
385
+
386
+ // Returns array of:
387
+ // [
388
+ // { slug: 'privacy-policy', locale: 'en', metadata: {...} },
389
+ // { slug: 'terms-of-service', locale: 'en', metadata: {...} }
390
+ // ]
391
+ ```
392
+
393
+ **Use case:** Generate policy index page
394
+
395
+ ```typescript
396
+ export default async function PoliciesPage({ params }) {
397
+ const policies = await getAllPolicies(params.locale);
398
+
399
+ return (
400
+ <ul>
401
+ {policies.map(policy => (
402
+ <li key={policy.slug}>
403
+ <Link href={`/${params.locale}/policies/${policy.slug}`}>
404
+ {policy.metadata.title}
405
+ </Link>
406
+ <p>{policy.metadata.description}</p>
407
+ </li>
408
+ ))}
409
+ </ul>
410
+ );
411
+ }
412
+ ```
413
+
414
+ ---
415
+
416
+ ### getPolicyLocales()
417
+
418
+ Get available locales for a specific policy.
419
+
420
+ ```typescript
421
+ import { getPolicyLocales } from 'simple-site-framework/lib/content';
422
+
423
+ const locales = getPolicyLocales('privacy-policy');
424
+ // Returns: ['en', 'fr', 'es']
425
+ ```
426
+
427
+ **Use case:** Generate language switcher for policy pages
428
+
429
+ ---
430
+
431
+ ## Multi-Language Support
432
+
433
+ ### Creating Translations
434
+
435
+ Create one markdown file per language:
436
+
437
+ ```
438
+ src/content/policies/
439
+ ├── privacy-policy.en.md
440
+ ├── privacy-policy.fr.md
441
+ ├── privacy-policy.es.md
442
+ └── privacy-policy.de.md
443
+ ```
444
+
445
+ ### Language Switcher
446
+
447
+ ```typescript
448
+ import { getPolicyLocales } from 'simple-site-framework/lib/content';
449
+ import { LanguageSelector } from 'simple-site-framework';
450
+
451
+ export default async function PolicyPage({ params }) {
452
+ const availableLocales = getPolicyLocales(params.slug);
453
+
454
+ return (
455
+ <>
456
+ <LanguageSelector
457
+ currentLocale={params.locale}
458
+ availableLocales={availableLocales} // Only show available translations
459
+ />
460
+ {/* Policy content */}
461
+ </>
462
+ );
463
+ }
464
+ ```
465
+
466
+ ### Handling Missing Translations
467
+
468
+ ```typescript
469
+ import { loadPolicy } from 'simple-site-framework/lib/content';
470
+ import { notFound } from 'next/navigation';
471
+
472
+ export default async function PolicyPage({ params }) {
473
+ try {
474
+ const policy = await loadPolicy(params.slug, params.locale);
475
+ return <PolicyLayout {...policy} />;
476
+ } catch (error) {
477
+ // Policy doesn't exist for this locale
478
+ notFound();
479
+ }
480
+ }
481
+ ```
482
+
483
+ ---
484
+
485
+ ## SEO Optimization
486
+
487
+ ### Meta Tags
488
+
489
+ ```typescript
490
+ export async function generateMetadata({ params }) {
491
+ const { metadata } = await loadPolicy(params.slug, params.locale);
492
+
493
+ return {
494
+ title: `${metadata.title} | Your Company`,
495
+ description: metadata.description,
496
+ openGraph: {
497
+ title: metadata.title,
498
+ description: metadata.description,
499
+ type: 'website',
500
+ },
501
+ };
502
+ }
503
+ ```
504
+
505
+ ### Canonical URLs
506
+
507
+ ```typescript
508
+ export async function generateMetadata({ params }) {
509
+ return {
510
+ alternates: {
511
+ canonical: `https://example.com/${params.locale}/${params.slug}`,
512
+ languages: {
513
+ 'en': `https://example.com/en/${params.slug}`,
514
+ 'fr': `https://example.com/fr/${params.slug}`,
515
+ },
516
+ },
517
+ };
518
+ }
519
+ ```
520
+
521
+ ### Indexing
522
+
523
+ Policy pages SHOULD be indexed (unlike some other legal pages):
524
+
525
+ ```typescript
526
+ export async function generateMetadata() {
527
+ return {
528
+ robots: {
529
+ index: true, // Allow indexing
530
+ follow: true,
531
+ },
532
+ };
533
+ }
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Styling
539
+
540
+ ### Tailwind Typography
541
+
542
+ The framework uses `@tailwindcss/typography` for automatic markdown styling.
543
+
544
+ **Included styles:**
545
+ - Headings (h1-h6) with proper sizing and spacing
546
+ - Paragraphs with optimal line height
547
+ - Lists (ul, ol) with proper indentation
548
+ - Links with hover states
549
+ - Blockquotes with border and background
550
+ - Code blocks with syntax highlighting
551
+ - Tables with borders and alternating rows
552
+
553
+ ### Customization
554
+
555
+ Override prose styles in PolicyLayout or globally:
556
+
557
+ ```typescript
558
+ <div className="prose prose-lg prose-primary">
559
+ {/* Custom prose theme */}
560
+ </div>
561
+ ```
562
+
563
+ **Available modifiers:**
564
+ - `prose-sm`, `prose-base`, `prose-lg`, `prose-xl`, `prose-2xl` - Size
565
+ - `prose-gray`, `prose-blue`, `prose-green` - Color theme
566
+ - `prose-headings:text-primary` - Custom heading color
567
+ - `prose-a:text-blue-600` - Custom link color
568
+
569
+ ---
570
+
571
+ ## Advanced: MDX Components
572
+
573
+ Use `.mdx` extension to embed React components in markdown.
574
+
575
+ ### Example MDX File
576
+
577
+ ```mdx
578
+ ---
579
+ title: "Privacy Policy"
580
+ lastUpdated: "2026-02-03"
581
+ ---
582
+
583
+ import { Alert } from '@/components/ui/Alert';
584
+ import { Button } from '@/components/ui/Button';
585
+
586
+ # Privacy Policy
587
+
588
+ <Alert variant="info">
589
+ This policy was last updated on {frontmatter.lastUpdated}
590
+ </Alert>
591
+
592
+ ## 1. Information We Collect
593
+
594
+ We collect the following types of information...
595
+
596
+ <Button href="/contact">Contact Us About Privacy</Button>
597
+ ```
598
+
599
+ ### When to Use MDX
600
+
601
+ - Need to embed interactive components
602
+ - Want custom alerts or callouts
603
+ - Need tables with custom styling
604
+ - Want to include forms or calculators
605
+ - Need dynamic content based on frontmatter
606
+
607
+ ### When to Use Plain Markdown
608
+
609
+ - Pure text content (most policies)
610
+ - Simplicity is important
611
+ - Non-technical editors will maintain content
612
+ - No interactive elements needed
613
+
614
+ ---
615
+
616
+ ## Performance
617
+
618
+ ### Static Generation (SSG)
619
+
620
+ Policy pages use Next.js static generation for optimal performance:
621
+
622
+ **Benefits:**
623
+ - ⚡ Instant page loads (served from CDN)
624
+ - 🔍 Perfect SEO (pre-rendered HTML)
625
+ - 💰 Low server costs (no compute per request)
626
+ - 📱 Excellent mobile performance
627
+
628
+ **Build time:**
629
+ ```bash
630
+ npm run build
631
+ # Generates static HTML for all policy pages
632
+ # app/[locale]/privacy/page.tsx → /en/privacy/index.html
633
+ ```
634
+
635
+ ### Bundle Size
636
+
637
+ Markdown content is NOT included in JavaScript bundle:
638
+
639
+ - Content compiled to HTML at build time
640
+ - Only layout components in client bundle
641
+ - ~5-10KB additional JavaScript
642
+ - 0KB content overhead (it's in HTML)
643
+
644
+ ---
645
+
646
+ ## Testing
647
+
648
+ ### Test Checklist
649
+
650
+ - [ ] Markdown renders correctly
651
+ - [ ] Frontmatter parsed properly
652
+ - [ ] Table of contents generated
653
+ - [ ] TOC scroll spy works
654
+ - [ ] Smooth scrolling to sections
655
+ - [ ] Mobile responsive layout
656
+ - [ ] All locales load correctly
657
+ - [ ] SEO meta tags present
658
+ - [ ] Last updated date formatted correctly
659
+ - [ ] Contact link works
660
+ - [ ] Heading hierarchy is valid
661
+ - [ ] No broken links
662
+ - [ ] Code blocks render properly
663
+ - [ ] Tables display correctly
664
+
665
+ ### Manual Testing
666
+
667
+ ```bash
668
+ # Start dev server
669
+ npm run dev
670
+
671
+ # Visit policy pages
672
+ http://localhost:3000/en/privacy
673
+ http://localhost:3000/fr/privacy
674
+
675
+ # Check table of contents
676
+ # - Click TOC links → smooth scroll
677
+ # - Scroll page → TOC highlights active section
678
+
679
+ # Test mobile
680
+ # - TOC should hide on small screens
681
+ # - Content should be readable
682
+ # - No horizontal scroll
683
+ ```
684
+
685
+ ---
686
+
687
+ ## Common Patterns
688
+
689
+ ### Policy Index Page
690
+
691
+ ```typescript
692
+ // app/[locale]/policies/page.tsx
693
+ import { getAllPolicies } from 'simple-site-framework/lib/content';
694
+
695
+ export default async function PoliciesPage({ params }) {
696
+ const policies = await getAllPolicies(params.locale);
697
+
698
+ return (
699
+ <div>
700
+ <h1>Legal Documents</h1>
701
+ <ul>
702
+ {policies.map(policy => (
703
+ <li key={policy.slug}>
704
+ <Link href={`/${params.locale}/policies/${policy.slug}`}>
705
+ <h3>{policy.metadata.title}</h3>
706
+ <p>{policy.metadata.description}</p>
707
+ <time>Updated: {policy.metadata.lastUpdated}</time>
708
+ </Link>
709
+ </li>
710
+ ))}
711
+ </ul>
712
+ </div>
713
+ );
714
+ }
715
+ ```
716
+
717
+ ### Dynamic Policy Routes
718
+
719
+ ```typescript
720
+ // app/[locale]/policies/[slug]/page.tsx
721
+ import { loadPolicy, getPolicySlugs } from 'simple-site-framework/lib/content';
722
+ import { PolicyLayout } from 'simple-site-framework';
723
+ import { notFound } from 'next/navigation';
724
+
725
+ export default async function PolicyPage({ params }) {
726
+ try {
727
+ const { content, metadata } = await loadPolicy(params.slug, params.locale);
728
+
729
+ return (
730
+ <PolicyLayout
731
+ title={metadata.title}
732
+ lastUpdated={metadata.lastUpdated}
733
+ locale={params.locale}
734
+ >
735
+ {content}
736
+ </PolicyLayout>
737
+ );
738
+ } catch {
739
+ notFound();
740
+ }
741
+ }
742
+
743
+ export async function generateStaticParams() {
744
+ const slugs = getPolicySlugs();
745
+ const locales = ['en', 'fr'];
746
+
747
+ return slugs.flatMap(slug =>
748
+ locales.map(locale => ({ slug, locale }))
749
+ );
750
+ }
751
+ ```
752
+
753
+ ---
754
+
755
+ ## Best Practices
756
+
757
+ ### Content
758
+
759
+ ✅ **DO:**
760
+ - Write in clear, plain language
761
+ - Use descriptive section headings
762
+ - Include "Last updated" date
763
+ - Provide contact information
764
+ - Link to related policies
765
+ - Use bullet points for lists
766
+ - Keep paragraphs short (3-4 sentences)
767
+
768
+ ❌ **DON'T:**
769
+ - Use overly legal jargon
770
+ - Create walls of text
771
+ - Skip heading levels
772
+ - Forget to update date when editing
773
+ - Mix multiple policies in one file
774
+
775
+ ### Structure
776
+
777
+ ✅ **DO:**
778
+ - Use consistent heading hierarchy
779
+ - Create logical sections
780
+ - Include table of contents
781
+ - Add introduction/overview
782
+ - Provide contact section at end
783
+
784
+ ❌ **DON'T:**
785
+ - Use more than 3-4 heading levels
786
+ - Create sections with only one subsection
787
+ - Nest lists too deeply
788
+
789
+ ### Maintenance
790
+
791
+ ✅ **DO:**
792
+ - Version control all changes
793
+ - Review policies annually
794
+ - Update "lastUpdated" date
795
+ - Get legal review before publishing
796
+ - Test all locales after updates
797
+
798
+ ❌ **DON'T:**
799
+ - Edit directly in production
800
+ - Forget to update translations
801
+ - Leave outdated information
802
+
803
+ ---
804
+
805
+ ## Troubleshooting
806
+
807
+ ### "Policy file not found"
808
+
809
+ **Error:** `Policy file not found: privacy-policy.en.md`
810
+
811
+ **Solution:**
812
+ 1. Check file exists: `src/content/policies/privacy-policy.en.md`
813
+ 2. Verify filename format: `{slug}.{locale}.md`
814
+ 3. Check file extension: `.md` or `.mdx`
815
+ 4. Verify contentDir path if custom
816
+
817
+ ### "Missing required frontmatter"
818
+
819
+ **Error:** `Policy privacy-policy.en.md is missing required frontmatter field: title`
820
+
821
+ **Solution:**
822
+ 1. Add frontmatter at top of file:
823
+ ```markdown
824
+ ---
825
+ title: "Your Title"
826
+ lastUpdated: "2026-02-03"
827
+ ---
828
+ ```
829
+ 2. Ensure proper YAML format (quotes, colons)
830
+ 3. Check for typos in field names
831
+
832
+ ### Table of Contents not showing
833
+
834
+ **Solutions:**
835
+ 1. Ensure headings use proper markdown (`##`, `###`)
836
+ 2. Check `showToc={true}` in PolicyLayout
837
+ 3. Verify at least one H2 or H3 exists
838
+ 4. Check `includeLevels` matches heading levels
839
+
840
+ ### Styling not applied
841
+
842
+ **Solutions:**
843
+ 1. Install Tailwind Typography: `npm install -D @tailwindcss/typography`
844
+ 2. Add to tailwind.config.js: `plugins: [require('@tailwindcss/typography')]`
845
+ 3. Restart dev server after config changes
846
+
847
+ ---
848
+
849
+ ## Examples
850
+
851
+ See complete examples in:
852
+ - `examples/policies/privacy-policy.en.md`
853
+ - `examples/policies/terms-of-service.en.md`
854
+ - `examples/app/[locale]/policies/` (page components)
855
+
856
+ ---
857
+
858
+ ## Resources
859
+
860
+ - [Tailwind Typography Docs](https://tailwindcss.com/docs/typography-plugin)
861
+ - [next-mdx-remote](https://github.com/hashicorp/next-mdx-remote)
862
+ - [Markdown Guide](https://www.markdownguide.org/)
863
+ - [YAML Frontmatter Spec](https://jekyllrb.com/docs/front-matter/)
864
+
865
+ ---
866
+
867
+ **Questions?** Open an issue on GitHub or contact us.