@pixelated-tech/components 3.2.14 → 3.3.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 (202) hide show
  1. package/README.COMPONENTS.md +520 -31
  2. package/README.md +163 -49
  3. package/dist/components/cms/cloudinary.image.js +1 -0
  4. package/dist/components/cms/wordpress.components.js +1 -0
  5. package/dist/components/general/sidepanel.js +1 -1
  6. package/dist/components/general/tab.css +105 -0
  7. package/dist/components/general/tab.js +26 -0
  8. package/dist/components/menu/menu-accordion.css +10 -0
  9. package/dist/components/menu/menu-accordion.js +2 -1
  10. package/dist/components/menu/menu-simple.css +0 -7
  11. package/dist/components/seo/metadata.components.js +0 -19
  12. package/dist/components/seo/metadata.functions.js +111 -0
  13. package/dist/components/seo/schema-blogposting.functions.js +42 -0
  14. package/dist/components/seo/schema-blogposting.js +0 -46
  15. package/dist/components/seo/sitemap.js +1 -1
  16. package/dist/components/shoppingcart/shoppingcart.components.js +4 -4
  17. package/dist/components/sitebuilder/config/CompoundFontSelector.css +25 -0
  18. package/dist/components/sitebuilder/config/CompoundFontSelector.js +41 -0
  19. package/dist/components/sitebuilder/config/ConfigBuilder.css +277 -0
  20. package/dist/components/sitebuilder/config/ConfigBuilder.js +380 -0
  21. package/dist/components/sitebuilder/config/ConfigEngine.js +82 -0
  22. package/dist/components/sitebuilder/config/FontSelector.css +82 -0
  23. package/dist/components/sitebuilder/config/FontSelector.js +115 -0
  24. package/dist/components/sitebuilder/config/google-fonts.js +112 -0
  25. package/dist/components/{pagebuilder → sitebuilder}/form/form.css +55 -34
  26. package/dist/components/sitebuilder/form/formbuilder.js +107 -0
  27. package/dist/components/sitebuilder/form/formcomponents.js +380 -0
  28. package/dist/components/sitebuilder/form/formengine.js +82 -0
  29. package/dist/components/{pagebuilder/form/form.js → sitebuilder/form/formextractor.js} +10 -211
  30. package/dist/components/sitebuilder/form/formutils.js +206 -0
  31. package/dist/components/sitebuilder/form/formvalidator.js +123 -0
  32. package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.js +1 -1
  33. package/dist/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.js +2 -2
  34. package/dist/components/{pagebuilder → sitebuilder/page}/components/PageEngine.js +1 -1
  35. package/dist/components/sitebuilder/page/documentation/api-examples/save-route-example.js +37 -0
  36. package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMap.js +3 -3
  37. package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.js +2 -2
  38. package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.js +2 -2
  39. package/dist/components/sitebuilder/page/lib/pageStorageTypes.js +1 -0
  40. package/dist/components/structured/markdown.js +1 -0
  41. package/dist/components/structured/recipe.js +1 -0
  42. package/dist/components/structured/timeline.js +1 -0
  43. package/dist/css/pixelated.global.css +0 -35
  44. package/dist/css/pixelated.grid.scss +4 -0
  45. package/dist/css/pixelated.visualdesign.scss +88 -0
  46. package/dist/data/form.json +18 -18
  47. package/dist/data/routes.json +32 -1
  48. package/dist/data/shipping.to.json +9 -9
  49. package/dist/data/siteinfo-form.json +200 -0
  50. package/dist/data/visualdesignform.json +244 -0
  51. package/dist/index.js +33 -21
  52. package/dist/index.server.js +24 -17
  53. package/dist/types/components/cms/cloudinary.image.d.ts.map +1 -1
  54. package/dist/types/components/cms/wordpress.components.d.ts.map +1 -1
  55. package/dist/types/components/general/semantic.d.ts +3 -3
  56. package/dist/types/components/general/tab.d.ts +18 -0
  57. package/dist/types/components/general/tab.d.ts.map +1 -0
  58. package/dist/types/components/menu/menu-accordion.d.ts.map +1 -1
  59. package/dist/types/components/seo/metadata.components.d.ts +0 -17
  60. package/dist/types/components/seo/metadata.components.d.ts.map +1 -1
  61. package/dist/types/components/seo/{metadata.d.ts → metadata.functions.d.ts} +15 -1
  62. package/dist/types/components/seo/metadata.functions.d.ts.map +1 -0
  63. package/dist/types/components/seo/schema-blogposting.d.ts +1 -25
  64. package/dist/types/components/seo/schema-blogposting.d.ts.map +1 -1
  65. package/dist/types/components/seo/schema-blogposting.functions.d.ts +26 -0
  66. package/dist/types/components/seo/schema-blogposting.functions.d.ts.map +1 -0
  67. package/dist/types/components/seo/sitemap.d.ts.map +1 -1
  68. package/dist/types/components/shoppingcart/shoppingcart.components.d.ts +1 -1
  69. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts +23 -0
  70. package/dist/types/components/sitebuilder/config/CompoundFontSelector.d.ts.map +1 -0
  71. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts +354 -0
  72. package/dist/types/components/sitebuilder/config/ConfigBuilder.d.ts.map +1 -0
  73. package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts +10 -0
  74. package/dist/types/components/sitebuilder/config/ConfigEngine.d.ts.map +1 -0
  75. package/dist/types/components/sitebuilder/config/FontSelector.d.ts +27 -0
  76. package/dist/types/components/sitebuilder/config/FontSelector.d.ts.map +1 -0
  77. package/dist/types/components/sitebuilder/config/google-fonts.d.ts +41 -0
  78. package/dist/types/components/sitebuilder/config/google-fonts.d.ts.map +1 -0
  79. package/dist/types/components/sitebuilder/form/formbuilder.d.ts +11 -0
  80. package/dist/types/components/sitebuilder/form/formbuilder.d.ts.map +1 -0
  81. package/dist/types/components/{pagebuilder → sitebuilder}/form/formcomponents.d.ts +15 -17
  82. package/dist/types/components/sitebuilder/form/formcomponents.d.ts.map +1 -0
  83. package/dist/types/components/{pagebuilder/form/form.submit.d.ts → sitebuilder/form/formemailer.d.ts} +1 -1
  84. package/dist/types/components/sitebuilder/form/formemailer.d.ts.map +1 -0
  85. package/dist/types/components/sitebuilder/form/formengine.d.ts +14 -0
  86. package/dist/types/components/sitebuilder/form/formengine.d.ts.map +1 -0
  87. package/dist/types/components/sitebuilder/form/formextractor.d.ts +25 -0
  88. package/dist/types/components/sitebuilder/form/formextractor.d.ts.map +1 -0
  89. package/dist/types/components/{pagebuilder/form/formvalidations.d.ts → sitebuilder/form/formfieldvalidations.d.ts} +1 -1
  90. package/dist/types/components/sitebuilder/form/formfieldvalidations.d.ts.map +1 -0
  91. package/dist/types/components/sitebuilder/form/formtypes.d.ts +66 -0
  92. package/dist/types/components/sitebuilder/form/formtypes.d.ts.map +1 -0
  93. package/dist/types/components/sitebuilder/form/formutils.d.ts +20 -0
  94. package/dist/types/components/sitebuilder/form/formutils.d.ts.map +1 -0
  95. package/dist/types/components/sitebuilder/form/formvalidator.d.ts +20 -0
  96. package/dist/types/components/sitebuilder/form/formvalidator.d.ts.map +1 -0
  97. package/dist/types/components/sitebuilder/page/components/ComponentPropertiesForm.d.ts.map +1 -0
  98. package/dist/types/components/sitebuilder/page/components/ComponentSelector.d.ts.map +1 -0
  99. package/dist/types/components/sitebuilder/page/components/ComponentTree.d.ts.map +1 -0
  100. package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageBuilderUI.d.ts +1 -1
  101. package/dist/types/components/sitebuilder/page/components/PageBuilderUI.d.ts.map +1 -0
  102. package/dist/types/components/sitebuilder/page/components/PageEngine.d.ts.map +1 -0
  103. package/dist/types/components/sitebuilder/page/components/SaveLoadSection.d.ts.map +1 -0
  104. package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts +6 -0
  105. package/dist/types/components/sitebuilder/page/documentation/api-examples/save-route-example.d.ts.map +1 -0
  106. package/dist/types/components/sitebuilder/page/lib/componentGeneration.d.ts.map +1 -0
  107. package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMap.d.ts +3 -3
  108. package/dist/types/components/sitebuilder/page/lib/componentMap.d.ts.map +1 -0
  109. package/dist/types/components/sitebuilder/page/lib/componentMetadata.d.ts.map +1 -0
  110. package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageContentful.d.ts +1 -1
  111. package/dist/types/components/sitebuilder/page/lib/pageStorageContentful.d.ts.map +1 -0
  112. package/dist/types/components/sitebuilder/page/lib/pageStorageLocal.d.ts.map +1 -0
  113. package/dist/types/components/sitebuilder/page/lib/pageStorageTypes.d.ts.map +1 -0
  114. package/dist/types/components/sitebuilder/page/lib/propTypeIntrospection.d.ts.map +1 -0
  115. package/dist/types/components/sitebuilder/page/lib/types.d.ts.map +1 -0
  116. package/dist/types/components/sitebuilder/page/lib/usePageBuilder.d.ts.map +1 -0
  117. package/dist/types/components/structured/markdown.d.ts.map +1 -1
  118. package/dist/types/components/structured/recipe.d.ts.map +1 -1
  119. package/dist/types/components/structured/timeline.d.ts.map +1 -1
  120. package/dist/types/index.d.ts +33 -20
  121. package/dist/types/index.server.d.ts +23 -16
  122. package/dist/types/stories/general/tab.stories.d.ts +45 -0
  123. package/dist/types/stories/general/tab.stories.d.ts.map +1 -0
  124. package/dist/types/stories/seo/seo.metadata.stories.d.ts +1 -1
  125. package/dist/types/stories/seo/seo.metadata.stories.d.ts.map +1 -1
  126. package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts +51 -0
  127. package/dist/types/stories/sitebuilder/compoundfontselector.stories.d.ts.map +1 -0
  128. package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts +103 -0
  129. package/dist/types/stories/sitebuilder/configbuilder.stories.d.ts.map +1 -0
  130. package/dist/types/stories/{pagebuilder → sitebuilder}/form-builder.stories.d.ts +1 -1
  131. package/dist/types/stories/sitebuilder/form-builder.stories.d.ts.map +1 -0
  132. package/dist/types/stories/{pagebuilder → sitebuilder}/form-engine.stories.d.ts +1 -1
  133. package/dist/types/stories/sitebuilder/form-engine.stories.d.ts.map +1 -0
  134. package/dist/types/stories/{pagebuilder → sitebuilder}/form-extractor.stories.d.ts +1 -1
  135. package/dist/types/stories/sitebuilder/form-extractor.stories.d.ts.map +1 -0
  136. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts +1 -1
  137. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.stories.d.ts.map +1 -1
  138. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts +1 -1
  139. package/dist/types/stories/{pagebuilder → sitebuilder}/pagebuilder.usageguide.stories.d.ts.map +1 -1
  140. package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts +1 -1
  141. package/dist/types/stories/{pagebuilder → sitebuilder}/pageengine.stories.d.ts.map +1 -1
  142. package/dist/types/tests/compoundfontselector.test.d.ts +2 -0
  143. package/dist/types/tests/compoundfontselector.test.d.ts.map +1 -0
  144. package/dist/types/tests/configbuilder.test.d.ts +2 -0
  145. package/dist/types/tests/configbuilder.test.d.ts.map +1 -0
  146. package/dist/types/tests/configengine.test.d.ts +2 -0
  147. package/dist/types/tests/configengine.test.d.ts.map +1 -0
  148. package/dist/types/tests/fontselector.test.d.ts +2 -0
  149. package/dist/types/tests/fontselector.test.d.ts.map +1 -0
  150. package/dist/types/tests/tab.test.d.ts +2 -0
  151. package/dist/types/tests/tab.test.d.ts.map +1 -0
  152. package/package.json +15 -12
  153. package/dist/components/pagebuilder/form/formcomponents.js +0 -359
  154. package/dist/components/seo/metadata.js +0 -108
  155. package/dist/types/components/pagebuilder/components/ComponentPropertiesForm.d.ts.map +0 -1
  156. package/dist/types/components/pagebuilder/components/ComponentSelector.d.ts.map +0 -1
  157. package/dist/types/components/pagebuilder/components/ComponentTree.d.ts.map +0 -1
  158. package/dist/types/components/pagebuilder/components/PageBuilderUI.d.ts.map +0 -1
  159. package/dist/types/components/pagebuilder/components/PageEngine.d.ts.map +0 -1
  160. package/dist/types/components/pagebuilder/components/SaveLoadSection.d.ts.map +0 -1
  161. package/dist/types/components/pagebuilder/form/form.d.ts +0 -46
  162. package/dist/types/components/pagebuilder/form/form.d.ts.map +0 -1
  163. package/dist/types/components/pagebuilder/form/form.submit.d.ts.map +0 -1
  164. package/dist/types/components/pagebuilder/form/formcomponents.d.ts.map +0 -1
  165. package/dist/types/components/pagebuilder/form/formvalidations.d.ts.map +0 -1
  166. package/dist/types/components/pagebuilder/lib/componentGeneration.d.ts.map +0 -1
  167. package/dist/types/components/pagebuilder/lib/componentMap.d.ts.map +0 -1
  168. package/dist/types/components/pagebuilder/lib/componentMetadata.d.ts.map +0 -1
  169. package/dist/types/components/pagebuilder/lib/pageStorageContentful.d.ts.map +0 -1
  170. package/dist/types/components/pagebuilder/lib/pageStorageLocal.d.ts.map +0 -1
  171. package/dist/types/components/pagebuilder/lib/pageStorageTypes.d.ts.map +0 -1
  172. package/dist/types/components/pagebuilder/lib/propTypeIntrospection.d.ts.map +0 -1
  173. package/dist/types/components/pagebuilder/lib/types.d.ts.map +0 -1
  174. package/dist/types/components/pagebuilder/lib/usePageBuilder.d.ts.map +0 -1
  175. package/dist/types/components/seo/metadata.d.ts.map +0 -1
  176. package/dist/types/stories/pagebuilder/form-builder.stories.d.ts.map +0 -1
  177. package/dist/types/stories/pagebuilder/form-engine.stories.d.ts.map +0 -1
  178. package/dist/types/stories/pagebuilder/form-extractor.stories.d.ts.map +0 -1
  179. /package/dist/components/{pagebuilder/form/form.submit.js → sitebuilder/form/formemailer.js} +0 -0
  180. /package/dist/components/{pagebuilder/form/formvalidations.js → sitebuilder/form/formfieldvalidations.js} +0 -0
  181. /package/dist/components/{pagebuilder/lib/pageStorageTypes.js → sitebuilder/form/formtypes.js} +0 -0
  182. /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.js +0 -0
  183. /package/dist/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.js +0 -0
  184. /package/dist/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.js +0 -0
  185. /package/dist/components/{pagebuilder → sitebuilder/page}/components/pagebuilder.scss +0 -0
  186. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.js +0 -0
  187. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.js +0 -0
  188. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.js +0 -0
  189. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/types.js +0 -0
  190. /package/dist/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.js +0 -0
  191. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentPropertiesForm.d.ts +0 -0
  192. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentSelector.d.ts +0 -0
  193. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/ComponentTree.d.ts +0 -0
  194. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/PageEngine.d.ts +0 -0
  195. /package/dist/types/components/{pagebuilder → sitebuilder/page}/components/SaveLoadSection.d.ts +0 -0
  196. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentGeneration.d.ts +0 -0
  197. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/componentMetadata.d.ts +0 -0
  198. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageLocal.d.ts +0 -0
  199. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/pageStorageTypes.d.ts +0 -0
  200. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/propTypeIntrospection.d.ts +0 -0
  201. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/types.d.ts +0 -0
  202. /package/dist/types/components/{pagebuilder → sitebuilder/page}/lib/usePageBuilder.d.ts +0 -0
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  <h3 align="center">Pixelated Components</h3>
19
19
 
20
20
  <p align="center">
21
- A comprehensive React component library for modern web development, featuring CMS integrations, UI components, and SEO optimization tools.
21
+ A comprehensive React component library for modern web development, featuring CMS integrations, UI components, SEO optimization tools, and accessibility-first design.
22
22
  <br />
23
23
  <a href="https://github.com/brianwhaley/pixelated-components"><strong>Explore the docs »</strong></a>
24
24
  <br />
@@ -60,6 +60,27 @@ This is a library of components I have found useful to build web sites quickly.
60
60
 
61
61
 
62
62
 
63
+ ## ♿ Accessibility
64
+
65
+ Pixelated Components is committed to creating inclusive web experiences. All components are built with accessibility in mind and include:
66
+
67
+ - **WCAG 2.1 AA compliance** - Components meet web accessibility guidelines
68
+ - **Keyboard navigation** - Full keyboard support for all interactive elements
69
+ - **Screen reader support** - Proper ARIA labels, roles, and semantic HTML
70
+ - **Focus management** - Clear focus indicators and logical tab order
71
+ - **Color contrast** - High contrast ratios for text and interactive elements
72
+ - **Semantic HTML** - Proper use of headings, landmarks, and structural elements
73
+
74
+ ### Accessibility Features
75
+ - Automatic ARIA label generation
76
+ - Focus trap management for modals and overlays
77
+ - Skip links for keyboard users
78
+ - Reduced motion support for users with vestibular disorders
79
+ - High contrast mode compatibility
80
+
81
+
82
+
83
+
63
84
  ## 📦 Installation & Setup
64
85
 
65
86
  ### Requirements
@@ -93,54 +114,126 @@ npm install react react-dom prop-types
93
114
 
94
115
  This library is written in TypeScript and provides full type definitions. No additional setup required.
95
116
 
117
+ ### Reference Implementation
118
+
119
+ For a complete working example of Pixelated Components in action, check out the [pixelated-admin](https://github.com/brianwhaley/pixelated-admin) project. This admin interface demonstrates:
120
+
121
+ - **Component Integration**: Real-world usage of all major components
122
+ - **Configuration Management**: Dynamic site configuration with ConfigBuilder
123
+ - **Page Building**: Visual page construction with PageBuilderUI
124
+ - **Authentication**: NextAuth.js integration for secure admin access
125
+ - **Deployment Ready**: Production-ready setup with HTTPS and optimized builds
96
126
 
127
+ ```bash
128
+ # Clone the reference implementation
129
+ git clone https://github.com/brianwhaley/pixelated-admin.git
130
+ cd pixelated-admin
131
+ npm install
132
+ npm run dev
133
+ ```
97
134
 
98
- ## 🧩 Component Categories
135
+ Visit `http://localhost:3006` to explore the admin interface and see components in action.
99
136
 
100
137
  ### General Components
101
138
  Reusable UI components for common patterns:
102
139
  - **Accordion** - Expandable content sections using native `<details>` elements
103
140
  - **Callout** - Flexible content highlight blocks with image support
104
- - **Modal** - Dialog overlays and popups
141
+ - **CSS** - Dynamic CSS utilities and styling helpers
142
+ - **Image** - Advanced image component with lazy loading and optimization
105
143
  - **Loading** - Progress indicators and loading states
106
- - **Panel** - Content containers with various layouts
144
+ - **MicroInteractions** - Subtle animations and interaction effects
145
+ - **Modal** - Dialog overlays and popups
146
+ - **Semantic** - Semantic HTML structure components
147
+ - **SidePanel** - Slide-out panel component for additional content
148
+ - **Table** - Data display and table components
107
149
 
108
150
  ### CMS Integration
109
151
  Headless CMS and content management components:
110
152
  - **WordPress** - Blog post integration and display with automatic Photon CDN URL processing
111
- - **Contentful** - Headless CMS components and utilities
153
+ - **Contentful** - Headless CMS components and utilities with delivery and management APIs
112
154
  - **PageBuilder** - Dynamic page construction from JSON
113
155
  - **PageEngine** - Advanced page rendering with Contentful integration
114
156
 
115
157
  ### UI Components
116
158
  User interface and interaction components:
117
159
  - **Carousel** - Image and content sliders (Hero, Reviews, Portfolio)
118
- - **Forms** - Form builder and validation components
160
+ - **Forms** - Form builder, validation, and emailer components
161
+ - **FormBuilder** - Advanced form construction and configuration
162
+ - **FormComponents** - Individual form field components and utilities
163
+ - **FormEngine** - Form rendering and processing engine
119
164
  - **Menu** - Navigation components (Simple, Accordion, Expando)
120
- - **Tables** - Data display and table components
165
+ - **Tab** - Tabbed interface component for organizing content
121
166
  - **Tiles** - Image grid and tile layouts
122
167
 
168
+ ### Development Tools
169
+ Components for development, configuration, and site building:
170
+ - **ComponentPropertiesForm** - Form for editing component properties
171
+ - **ComponentSelector** - Component selection interface
172
+ - **ComponentTree** - Visual component hierarchy display
173
+ - **ConfigBuilder** - Interactive configuration builder for site settings, metadata, routes, and visual design tokens
174
+ - **PageBuilderUI** - User interface for page building
175
+ - **SaveLoadSection** - Save and load functionality for configurations
176
+
123
177
  ### SEO & Schema
124
178
  Search engine optimization and structured data:
179
+ - **404** - Custom 404 error page component
180
+ - **GoogleAnalytics** - Google Analytics integration
181
+ - **GoogleMap** - Interactive Google Maps component
182
+ - **GoogleSearch** - Google Custom Search integration
125
183
  - **JSON-LD** - Structured data schemas (LocalBusiness, Recipe, BlogPosting, etc.)
184
+ - **Manifest** - Web app manifest generation
126
185
  - **MetaTags** - Dynamic meta tag injection
186
+ - **SchemaBlogPosting** - Blog post structured data
187
+ - **SchemaLocalBusiness** - Local business structured data
188
+ - **SchemaRecipe** - Recipe structured data
189
+ - **SchemaServices** - Services structured data
190
+ - **SchemaWebsite** - Website structured data
127
191
  - **Sitemap** - XML sitemap generation
128
192
  - **Social Cards** - Open Graph and Twitter card generation
193
+ - **BuzzwordBingo** - Interactive buzzword bingo game
194
+ - **Markdown** - Markdown rendering component
195
+ - **Recipe** - Recipe display component
196
+ - **Resume** - Resume/CV display component
197
+ - **Timeline** - Timeline visualization component
129
198
 
130
199
  ### Third-Party Integrations
131
200
  External service integrations:
132
201
  - **Calendly** - Scheduling and appointment booking
133
202
  - **Cloudinary** - Image optimization and delivery
203
+ - **GoogleReviews** - Google business reviews integration
204
+ - **Gravatar** - User avatar integration
134
205
  - **HubSpot** - CRM and marketing automation
135
- - **PayPal** - Payment processing
136
206
  - **Instagram** - Social media image integration
137
207
  - **Flickr** - Photo sharing integration
138
- - **Gravatar** - User avatar integration
139
- - **Google** - Analytics, Maps, and Search integration
208
+ - **PayPal** - Payment processing
209
+ - **ShoppingCart** - E-commerce shopping cart functionality
140
210
  - **eBay** - Store listings and shopping cart
141
211
  - **NerdJokes** - Entertainment content integration
212
+ - **Yelp** - Business reviews and ratings
142
213
 
143
214
 
215
+ ## 🎨 Visual Design Configuration
216
+
217
+ The ConfigBuilder now includes a **Visual Design** tab that allows users to configure visual design tokens such as colors, fonts, spacing, and other design system variables. These tokens are stored in the `routes.json` file under the `visualdesign` object and can be used throughout your application for consistent theming.
218
+
219
+ ### Features:
220
+ - **Color Management**: Primary, secondary, accent colors with color picker inputs
221
+ - **Typography**: Font family and base font size settings
222
+ - **Spacing & Layout**: Border radius, box shadows, transitions
223
+ - **Form-Based Editing**: User-friendly form interface powered by FormEngine
224
+ - **JSON Storage**: Design tokens stored as flattened key-value pairs in routes.json
225
+
226
+ ### Usage:
227
+ ```tsx
228
+ import { ConfigBuilder } from '@pixelated-tech/components';
229
+
230
+ function MyConfigPage() {
231
+ return <ConfigBuilder />;
232
+ }
233
+ ```
234
+
235
+ The visual design tokens can be accessed in your components via the config context or directly from the routes.json file.
236
+
144
237
 
145
238
  ## � Quick Start
146
239
 
@@ -182,7 +275,7 @@ npm run storybook
182
275
  - [ ] Buffer Integration (or Sendible, Sprout Social, Hootsuite)
183
276
  - [ ] Zapier Integration
184
277
  - [ ] Hero Banner: headline, subtext, CTA, background image/video, overlay.
185
- - [ ] Accessibility Enhancer: wrapper component that automatically improves accessibility across Pixelated sites by adding ARIA labels, roles, and states to existing components. Includes color contrast checking, keyboard navigation helpers, and alt-text suggestions for images.
278
+ - [ ] Accessibility Enhancer: wrapper component that automatically improves accessibility across Pixelated sites by adding ARIA labels, roles, and states to existing components. Includes color contrast checking, keyboard navigation helpers, alt-text suggestions for images, and automated accessibility audits.
186
279
  - [ ] SEO Dashboard with AI Integration: component that analyzes site content, suggests optimizations, integrates with AI for meta descriptions and keyword research.
187
280
 
188
281
  ### CI / CD Improvements
@@ -197,22 +290,35 @@ npm run storybook
197
290
  - [ ] find a better solution than to generate image via build script in amplify for json for sitemap creation
198
291
  - [ ] **SocialCards Component**: Fix state initialization to track prop changes properly.
199
292
  - [ ] **Modal Component**: Clarify content source pattern (accepts both `modalContent` and `children`).
200
- - [ ] **Form Components**: Fix validation state reset when input props change.
201
293
  - [ ] **Carousel Component**: Fix active card state reset when `props.cards` changes.
202
294
  - [ ] **NerdJoke Component**: Add props to useEffect dependencies if endpoint becomes configurable.
203
295
  - [ ] **GoogleReviews Component**: Add carousel/grid display modes.
204
296
  - [ ] **GoogleReviews Component**: Add API key to config provider instead of hardcoding.
205
297
  - [ ] **Instagram Component**: Add accessToken and userId to config provider for centralized API credentials.
298
+ - [ ] **Critters Integration**: Explore adding critters CSS inlining tool for improved page load performance and critical CSS optimization.
299
+
300
+ ### Platform Enhancements
301
+ - [ ] **Project Scaffolding CLI**: Interactive CLI tool that generates complete Next.js projects with pixelated-components pre-configured, including routes.json, layout.tsx, package.json, and basic page structure
302
+ - [ ] **Template Marketplace**: Pre-built industry-specific templates (restaurant, law firm, contractor, etc.) that users can clone and customize
303
+ - [ ] **Configuration Wizard**: Step-by-step setup wizard that collects business info, generates site configuration, and creates initial content structure
304
+ - [ ] **Centralized Site Manager**: Web dashboard for managing multiple pixelated sites, with bulk updates, version control, and deployment status monitoring
305
+ - [ ] **Site Health Monitoring**: Automated monitoring dashboard that checks site performance, broken links, SEO scores, and security vulnerabilities across all sites
306
+ - [ ] **Content Migration Tools**: Automated importers for WordPress, Squarespace, Wix, and other platforms to migrate content to pixelated sites
307
+ - [ ] **A/B Testing Framework**: Built-in experimentation system for testing different layouts, content, and CTAs with automatic winner selection
308
+ - [ ] **Personalization Engine**: Dynamic content delivery based on user behavior, location, and preferences
309
+ - [ ] **Hot Module Replacement for Configs**: Live preview of configuration changes without full rebuilds
310
+ - [ ] **Automated Dependency Updates**: Smart update system that tests component changes across all sites before deployment
311
+ - [ ] **Rollback System**: One-click rollback to previous versions with automatic database and asset restoration
312
+ - [ ] **Performance Budget Tracker**: Automated monitoring of Core Web Vitals with alerts when sites exceed performance budgets
313
+ - [ ] **ConfigBuilder SEO Enhancement**: Upgrade ConfigBuilder with AI-powered meta description generation, keyword optimization suggestions, and automated schema markup
314
+ - [ ] **Conversion Funnel Builder**: Visual funnel creation with automated tracking, A/B testing, and optimization recommendations
315
+ - [ ] **Automated Security Scanner**: Regular security audits with vulnerability detection and automated fixes
316
+ - [ ] **GDPR Compliance Toolkit**: Automated cookie consent, data mapping, and privacy policy generation
317
+ - [ ] **API Gateway**: Unified API management for connecting to CRM, email marketing, payment processors, and other business tools
318
+ - [ ] **Webhook Automation**: Event-driven automation for form submissions, new content, user registrations, and business workflows
319
+ - [ ] **Third-Party Sync Engine**: Bidirectional sync with tools like HubSpot, Mailchimp, QuickBooks, and project management systems
320
+ - [ ] **Documentation Auto-Generator**: Automatically generated API docs, component usage guides, and deployment instructions
206
321
 
207
- ### SSR Fixes
208
- - [ ] **cloudinary.image.tsx** (`SmartImage`): Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
209
- - [ ] **wordpress.components.tsx** (`BlogPostList`, etc.): Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
210
- - [ ] **pagebuilder/form/formcomponents.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
211
- - [ ] **cms/hubspot.components.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
212
- - [ ] **cms/gravatar.components.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
213
- - [ ] **structured/recipe.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
214
- - [ ] **structured/timeline.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
215
- - [ ] **structured/markdown.tsx**: Add `"use client"` or refactor to avoid `usePixelatedConfig` in server contexts
216
322
 
217
323
  See the [open issues](https://github.com/brianwhaley/pixelated-components/issues) for a full list of proposed features (and known issues).
218
324
 
@@ -270,16 +376,16 @@ Project Link: [https://github.com/brianwhaley/pixelated-components](https://gith
270
376
 
271
377
  ### Overview
272
378
 
273
- **Current Status**: ✅ 2,210 tests passing across 65 test files
379
+ **Current Status**: ✅ 2,246 tests passing across 67 test files
274
380
 
275
381
  | Metric | Value |
276
382
  |--------|-------|
277
- | Test Files | 65 |
278
- | Total Tests | 2,210 |
279
- | Coverage (Statements) | 77.92% |
280
- | Coverage (Lines) | 81.09% |
281
- | Coverage (Functions) | 81.27% |
282
- | Coverage (Branches) | 67.31% |
383
+ | Test Files | 67 |
384
+ | Total Tests | 2,246 |
385
+ | Coverage (Statements) | 76.11% |
386
+ | Coverage (Lines) | 78.91% |
387
+ | Coverage (Functions) | 77.95% |
388
+ | Coverage (Branches) | 66.58% |
283
389
  | Test Framework | Vitest 4.x |
284
390
  | Testing Library | @testing-library/react + jsdom |
285
391
 
@@ -297,52 +403,60 @@ npm run test:run # Single run (for CI)
297
403
  **Component Coverage Summary**
298
404
 
299
405
  #### Component Coverage (Sorted by Statement Coverage)
406
+ - **tiles.tsx**: 100% statements
407
+ - **google.reviews.functions.ts**: 100% statements
300
408
  - **config.server.tsx**: 100% statements
301
- - **modal.tsx**: 100% statements
302
409
  - **accordion.tsx**: 100% statements
303
- - **tiles.tsx**: 100% statements
304
- - **googlesearch.tsx**: 100% statements
305
- - **formvalidations.tsx**: 100% statements
306
- - **buzzwordbingo.tsx**: 100% statements
307
- - **timeline.tsx**: 100% statements
308
- - **markdown.tsx**: 100% statements
410
+ - **modal.tsx**: 100% statements
411
+ - **tab.tsx**: 100% statements
309
412
  - **ComponentPropertiesForm.tsx**: 100% statements
310
413
  - **ComponentSelector.tsx**: 100% statements
311
414
  - **ComponentTree.tsx**: 100% statements
415
+ - **formvalidations.tsx**: 100% statements
416
+ - **componentMetadata.tsx**: 100% statements
417
+ - **googlesearch.tsx**: 100% statements
312
418
  - **schema-localbusiness.tsx**: 100% statements
313
419
  - **schema-recipe.tsx**: 100% statements
314
420
  - **schema-services.tsx**: 100% statements
315
421
  - **schema-website.tsx**: 100% statements
316
- - **google.reviews.functions.ts**: 100% statements
422
+ - **buzzwordbingo.tsx**: 100% statements
423
+ - **markdown.tsx**: 100% statements
424
+ - **timeline.tsx**: 100% statements
317
425
  - **sidepanel.tsx**: 97.5% statements
318
426
  - **config.ts**: 96.55% statements
319
427
  - **google.reviews.components.tsx**: 95.83% statements
320
- - **schema-blogposting.tsx**: 95.23% statements
428
+ - **schema-blogposting.tsx**: 95.24% statements
321
429
  - **recipe.tsx**: 94.59% statements
322
430
  - **resume.tsx**: 94.38% statements
323
431
  - **contentful.delivery.ts**: 92.5% statements
324
- - **css.tsx**: 91.42% statements
325
- - **functions.ts**: 90.9% statements
326
- - **config.client.tsx**: 90% statements
432
+ - **css.tsx**: 91.43% statements
433
+ - **functions.ts**: 90.91% statements
327
434
  - **menu-expando.tsx**: 90.12% statements
435
+ - **config.client.tsx**: 90% statements
436
+ - **loading.tsx**: 85.71% statements
437
+ - **SaveLoadSection.tsx**: 84.85% statements
438
+ - **table.tsx**: 84.48% statements
439
+ - **ConfigBuilder.tsx**: 83.52% statements
328
440
  - **cloudinary.ts**: 83.33% statements
441
+ - **formcomponents.tsx**: 83.33% statements
329
442
  - **form.tsx**: 83.2% statements
330
- - **shoppingcart.functions.ts**: 81.69% statements
443
+ - **shoppingcart.functions.ts**: 81.7% statements
331
444
  - **callout.tsx**: 80% statements
332
445
  - **microinteractions.tsx**: 80% statements
333
- - **sitemap.ts**: 76.05% statements
446
+ - **cloudinary.image.tsx**: 78.57% statements
447
+ - **sitemap.ts**: 76.06% statements
334
448
  - **manifest.tsx**: 75% statements
335
- - **carousel.tsx**: 71.69% statements
449
+ - **carousel.tsx**: 71.7% statements
336
450
  - **nerdjoke.tsx**: 69.44% statements
337
451
  - **menu-accordion.tsx**: 68.13% statements
338
452
  - **semantic.tsx**: 63.51% statements
339
- - **flickr.ts**: 51.42% statements
453
+ - **componentMap.tsx**: 60% statements
454
+ - **propTypeIntrospection.tsx**: 60% statements
455
+ - **wordpress.functions.ts**: 51.43% statements
340
456
  - **PageEngine.tsx**: 48% statements
341
- - **SaveLoadSection.tsx**: 84.84% statements
342
- - **table.tsx**: 84.48% statements
343
- - **loading.tsx**: 85.71% statements
344
- - **socialcard.tsx**: 29.5% statements
345
- - **PageBuilderUI.tsx**: 26.66% statements
457
+ - **componentGeneration.tsx**: 38.89% statements
458
+ - **socialcard.tsx**: 29.51% statements
459
+ - **PageBuilderUI.tsx**: 26.67% statements
346
460
 
347
461
  ### Testing Next Steps
348
462
 
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import React from 'react';
3
4
  import PropTypes from 'prop-types';
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useEffect, useState } from 'react';
3
4
  import { usePixelatedConfig } from "../config/config.client";
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  import { useEffect, useRef, useState } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import './sidepanel.css';
7
- export default function SidePanel({ isOpen, onClose, onToggle, position = 'left', width = '300px', showOverlay = true, showTab = false, tabIcon, tabLabel, children, className = '' }) {
7
+ export default function SidePanel({ isOpen, onClose, onToggle, position = 'left', width = '300px', showOverlay = true, showTab = false, tabIcon /* = "≡" */, tabLabel, children, className = '' }) {
8
8
  const portalRootRef = useRef(null);
9
9
  const wrapperRef = useRef(null);
10
10
  const [hasMounted, setHasMounted] = useState(false);
@@ -0,0 +1,105 @@
1
+ /* Tab Component Styles */
2
+
3
+ .tab-container {
4
+ display: flex;
5
+ border: 1px solid #ddd;
6
+ border-radius: 4px;
7
+ overflow: hidden;
8
+ height: 100%;
9
+ }
10
+
11
+ .tab-top {
12
+ flex-direction: column;
13
+ }
14
+
15
+ .tab-bottom {
16
+ flex-direction: column-reverse;
17
+ }
18
+
19
+ .tab-left {
20
+ flex-direction: row;
21
+ }
22
+
23
+ .tab-right {
24
+ flex-direction: row-reverse;
25
+ }
26
+
27
+ .tab-headers {
28
+ display: flex;
29
+ background-color: #f5f5f5;
30
+ }
31
+
32
+ .tab-top .tab-headers,
33
+ .tab-bottom .tab-headers {
34
+ flex-direction: row;
35
+ width: 100%;
36
+ }
37
+
38
+ .tab-left .tab-headers,
39
+ .tab-right .tab-headers {
40
+ flex-direction: column;
41
+ height: 100%;
42
+ }
43
+
44
+ .tab-header {
45
+ padding: 10px 20px;
46
+ border: none;
47
+ background: none;
48
+ cursor: pointer;
49
+ transition: background-color 0.3s;
50
+ white-space: nowrap;
51
+ flex-shrink: 0;
52
+ }
53
+
54
+ .tab-top .tab-header,
55
+ .tab-bottom .tab-header {
56
+ border-right: 1px solid #ddd;
57
+ border-bottom: none;
58
+ }
59
+
60
+ .tab-left .tab-header,
61
+ .tab-right .tab-header {
62
+ border-right: none;
63
+ border-bottom: 1px solid #ddd;
64
+ height: auto;
65
+ padding: 20px 10px;
66
+ }
67
+
68
+ .tab-left .tab-header {
69
+ transform: rotate(-90deg);
70
+ }
71
+
72
+ .tab-right .tab-header {
73
+ transform: rotate(90deg);
74
+ }
75
+
76
+ .tab-header:last-child {
77
+ border-right: none;
78
+ }
79
+
80
+ .tab-top .tab-header:last-child,
81
+ .tab-bottom .tab-header:last-child {
82
+ border-right: none;
83
+ }
84
+
85
+ .tab-left .tab-header:last-child,
86
+ .tab-right .tab-header:last-child {
87
+ border-bottom: none;
88
+ }
89
+
90
+ .tab-header:hover {
91
+ background-color: #e0e0e0;
92
+ }
93
+
94
+ .tab-header.active {
95
+ background-color: #fff;
96
+ font-weight: bold;
97
+ }
98
+
99
+ .tab-content {
100
+ flex: 1;
101
+ padding: 20px;
102
+ background-color: #fff;
103
+ min-height: 200px;
104
+ overflow: auto;
105
+ }
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import PropTypes from 'prop-types';
4
+ import './tab.css';
5
+ const TabItemPropTypes = {
6
+ id: PropTypes.string.isRequired,
7
+ label: PropTypes.string.isRequired, //
8
+ content: PropTypes.node.isRequired,
9
+ };
10
+ // type TabItemType = InferProps<typeof TabItemPropTypes>;
11
+ Tab.propTypes = {
12
+ tabs: PropTypes.arrayOf(PropTypes.shape(TabItemPropTypes).isRequired).isRequired,
13
+ orientation: PropTypes.oneOf(['top', 'bottom', 'left', 'right']),
14
+ defaultActiveTab: PropTypes.string,
15
+ onTabChange: PropTypes.func,
16
+ };
17
+ export function Tab({ tabs, orientation = 'top', defaultActiveTab, onTabChange }) {
18
+ const [activeTab, setActiveTab] = useState(defaultActiveTab || tabs[0]?.id || '');
19
+ const handleTabClick = (tabId) => {
20
+ setActiveTab(tabId);
21
+ onTabChange?.(tabId);
22
+ };
23
+ const activeContent = tabs.find(tab => tab.id === activeTab)?.content;
24
+ const tabClass = `tab-container tab-${orientation}`;
25
+ return (_jsxs("div", { className: tabClass, children: [_jsx("div", { className: "tab-headers", children: tabs.map(tab => (_jsx("button", { className: `tab-header ${activeTab === tab.id ? 'active' : ''}`, onClick: () => handleTabClick(tab.id), children: tab.label }, tab.id))) }), _jsx("div", { className: "tab-content", children: activeContent })] }));
26
+ }
@@ -24,6 +24,15 @@
24
24
 
25
25
 
26
26
  .panelMenuButton .hamburger{
27
+ cursor: pointer;
28
+ font-family: Verdana;
29
+ font-size: 1.5rem;
30
+ color: #CCC;
31
+
32
+ }
33
+
34
+ .panelMenuButton .hamburger2{
35
+ /* 3 lines instead of unicode hamburger (U+2261) */
27
36
  /* the tricks */
28
37
  /* display: block; */
29
38
  /* display inline-block is fine too */
@@ -40,6 +49,7 @@
40
49
  padding-top: 5px;
41
50
 
42
51
  }
52
+
43
53
  /* hamburger hover style */
44
54
  /* .accordionMenu .hamburger:hover {
45
55
  color: #FFC75F;
@@ -3,6 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useRef } from 'react';
4
4
  import PropTypes from 'prop-types';
5
5
  import './menu-accordion.css';
6
+ const hamburgerIcon = "≡"; /* (U+2261) */ /* ||| */
6
7
  function generateMenuItems({ menuData, state = "hide" }) {
7
8
  const myItems = [];
8
9
  let index = 0;
@@ -174,5 +175,5 @@ export function MenuAccordionButton() {
174
175
  }
175
176
  }
176
177
  // suppressHydrationWarning suppresses hydration mismatch warnings for this button
177
- return (_jsx("button", { className: "panelMenuButton", id: "panelMenuButton", onClick: slideMobilePanel, suppressHydrationWarning: true, children: _jsx("span", { className: "hamburger text-outline", children: "|||" }) }));
178
+ return (_jsx("button", { className: "panelMenuButton", id: "panelMenuButton", onClick: slideMobilePanel, suppressHydrationWarning: true, children: _jsx("span", { className: "hamburger text-outline", children: hamburgerIcon }) }));
178
179
  }
@@ -3,13 +3,6 @@
3
3
  ============ SIMPLE MENU ============
4
4
  ======================================== */
5
5
 
6
- :root {
7
- --primary-color: #369;
8
- --secondary-color: #BCD;
9
- --accent1-color: #CCC;
10
- --accent2-color: #EEE;
11
- }
12
-
13
6
  .menu-wrapper {
14
7
  height: 100%;
15
8
  }
@@ -1,23 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import PropTypes from "prop-types";
3
- // https://gist.github.com/whitingx/3840905
4
- generateMetaTags.propTypes = {
5
- title: PropTypes.string.isRequired,
6
- description: PropTypes.string.isRequired,
7
- keywords: PropTypes.string.isRequired,
8
- site_name: PropTypes.string.isRequired,
9
- email: PropTypes.string.isRequired,
10
- origin: PropTypes.string.isRequired,
11
- url: PropTypes.string.isRequired,
12
- image: PropTypes.string.isRequired,
13
- image_height: PropTypes.string.isRequired,
14
- image_width: PropTypes.string.isRequired,
15
- favicon: PropTypes.string.isRequired,
16
- };
17
- export function generateMetaTags(props) {
18
- const { title, description, keywords, site_name, email, origin, url, image, image_height, image_width, favicon } = props;
19
- return (_jsxs(_Fragment, { children: [_jsx("title", { children: title }), _jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { httpEquiv: "content-type", content: "text/html; charset=UTF-8" }), _jsx("meta", { httpEquiv: 'Expires', content: '0' }), _jsx("meta", { httpEquiv: 'Pragma', content: 'no-cache' }), _jsx("meta", { httpEquiv: 'Cache-Control', content: 'no-cache' }), _jsx("meta", { name: "application-name", content: site_name }), _jsx("meta", { name: "author", content: site_name + ", " + email }), _jsx("meta", { name: 'copyright', content: site_name }), _jsx("meta", { name: "creator", content: site_name }), _jsx("meta", { name: "description", content: description }), _jsx("meta", { name: "keywords", content: keywords }), _jsx("meta", { name: 'language', content: 'EN' }), _jsx("meta", { name: 'owner', content: site_name }), _jsx("meta", { name: "publisher", content: site_name }), _jsx("meta", { name: 'rating', content: 'General' }), _jsx("meta", { name: 'reply-to', content: email }), _jsx("meta", { name: "robots", content: "index, follow" }), _jsx("meta", { name: 'url', content: url }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0, shrink-to-fit=no" }), _jsx("meta", { property: "og:description", content: description }), _jsx("meta", { property: 'og:email', content: email }), _jsx("meta", { property: "og:image", content: image }), _jsx("meta", { property: "og:image:height", content: image_height }), _jsx("meta", { property: "og:image:width", content: image_width }), _jsx("meta", { property: "og:locale", content: "en_US" }), _jsx("meta", { property: "og:site_name", content: site_name }), _jsx("meta", { property: "og:title", content: title }), _jsx("meta", { property: "og:type", content: "website" }), _jsx("meta", { property: "og:url", content: url }), _jsx("meta", { itemProp: "name", content: site_name }), _jsx("meta", { itemProp: "url", content: url }), _jsx("meta", { itemProp: "description", content: description }), _jsx("meta", { itemProp: "thumbnailUrl", content: image }), _jsx("meta", { property: "twitter:domain", content: new URL(origin).hostname }), _jsx("meta", { property: "twitter:url", content: url }), _jsx("meta", { name: "twitter:card", content: "summary_large_image" }), _jsx("meta", { name: "twitter:creator", content: site_name }), _jsx("meta", { name: "twitter:description", content: description }), _jsx("meta", { name: "twitter:image", content: image }), _jsx("meta", { name: "twitter:image:height", content: image_height }), _jsx("meta", { name: "twitter:image:width", content: image_width }), _jsx("meta", { name: "twitter:title", content: title }), _jsx("link", { rel: "author", href: origin }), _jsx("link", { rel: "canonical", href: url }), _jsx("link", { rel: "icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "shortcut icon", type: "image/x-icon", href: favicon }), _jsx("link", { rel: "manifest", href: "/manifest.webmanifest" }), _jsx("link", { rel: "preconnect", href: "https://images.ctfassets.net/" }), _jsx("link", { rel: "preconnect", href: "https://res.cloudinary.com/" }), _jsx("link", { rel: "preconnect", href: "https://farm2.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm6.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm8.static.flickr.com" }), _jsx("link", { rel: "preconnect", href: "https://farm66.static.flickr.com" })] }));
20
- }
21
2
  setClientMetadata.propTypes = {
22
3
  title: PropTypes.string.isRequired,
23
4
  description: PropTypes.string.isRequired,