@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,390 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * create-simple-site CLI tool
5
+ * Scaffolds new projects with the Simple Site Framework
6
+ */
7
+
8
+ const { Command } = require('commander');
9
+ const inquirer = require('inquirer');
10
+ const chalk = require('chalk');
11
+ const ora = require('ora');
12
+ const path = require('path');
13
+ const fs = require('fs-extra');
14
+ const { execSync } = require('child_process');
15
+
16
+ const program = new Command();
17
+
18
+ program
19
+ .name('create-simple-site')
20
+ .description('Create a new Simple Site Framework project')
21
+ .argument('[project-name]', 'Name of your project')
22
+ .option('-t, --template <template>', 'Template to use (service-business, saas, portfolio, blog, blank)')
23
+ .option('--skip-install', 'Skip npm install')
24
+ .option('--skip-git', 'Skip git initialization')
25
+ .action(async (projectName, options) => {
26
+ try {
27
+ await createProject(projectName, options);
28
+ } catch (error) {
29
+ console.error(chalk.red('\n✖ Error:'), error.message);
30
+ process.exit(1);
31
+ }
32
+ });
33
+
34
+ program.parse();
35
+
36
+ async function createProject(projectName, options) {
37
+ console.log(chalk.bold.cyan('\n🚀 Simple Site Framework\n'));
38
+
39
+ // Get project name if not provided
40
+ if (!projectName) {
41
+ const answers = await inquirer.prompt([
42
+ {
43
+ type: 'input',
44
+ name: 'projectName',
45
+ message: 'Project name:',
46
+ default: 'my-site',
47
+ validate: (input) => {
48
+ if (/^([A-Za-z\-\_\d])+$/.test(input)) return true;
49
+ return 'Project name may only include letters, numbers, underscores and hashes.';
50
+ }
51
+ }
52
+ ]);
53
+ projectName = answers.projectName;
54
+ }
55
+
56
+ const projectPath = path.join(process.cwd(), projectName);
57
+
58
+ // Check if directory exists
59
+ if (fs.existsSync(projectPath)) {
60
+ const { overwrite } = await inquirer.prompt([
61
+ {
62
+ type: 'confirm',
63
+ name: 'overwrite',
64
+ message: `Directory ${projectName} already exists. Overwrite?`,
65
+ default: false
66
+ }
67
+ ]);
68
+
69
+ if (!overwrite) {
70
+ console.log(chalk.yellow('\n✖ Cancelled'));
71
+ process.exit(0);
72
+ }
73
+
74
+ fs.removeSync(projectPath);
75
+ }
76
+
77
+ // Interactive setup
78
+ const config = await interactiveSetup(options);
79
+
80
+ // Create project
81
+ const spinner = ora('Creating project...').start();
82
+
83
+ try {
84
+ // Create directory
85
+ fs.ensureDirSync(projectPath);
86
+
87
+ // Copy template
88
+ await copyTemplate(projectPath, config.template);
89
+
90
+ // Update configuration files
91
+ await updateConfig(projectPath, config);
92
+
93
+ spinner.succeed('Project created!');
94
+
95
+ // Install dependencies
96
+ if (!options.skipInstall) {
97
+ const installSpinner = ora('Installing dependencies...').start();
98
+ try {
99
+ execSync('npm install', {
100
+ cwd: projectPath,
101
+ stdio: 'ignore'
102
+ });
103
+ installSpinner.succeed('Dependencies installed!');
104
+ } catch (error) {
105
+ installSpinner.fail('Failed to install dependencies');
106
+ console.log(chalk.yellow(' Run `npm install` manually in the project directory'));
107
+ }
108
+ }
109
+
110
+ // Initialize git
111
+ if (!options.skipGit) {
112
+ const gitSpinner = ora('Initializing git repository...').start();
113
+ try {
114
+ execSync('git init', { cwd: projectPath, stdio: 'ignore' });
115
+ execSync('git add -A', { cwd: projectPath, stdio: 'ignore' });
116
+ execSync('git commit -m "Initial commit from create-simple-site"', {
117
+ cwd: projectPath,
118
+ stdio: 'ignore'
119
+ });
120
+ gitSpinner.succeed('Git repository initialized!');
121
+ } catch (error) {
122
+ gitSpinner.fail('Failed to initialize git');
123
+ }
124
+ }
125
+
126
+ // Success message
127
+ console.log(chalk.green('\n✓ Project ready!\n'));
128
+ console.log(chalk.bold('Next steps:'));
129
+ console.log(chalk.gray(` cd ${projectName}`));
130
+ if (options.skipInstall) {
131
+ console.log(chalk.gray(' npm install'));
132
+ }
133
+ console.log(chalk.gray(' npm run dev\n'));
134
+
135
+ } catch (error) {
136
+ spinner.fail('Failed to create project');
137
+ throw error;
138
+ }
139
+ }
140
+
141
+ async function interactiveSetup(options) {
142
+ const questions = [];
143
+
144
+ // Template selection
145
+ if (!options.template) {
146
+ questions.push({
147
+ type: 'list',
148
+ name: 'template',
149
+ message: 'Choose a template:',
150
+ choices: [
151
+ { name: 'Service Business - Professional services (law, consulting)', value: 'service-business' },
152
+ { name: 'SaaS - Software-as-a-service marketing site', value: 'saas' },
153
+ { name: 'Portfolio - Personal/agency portfolio', value: 'portfolio' },
154
+ { name: 'Blog - Content-focused blog', value: 'blog' },
155
+ { name: 'Blank - Minimal starter', value: 'blank' }
156
+ ],
157
+ default: 'service-business'
158
+ });
159
+ }
160
+
161
+ // Theme configuration
162
+ questions.push(
163
+ {
164
+ type: 'input',
165
+ name: 'primaryColor',
166
+ message: 'Primary color (hex):',
167
+ default: '#f97316',
168
+ validate: (input) => /^#[0-9A-F]{6}$/i.test(input) || 'Please enter a valid hex color'
169
+ },
170
+ {
171
+ type: 'list',
172
+ name: 'fontHeading',
173
+ message: 'Heading font:',
174
+ choices: ['Inter', 'Poppins', 'Montserrat', 'Roboto', 'Open Sans'],
175
+ default: 'Inter'
176
+ },
177
+ {
178
+ type: 'list',
179
+ name: 'fontBody',
180
+ message: 'Body font:',
181
+ choices: ['Inter', 'Roboto', 'Open Sans', 'Lato', 'Source Sans Pro'],
182
+ default: 'Inter'
183
+ },
184
+ {
185
+ type: 'checkbox',
186
+ name: 'locales',
187
+ message: 'Languages to support:',
188
+ choices: [
189
+ { name: 'English', value: 'en', checked: true },
190
+ { name: 'French', value: 'fr' }
191
+ ],
192
+ validate: (answer) => answer.length > 0 || 'You must choose at least one language'
193
+ }
194
+ );
195
+
196
+ // Company info
197
+ questions.push(
198
+ {
199
+ type: 'input',
200
+ name: 'companyName',
201
+ message: 'Company name:',
202
+ default: 'My Company'
203
+ },
204
+ {
205
+ type: 'input',
206
+ name: 'companyEmail',
207
+ message: 'Contact email:',
208
+ default: 'contact@example.com'
209
+ },
210
+ {
211
+ type: 'input',
212
+ name: 'companyPhone',
213
+ message: 'Phone number (optional):',
214
+ default: ''
215
+ }
216
+ );
217
+
218
+ const answers = await inquirer.prompt(questions);
219
+
220
+ return {
221
+ template: options.template || answers.template,
222
+ theme: {
223
+ primaryColor: answers.primaryColor,
224
+ fontHeading: answers.fontHeading,
225
+ fontBody: answers.fontBody
226
+ },
227
+ locales: answers.locales,
228
+ company: {
229
+ name: answers.companyName,
230
+ email: answers.companyEmail,
231
+ phone: answers.companyPhone
232
+ }
233
+ };
234
+ }
235
+
236
+ async function copyTemplate(projectPath, template) {
237
+ const templatesDir = path.join(__dirname, '..', 'templates');
238
+ const templatePath = path.join(templatesDir, template);
239
+
240
+ if (!fs.existsSync(templatePath)) {
241
+ // Fallback to blank template
242
+ const blankPath = path.join(templatesDir, 'blank');
243
+ if (fs.existsSync(blankPath)) {
244
+ fs.copySync(blankPath, projectPath);
245
+ } else {
246
+ // Create minimal structure if no template exists
247
+ createMinimalStructure(projectPath);
248
+ }
249
+ } else {
250
+ fs.copySync(templatePath, projectPath);
251
+ }
252
+ }
253
+
254
+ function createMinimalStructure(projectPath) {
255
+ // Create basic Next.js App Router structure
256
+ const structure = {
257
+ 'app': {
258
+ 'layout.tsx': `import './globals.css'
259
+
260
+ export default function RootLayout({
261
+ children,
262
+ }: {
263
+ children: React.ReactNode
264
+ }) {
265
+ return (
266
+ <html lang="en">
267
+ <body>{children}</body>
268
+ </html>
269
+ )
270
+ }`,
271
+ 'page.tsx': `export default function Home() {
272
+ return <main className="min-h-screen p-8">
273
+ <h1 className="text-4xl font-bold">Welcome to Simple Site Framework</h1>
274
+ </main>
275
+ }`,
276
+ 'globals.css': `@tailwind base;
277
+ @tailwind components;
278
+ @tailwind utilities;`
279
+ },
280
+ 'config': {
281
+ 'theme.ts': `export const theme = {
282
+ colors: {
283
+ primary: '#f97316'
284
+ }
285
+ }`
286
+ },
287
+ 'public': {},
288
+ 'package.json': JSON.stringify({
289
+ name: 'my-site',
290
+ version: '0.1.0',
291
+ private: true,
292
+ scripts: {
293
+ dev: 'next dev',
294
+ build: 'next build',
295
+ start: 'next start',
296
+ lint: 'next lint'
297
+ },
298
+ dependencies: {
299
+ '@zoyth/simple-site-framework': 'latest',
300
+ 'next': '^14.0.0',
301
+ 'react': '^18.0.0',
302
+ 'react-dom': '^18.0.0'
303
+ },
304
+ devDependencies: {
305
+ '@types/node': '^20.0.0',
306
+ '@types/react': '^18.0.0',
307
+ '@types/react-dom': '^18.0.0',
308
+ 'typescript': '^5.0.0',
309
+ 'tailwindcss': '^3.4.0'
310
+ }
311
+ }, null, 2),
312
+ 'tsconfig.json': JSON.stringify({
313
+ compilerOptions: {
314
+ target: 'es5',
315
+ lib: ['dom', 'dom.iterable', 'esnext'],
316
+ allowJs: true,
317
+ skipLibCheck: true,
318
+ strict: true,
319
+ forceConsistentCasingInFileNames: true,
320
+ noEmit: true,
321
+ esModuleInterop: true,
322
+ module: 'esnext',
323
+ moduleResolution: 'bundler',
324
+ resolveJsonModule: true,
325
+ isolatedModules: true,
326
+ jsx: 'preserve',
327
+ incremental: true,
328
+ plugins: [{ name: 'next' }],
329
+ paths: { '@/*': ['./*'] }
330
+ },
331
+ include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
332
+ exclude: ['node_modules']
333
+ }, null, 2),
334
+ 'next.config.js': `/** @type {import('next').NextConfig} */
335
+ const nextConfig = {}
336
+
337
+ module.exports = nextConfig`,
338
+ 'tailwind.config.ts': `import type { Config } from 'tailwindcss'
339
+
340
+ const config: Config = {
341
+ content: [
342
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
343
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
344
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
345
+ ],
346
+ theme: {
347
+ extend: {},
348
+ },
349
+ plugins: [],
350
+ }
351
+ export default config`,
352
+ '.gitignore': `node_modules
353
+ .next
354
+ out
355
+ .env*.local
356
+ .DS_Store`
357
+ };
358
+
359
+ function createStructure(basePath, struct) {
360
+ for (const [key, value] of Object.entries(struct)) {
361
+ const itemPath = path.join(basePath, key);
362
+ if (typeof value === 'string') {
363
+ fs.writeFileSync(itemPath, value);
364
+ } else {
365
+ fs.ensureDirSync(itemPath);
366
+ createStructure(itemPath, value);
367
+ }
368
+ }
369
+ }
370
+
371
+ createStructure(projectPath, structure);
372
+ }
373
+
374
+ async function updateConfig(projectPath, config) {
375
+ // Update theme config
376
+ const themeConfigPath = path.join(projectPath, 'config', 'theme.ts');
377
+ if (fs.existsSync(themeConfigPath)) {
378
+ let themeContent = fs.readFileSync(themeConfigPath, 'utf-8');
379
+ themeContent = themeContent.replace(/#[0-9A-F]{6}/gi, config.theme.primaryColor);
380
+ fs.writeFileSync(themeConfigPath, themeContent);
381
+ }
382
+
383
+ // Update package.json with company name
384
+ const packageJsonPath = path.join(projectPath, 'package.json');
385
+ if (fs.existsSync(packageJsonPath)) {
386
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
387
+ packageJson.name = config.company.name.toLowerCase().replace(/\s+/g, '-');
388
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
389
+ }
390
+ }