@easy-web/content-blocks 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 (54) hide show
  1. package/README.md +332 -0
  2. package/dist/components/__tests__/UniversalMedia.test.d.ts +2 -0
  3. package/dist/components/__tests__/UniversalMedia.test.d.ts.map +1 -0
  4. package/dist/components/__tests__/UniversalMedia.test.js +46 -0
  5. package/dist/components/__tests__/UniversalMedia.test.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/schemas/__tests__/notFound.test.d.ts +2 -0
  11. package/dist/schemas/__tests__/notFound.test.d.ts.map +1 -0
  12. package/dist/schemas/__tests__/notFound.test.js +29 -0
  13. package/dist/schemas/__tests__/notFound.test.js.map +1 -0
  14. package/dist/schemas/galleries.d.ts +319 -0
  15. package/dist/schemas/galleries.d.ts.map +1 -0
  16. package/dist/schemas/galleries.js +136 -0
  17. package/dist/schemas/galleries.js.map +1 -0
  18. package/dist/schemas/notFound.d.ts +35 -0
  19. package/dist/schemas/notFound.d.ts.map +1 -0
  20. package/dist/schemas/notFound.js +30 -0
  21. package/dist/schemas/notFound.js.map +1 -0
  22. package/package.json +51 -0
  23. package/src/components/.gitkeep +0 -0
  24. package/src/components/BlogPostCard.astro +79 -0
  25. package/src/components/Card.astro +70 -0
  26. package/src/components/CardGrid.astro +29 -0
  27. package/src/components/ContactSection.astro +73 -0
  28. package/src/components/CtaSection.astro +82 -0
  29. package/src/components/DraftBanner.astro +33 -0
  30. package/src/components/Footer.astro +81 -0
  31. package/src/components/GalleryCarousel.astro +296 -0
  32. package/src/components/GalleryFeatureHighlight.astro +153 -0
  33. package/src/components/GalleryHeroSlider.astro +362 -0
  34. package/src/components/GalleryImageGrid.astro +143 -0
  35. package/src/components/GalleryLightboxGrid.astro +353 -0
  36. package/src/components/GalleryMasonryGrid.astro +127 -0
  37. package/src/components/GallerySection.astro +69 -0
  38. package/src/components/Header.astro +210 -0
  39. package/src/components/HeaderCentered.astro +231 -0
  40. package/src/components/HeaderFlyout.astro +373 -0
  41. package/src/components/HeaderHideOnScroll.astro +237 -0
  42. package/src/components/Hero.astro +78 -0
  43. package/src/components/LanguageNotice.astro +32 -0
  44. package/src/components/LanguageSwitch.astro +67 -0
  45. package/src/components/LegalLayout.astro +53 -0
  46. package/src/components/NotFound.astro +124 -0
  47. package/src/components/Prose.astro +156 -0
  48. package/src/components/Section.astro +37 -0
  49. package/src/components/ThemeToggle.astro +82 -0
  50. package/src/components/UniversalMedia.astro +102 -0
  51. package/src/components/__tests__/UniversalMedia.test.ts +65 -0
  52. package/src/schemas/__tests__/notFound.test.ts +32 -0
  53. package/src/schemas/galleries.ts +152 -0
  54. package/src/schemas/notFound.ts +33 -0
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Gallery content collection schema — discriminated union over supported variants.
3
+ *
4
+ * Each entry under a consuming site's `src/content/galleries/` selects a `kind`
5
+ * that determines which Astro component renders it. Pages decide which entry
6
+ * to load and where to place it; the entry decides what to show and how it is
7
+ * laid out.
8
+ *
9
+ * Supported kinds: `image-grid`, `hero-slider`, `carousel`, `masonry-grid`,
10
+ * `feature-highlight`, `lightbox-grid`. Adding a new kind requires updating
11
+ * this union AND adding the matching component in `../components/`. Authoring
12
+ * an unsupported `kind` fails Zod validation at build time.
13
+ *
14
+ * Usage:
15
+ *
16
+ * ```ts
17
+ * import { defineCollection } from 'astro:content';
18
+ * import { glob } from 'astro/loaders';
19
+ * import { gallerySchema } from '@easy-web/content-blocks/schemas/galleries';
20
+ *
21
+ * const galleries = defineCollection({
22
+ * loader: glob({ pattern: '**\/*.{yml,yaml}', base: './src/content/galleries' }),
23
+ * schema: gallerySchema,
24
+ * });
25
+ * ```
26
+ */
27
+ import { z } from 'zod';
28
+ declare const galleryImageGridSchema: z.ZodObject<{
29
+ kind: z.ZodLiteral<"image-grid">;
30
+ title: z.ZodOptional<z.ZodString>;
31
+ locale: z.ZodEnum<{
32
+ de: "de";
33
+ en: "en";
34
+ }>;
35
+ translationKey: z.ZodString;
36
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>, z.ZodLiteral<6>]>>;
37
+ gap: z.ZodDefault<z.ZodEnum<{
38
+ sm: "sm";
39
+ md: "md";
40
+ lg: "lg";
41
+ }>>;
42
+ aspectRatio: z.ZodDefault<z.ZodEnum<{
43
+ square: "square";
44
+ "4/3": "4/3";
45
+ "16/9": "16/9";
46
+ auto: "auto";
47
+ }>>;
48
+ items: z.ZodArray<z.ZodObject<{
49
+ src: z.ZodString;
50
+ alt: z.ZodString;
51
+ caption: z.ZodOptional<z.ZodString>;
52
+ href: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strip>>;
54
+ }, z.core.$strip>;
55
+ declare const galleryHeroSliderSchema: z.ZodObject<{
56
+ kind: z.ZodLiteral<"hero-slider">;
57
+ title: z.ZodOptional<z.ZodString>;
58
+ locale: z.ZodEnum<{
59
+ de: "de";
60
+ en: "en";
61
+ }>;
62
+ translationKey: z.ZodString;
63
+ autoplay: z.ZodDefault<z.ZodBoolean>;
64
+ interval: z.ZodDefault<z.ZodNumber>;
65
+ height: z.ZodDefault<z.ZodEnum<{
66
+ sm: "sm";
67
+ md: "md";
68
+ lg: "lg";
69
+ full: "full";
70
+ }>>;
71
+ slides: z.ZodArray<z.ZodObject<{
72
+ src: z.ZodString;
73
+ alt: z.ZodString;
74
+ title: z.ZodString;
75
+ subtitle: z.ZodOptional<z.ZodString>;
76
+ href: z.ZodOptional<z.ZodString>;
77
+ cta: z.ZodOptional<z.ZodString>;
78
+ }, z.core.$strip>>;
79
+ }, z.core.$strip>;
80
+ declare const galleryCarouselSchema: z.ZodObject<{
81
+ kind: z.ZodLiteral<"carousel">;
82
+ title: z.ZodOptional<z.ZodString>;
83
+ locale: z.ZodEnum<{
84
+ de: "de";
85
+ en: "en";
86
+ }>;
87
+ translationKey: z.ZodString;
88
+ autoplay: z.ZodDefault<z.ZodBoolean>;
89
+ interval: z.ZodDefault<z.ZodNumber>;
90
+ showDots: z.ZodDefault<z.ZodBoolean>;
91
+ showArrows: z.ZodDefault<z.ZodBoolean>;
92
+ slides: z.ZodArray<z.ZodObject<{
93
+ src: z.ZodString;
94
+ alt: z.ZodString;
95
+ caption: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strip>>;
97
+ }, z.core.$strip>;
98
+ declare const galleryMasonryGridSchema: z.ZodObject<{
99
+ kind: z.ZodLiteral<"masonry-grid">;
100
+ title: z.ZodOptional<z.ZodString>;
101
+ locale: z.ZodEnum<{
102
+ de: "de";
103
+ en: "en";
104
+ }>;
105
+ translationKey: z.ZodString;
106
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
107
+ gap: z.ZodDefault<z.ZodEnum<{
108
+ sm: "sm";
109
+ md: "md";
110
+ lg: "lg";
111
+ }>>;
112
+ items: z.ZodArray<z.ZodObject<{
113
+ src: z.ZodString;
114
+ alt: z.ZodString;
115
+ caption: z.ZodOptional<z.ZodString>;
116
+ href: z.ZodOptional<z.ZodString>;
117
+ }, z.core.$strip>>;
118
+ }, z.core.$strip>;
119
+ declare const galleryFeatureHighlightSchema: z.ZodObject<{
120
+ kind: z.ZodLiteral<"feature-highlight">;
121
+ title: z.ZodOptional<z.ZodString>;
122
+ locale: z.ZodEnum<{
123
+ de: "de";
124
+ en: "en";
125
+ }>;
126
+ translationKey: z.ZodString;
127
+ gap: z.ZodDefault<z.ZodEnum<{
128
+ sm: "sm";
129
+ md: "md";
130
+ lg: "lg";
131
+ }>>;
132
+ items: z.ZodArray<z.ZodObject<{
133
+ src: z.ZodString;
134
+ alt: z.ZodString;
135
+ title: z.ZodString;
136
+ description: z.ZodString;
137
+ href: z.ZodOptional<z.ZodString>;
138
+ cta: z.ZodOptional<z.ZodString>;
139
+ imagePosition: z.ZodOptional<z.ZodEnum<{
140
+ left: "left";
141
+ right: "right";
142
+ }>>;
143
+ }, z.core.$strip>>;
144
+ }, z.core.$strip>;
145
+ declare const galleryLightboxGridSchema: z.ZodObject<{
146
+ kind: z.ZodLiteral<"lightbox-grid">;
147
+ title: z.ZodOptional<z.ZodString>;
148
+ locale: z.ZodEnum<{
149
+ de: "de";
150
+ en: "en";
151
+ }>;
152
+ translationKey: z.ZodString;
153
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
154
+ gap: z.ZodDefault<z.ZodEnum<{
155
+ sm: "sm";
156
+ md: "md";
157
+ lg: "lg";
158
+ }>>;
159
+ aspectRatio: z.ZodDefault<z.ZodEnum<{
160
+ square: "square";
161
+ "4/3": "4/3";
162
+ auto: "auto";
163
+ }>>;
164
+ items: z.ZodArray<z.ZodObject<{
165
+ src: z.ZodString;
166
+ alt: z.ZodString;
167
+ title: z.ZodOptional<z.ZodString>;
168
+ description: z.ZodOptional<z.ZodString>;
169
+ }, z.core.$strip>>;
170
+ }, z.core.$strip>;
171
+ export declare const gallerySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
172
+ kind: z.ZodLiteral<"image-grid">;
173
+ title: z.ZodOptional<z.ZodString>;
174
+ locale: z.ZodEnum<{
175
+ de: "de";
176
+ en: "en";
177
+ }>;
178
+ translationKey: z.ZodString;
179
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>, z.ZodLiteral<6>]>>;
180
+ gap: z.ZodDefault<z.ZodEnum<{
181
+ sm: "sm";
182
+ md: "md";
183
+ lg: "lg";
184
+ }>>;
185
+ aspectRatio: z.ZodDefault<z.ZodEnum<{
186
+ square: "square";
187
+ "4/3": "4/3";
188
+ "16/9": "16/9";
189
+ auto: "auto";
190
+ }>>;
191
+ items: z.ZodArray<z.ZodObject<{
192
+ src: z.ZodString;
193
+ alt: z.ZodString;
194
+ caption: z.ZodOptional<z.ZodString>;
195
+ href: z.ZodOptional<z.ZodString>;
196
+ }, z.core.$strip>>;
197
+ }, z.core.$strip>, z.ZodObject<{
198
+ kind: z.ZodLiteral<"hero-slider">;
199
+ title: z.ZodOptional<z.ZodString>;
200
+ locale: z.ZodEnum<{
201
+ de: "de";
202
+ en: "en";
203
+ }>;
204
+ translationKey: z.ZodString;
205
+ autoplay: z.ZodDefault<z.ZodBoolean>;
206
+ interval: z.ZodDefault<z.ZodNumber>;
207
+ height: z.ZodDefault<z.ZodEnum<{
208
+ sm: "sm";
209
+ md: "md";
210
+ lg: "lg";
211
+ full: "full";
212
+ }>>;
213
+ slides: z.ZodArray<z.ZodObject<{
214
+ src: z.ZodString;
215
+ alt: z.ZodString;
216
+ title: z.ZodString;
217
+ subtitle: z.ZodOptional<z.ZodString>;
218
+ href: z.ZodOptional<z.ZodString>;
219
+ cta: z.ZodOptional<z.ZodString>;
220
+ }, z.core.$strip>>;
221
+ }, z.core.$strip>, z.ZodObject<{
222
+ kind: z.ZodLiteral<"carousel">;
223
+ title: z.ZodOptional<z.ZodString>;
224
+ locale: z.ZodEnum<{
225
+ de: "de";
226
+ en: "en";
227
+ }>;
228
+ translationKey: z.ZodString;
229
+ autoplay: z.ZodDefault<z.ZodBoolean>;
230
+ interval: z.ZodDefault<z.ZodNumber>;
231
+ showDots: z.ZodDefault<z.ZodBoolean>;
232
+ showArrows: z.ZodDefault<z.ZodBoolean>;
233
+ slides: z.ZodArray<z.ZodObject<{
234
+ src: z.ZodString;
235
+ alt: z.ZodString;
236
+ caption: z.ZodOptional<z.ZodString>;
237
+ }, z.core.$strip>>;
238
+ }, z.core.$strip>, z.ZodObject<{
239
+ kind: z.ZodLiteral<"masonry-grid">;
240
+ title: z.ZodOptional<z.ZodString>;
241
+ locale: z.ZodEnum<{
242
+ de: "de";
243
+ en: "en";
244
+ }>;
245
+ translationKey: z.ZodString;
246
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
247
+ gap: z.ZodDefault<z.ZodEnum<{
248
+ sm: "sm";
249
+ md: "md";
250
+ lg: "lg";
251
+ }>>;
252
+ items: z.ZodArray<z.ZodObject<{
253
+ src: z.ZodString;
254
+ alt: z.ZodString;
255
+ caption: z.ZodOptional<z.ZodString>;
256
+ href: z.ZodOptional<z.ZodString>;
257
+ }, z.core.$strip>>;
258
+ }, z.core.$strip>, z.ZodObject<{
259
+ kind: z.ZodLiteral<"feature-highlight">;
260
+ title: z.ZodOptional<z.ZodString>;
261
+ locale: z.ZodEnum<{
262
+ de: "de";
263
+ en: "en";
264
+ }>;
265
+ translationKey: z.ZodString;
266
+ gap: z.ZodDefault<z.ZodEnum<{
267
+ sm: "sm";
268
+ md: "md";
269
+ lg: "lg";
270
+ }>>;
271
+ items: z.ZodArray<z.ZodObject<{
272
+ src: z.ZodString;
273
+ alt: z.ZodString;
274
+ title: z.ZodString;
275
+ description: z.ZodString;
276
+ href: z.ZodOptional<z.ZodString>;
277
+ cta: z.ZodOptional<z.ZodString>;
278
+ imagePosition: z.ZodOptional<z.ZodEnum<{
279
+ left: "left";
280
+ right: "right";
281
+ }>>;
282
+ }, z.core.$strip>>;
283
+ }, z.core.$strip>, z.ZodObject<{
284
+ kind: z.ZodLiteral<"lightbox-grid">;
285
+ title: z.ZodOptional<z.ZodString>;
286
+ locale: z.ZodEnum<{
287
+ de: "de";
288
+ en: "en";
289
+ }>;
290
+ translationKey: z.ZodString;
291
+ columns: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<2>, z.ZodLiteral<3>, z.ZodLiteral<4>]>>;
292
+ gap: z.ZodDefault<z.ZodEnum<{
293
+ sm: "sm";
294
+ md: "md";
295
+ lg: "lg";
296
+ }>>;
297
+ aspectRatio: z.ZodDefault<z.ZodEnum<{
298
+ square: "square";
299
+ "4/3": "4/3";
300
+ auto: "auto";
301
+ }>>;
302
+ items: z.ZodArray<z.ZodObject<{
303
+ src: z.ZodString;
304
+ alt: z.ZodString;
305
+ title: z.ZodOptional<z.ZodString>;
306
+ description: z.ZodOptional<z.ZodString>;
307
+ }, z.core.$strip>>;
308
+ }, z.core.$strip>], "kind">;
309
+ export type GalleryEntry = z.infer<typeof gallerySchema>;
310
+ export type GalleryImageGridData = z.infer<typeof galleryImageGridSchema>;
311
+ export type GalleryHeroSliderData = z.infer<typeof galleryHeroSliderSchema>;
312
+ export type GalleryCarouselData = z.infer<typeof galleryCarouselSchema>;
313
+ export type GalleryMasonryGridData = z.infer<typeof galleryMasonryGridSchema>;
314
+ export type GalleryFeatureHighlightData = z.infer<typeof galleryFeatureHighlightSchema>;
315
+ export type GalleryLightboxGridData = z.infer<typeof galleryLightboxGridSchema>;
316
+ export declare const GALLERY_KINDS: readonly ["image-grid", "hero-slider", "carousel", "masonry-grid", "feature-highlight", "lightbox-grid"];
317
+ export type GalleryKind = typeof GALLERY_KINDS[number];
318
+ export {};
319
+ //# sourceMappingURL=galleries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"galleries.d.ts","sourceRoot":"","sources":["../../src/schemas/galleries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAc1B,CAAC;AAEH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;iBAgB3B,CAAC;AAEH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;iBAczB,CAAC;AAEH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,QAAA,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;iBAejC,CAAC;AAEH,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;iBAc7B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAOxB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AACzD,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACxE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC9E,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACxF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEhF,eAAO,MAAM,aAAa,0GAOhB,CAAC;AACX,MAAM,MAAM,WAAW,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Gallery content collection schema — discriminated union over supported variants.
3
+ *
4
+ * Each entry under a consuming site's `src/content/galleries/` selects a `kind`
5
+ * that determines which Astro component renders it. Pages decide which entry
6
+ * to load and where to place it; the entry decides what to show and how it is
7
+ * laid out.
8
+ *
9
+ * Supported kinds: `image-grid`, `hero-slider`, `carousel`, `masonry-grid`,
10
+ * `feature-highlight`, `lightbox-grid`. Adding a new kind requires updating
11
+ * this union AND adding the matching component in `../components/`. Authoring
12
+ * an unsupported `kind` fails Zod validation at build time.
13
+ *
14
+ * Usage:
15
+ *
16
+ * ```ts
17
+ * import { defineCollection } from 'astro:content';
18
+ * import { glob } from 'astro/loaders';
19
+ * import { gallerySchema } from '@easy-web/content-blocks/schemas/galleries';
20
+ *
21
+ * const galleries = defineCollection({
22
+ * loader: glob({ pattern: '**\/*.{yml,yaml}', base: './src/content/galleries' }),
23
+ * schema: gallerySchema,
24
+ * });
25
+ * ```
26
+ */
27
+ import { z } from 'zod';
28
+ const galleryImageGridSchema = z.object({
29
+ kind: z.literal('image-grid'),
30
+ title: z.string().optional(),
31
+ locale: z.enum(['de', 'en']),
32
+ translationKey: z.string(),
33
+ columns: z.union([z.literal(2), z.literal(3), z.literal(4), z.literal(6)]).default(3),
34
+ gap: z.enum(['sm', 'md', 'lg']).default('md'),
35
+ aspectRatio: z.enum(['square', '4/3', '16/9', 'auto']).default('square'),
36
+ items: z.array(z.object({
37
+ src: z.string().min(1),
38
+ alt: z.string(),
39
+ caption: z.string().optional(),
40
+ href: z.string().optional(),
41
+ })).min(1),
42
+ });
43
+ const galleryHeroSliderSchema = z.object({
44
+ kind: z.literal('hero-slider'),
45
+ title: z.string().optional(),
46
+ locale: z.enum(['de', 'en']),
47
+ translationKey: z.string(),
48
+ autoplay: z.boolean().default(true),
49
+ interval: z.number().int().positive().default(5000),
50
+ height: z.enum(['sm', 'md', 'lg', 'full']).default('lg'),
51
+ slides: z.array(z.object({
52
+ src: z.string().min(1),
53
+ alt: z.string(),
54
+ title: z.string(),
55
+ subtitle: z.string().optional(),
56
+ href: z.string().optional(),
57
+ cta: z.string().optional(),
58
+ })).min(1),
59
+ });
60
+ const galleryCarouselSchema = z.object({
61
+ kind: z.literal('carousel'),
62
+ title: z.string().optional(),
63
+ locale: z.enum(['de', 'en']),
64
+ translationKey: z.string(),
65
+ autoplay: z.boolean().default(false),
66
+ interval: z.number().int().positive().default(4000),
67
+ showDots: z.boolean().default(true),
68
+ showArrows: z.boolean().default(true),
69
+ slides: z.array(z.object({
70
+ src: z.string().min(1),
71
+ alt: z.string(),
72
+ caption: z.string().optional(),
73
+ })).min(1),
74
+ });
75
+ const galleryMasonryGridSchema = z.object({
76
+ kind: z.literal('masonry-grid'),
77
+ title: z.string().optional(),
78
+ locale: z.enum(['de', 'en']),
79
+ translationKey: z.string(),
80
+ columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).default(3),
81
+ gap: z.enum(['sm', 'md', 'lg']).default('md'),
82
+ items: z.array(z.object({
83
+ src: z.string().min(1),
84
+ alt: z.string(),
85
+ caption: z.string().optional(),
86
+ href: z.string().optional(),
87
+ })).min(1),
88
+ });
89
+ const galleryFeatureHighlightSchema = z.object({
90
+ kind: z.literal('feature-highlight'),
91
+ title: z.string().optional(),
92
+ locale: z.enum(['de', 'en']),
93
+ translationKey: z.string(),
94
+ gap: z.enum(['sm', 'md', 'lg']).default('lg'),
95
+ items: z.array(z.object({
96
+ src: z.string().min(1),
97
+ alt: z.string(),
98
+ title: z.string(),
99
+ description: z.string(),
100
+ href: z.string().optional(),
101
+ cta: z.string().optional(),
102
+ imagePosition: z.enum(['left', 'right']).optional(),
103
+ })).min(1),
104
+ });
105
+ const galleryLightboxGridSchema = z.object({
106
+ kind: z.literal('lightbox-grid'),
107
+ title: z.string().optional(),
108
+ locale: z.enum(['de', 'en']),
109
+ translationKey: z.string(),
110
+ columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).default(3),
111
+ gap: z.enum(['sm', 'md', 'lg']).default('md'),
112
+ aspectRatio: z.enum(['square', '4/3', 'auto']).default('square'),
113
+ items: z.array(z.object({
114
+ src: z.string().min(1),
115
+ alt: z.string(),
116
+ title: z.string().optional(),
117
+ description: z.string().optional(),
118
+ })).min(1),
119
+ });
120
+ export const gallerySchema = z.discriminatedUnion('kind', [
121
+ galleryImageGridSchema,
122
+ galleryHeroSliderSchema,
123
+ galleryCarouselSchema,
124
+ galleryMasonryGridSchema,
125
+ galleryFeatureHighlightSchema,
126
+ galleryLightboxGridSchema,
127
+ ]);
128
+ export const GALLERY_KINDS = [
129
+ 'image-grid',
130
+ 'hero-slider',
131
+ 'carousel',
132
+ 'masonry-grid',
133
+ 'feature-highlight',
134
+ 'lightbox-grid',
135
+ ];
136
+ //# sourceMappingURL=galleries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"galleries.js","sourceRoot":"","sources":["../../src/schemas/galleries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Not-found (404) singleton schema — locale-keyed CMS-editable content.
3
+ *
4
+ * Each locale key maps to an optional set of fields rendered by the site's
5
+ * 404 page. All fields are optional so partial translations are valid; a
6
+ * consuming page falls back to defaults when a field is absent.
7
+ *
8
+ * The record key is an open `z.string()` so any IETF locale tag (e.g. `de`,
9
+ * `en`, `fr`, `sq`, `de-AT`) is accepted without schema changes. Do NOT add
10
+ * SEO metadata (title, canonical) here — those belong to the page layer.
11
+ *
12
+ * Usage:
13
+ *
14
+ * ```ts
15
+ * import { notFoundSchema } from '@easy-web/content-blocks/schemas/notFound';
16
+ *
17
+ * const data = notFoundSchema.parse(await import('../content/not-found.json'));
18
+ * ```
19
+ *
20
+ * @see docs/decisions/0011-content-architecture-strategy.md
21
+ * @see docs/decisions/0013-shared-not-found-primitives.md
22
+ */
23
+ import { z } from 'zod';
24
+ export declare const localizedNotFoundSchema: z.ZodObject<{
25
+ image: z.ZodOptional<z.ZodString>;
26
+ heading: z.ZodOptional<z.ZodString>;
27
+ message: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strip>;
29
+ export declare const notFoundSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
30
+ image: z.ZodOptional<z.ZodString>;
31
+ heading: z.ZodOptional<z.ZodString>;
32
+ message: z.ZodOptional<z.ZodString>;
33
+ }, z.core.$strip>>;
34
+ export type NotFoundData = z.infer<typeof notFoundSchema>;
35
+ //# sourceMappingURL=notFound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notFound.d.ts","sourceRoot":"","sources":["../../src/schemas/notFound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,uBAAuB;;;;iBAIlC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;kBAAgD,CAAC;AAE5E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Not-found (404) singleton schema — locale-keyed CMS-editable content.
3
+ *
4
+ * Each locale key maps to an optional set of fields rendered by the site's
5
+ * 404 page. All fields are optional so partial translations are valid; a
6
+ * consuming page falls back to defaults when a field is absent.
7
+ *
8
+ * The record key is an open `z.string()` so any IETF locale tag (e.g. `de`,
9
+ * `en`, `fr`, `sq`, `de-AT`) is accepted without schema changes. Do NOT add
10
+ * SEO metadata (title, canonical) here — those belong to the page layer.
11
+ *
12
+ * Usage:
13
+ *
14
+ * ```ts
15
+ * import { notFoundSchema } from '@easy-web/content-blocks/schemas/notFound';
16
+ *
17
+ * const data = notFoundSchema.parse(await import('../content/not-found.json'));
18
+ * ```
19
+ *
20
+ * @see docs/decisions/0011-content-architecture-strategy.md
21
+ * @see docs/decisions/0013-shared-not-found-primitives.md
22
+ */
23
+ import { z } from 'zod';
24
+ export const localizedNotFoundSchema = z.object({
25
+ image: z.string().optional(),
26
+ heading: z.string().optional(),
27
+ message: z.string().optional(),
28
+ });
29
+ export const notFoundSchema = z.record(z.string(), localizedNotFoundSchema);
30
+ //# sourceMappingURL=notFound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notFound.js","sourceRoot":"","sources":["../../src/schemas/notFound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@easy-web/content-blocks",
3
+ "version": "1.0.0",
4
+ "packageManager": "pnpm@10.34.4",
5
+ "type": "module",
6
+ "description": "Reusable Astro content-block components for the easy-web ecosystem.",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./components/*": "./src/components/*.astro",
13
+ "./schemas/*": "./src/schemas/*.ts"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src/components",
18
+ "src/schemas",
19
+ "README.md"
20
+ ],
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "vitest run"
26
+ },
27
+ "peerDependencies": {
28
+ "@easy-web/i18n": ">=1.0.0",
29
+ "@easy-web/theme-core": ">=1.0.0",
30
+ "astro": ">=6.0.0 <8.0.0",
31
+ "zod": ">=4.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@easy-web/i18n": "workspace:*",
35
+ "@easy-web/theme-core": "workspace:*",
36
+ "astro": "^6.4.0",
37
+ "typescript": "^5.5.0",
38
+ "vitest": "^4.1.10",
39
+ "zod": "^4.0.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "provenance": true
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/achimismaili/easy-web.git",
48
+ "directory": "packages/easy-web-content-blocks"
49
+ },
50
+ "license": "MIT"
51
+ }
File without changes
@@ -0,0 +1,79 @@
1
+ ---
2
+ /**
3
+ * BlogPostCard – clickable card linking to a blog post.
4
+ * Displays optional hero image, title, locale-formatted date,
5
+ * and description excerpt.
6
+ * Pure Astro component using --ew-* design tokens.
7
+ */
8
+ interface Props {
9
+ title: string;
10
+ description: string;
11
+ href: string;
12
+ pubDate: Date;
13
+ heroImage?: string;
14
+ heroImageAlt?: string;
15
+ locale?: string;
16
+ }
17
+
18
+ const { title, description, href, pubDate, heroImage, heroImageAlt, locale } = Astro.props;
19
+ const alt = heroImageAlt || title;
20
+ const formattedDate = new Intl.DateTimeFormat(locale ?? undefined, { dateStyle: 'medium' }).format(pubDate);
21
+ ---
22
+
23
+ <a class="ew-blog-card" href={href}>
24
+ {heroImage && (
25
+ <img src={heroImage} alt={alt} class="ew-blog-card__image" loading="lazy" />
26
+ )}
27
+ <div class="ew-blog-card__body">
28
+ <h3 class="ew-blog-card__title">{title}</h3>
29
+ <time class="ew-blog-card__date" datetime={pubDate.toISOString()}>{formattedDate}</time>
30
+ <p class="ew-blog-card__description">{description}</p>
31
+ </div>
32
+ </a>
33
+
34
+ <style>
35
+ .ew-blog-card {
36
+ display: flex;
37
+ flex-direction: column;
38
+ background: var(--ew-surface);
39
+ border: 1px solid var(--ew-border);
40
+ border-radius: var(--ew-radius-lg);
41
+ overflow: hidden;
42
+ text-decoration: none;
43
+ color: inherit;
44
+ }
45
+ .ew-blog-card:hover {
46
+ border-color: var(--ew-primary);
47
+ }
48
+ .ew-blog-card__image {
49
+ width: 100%;
50
+ aspect-ratio: 16 / 9;
51
+ object-fit: cover;
52
+ display: block;
53
+ }
54
+ .ew-blog-card__body {
55
+ padding: var(--ew-space-4);
56
+ }
57
+ .ew-blog-card__title {
58
+ font-family: var(--ew-font-sans);
59
+ font-size: var(--ew-text-lg);
60
+ font-weight: 600;
61
+ color: var(--ew-on-surface);
62
+ margin: 0;
63
+ line-height: var(--ew-leading-tight);
64
+ }
65
+ .ew-blog-card__date {
66
+ display: block;
67
+ font-family: var(--ew-font-sans);
68
+ font-size: var(--ew-text-xs);
69
+ color: var(--ew-muted);
70
+ margin-top: var(--ew-space-1);
71
+ }
72
+ .ew-blog-card__description {
73
+ font-family: var(--ew-font-sans);
74
+ font-size: var(--ew-text-sm);
75
+ color: var(--ew-muted);
76
+ margin: var(--ew-space-2) 0 0;
77
+ line-height: var(--ew-leading-normal);
78
+ }
79
+ </style>