@styleflow.app/flowpress-astro-core 1.0.0-beta.5 → 1.0.0-beta.6

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 (53) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +41 -3
  3. package/dist/astro/BaseLayout.astro +5 -1
  4. package/dist/astro/CompositionRenderer.astro +126 -0
  5. package/dist/astro/DefaultTemplate.astro +6 -1
  6. package/dist/astro/GutenbergRenderer.astro +62 -51
  7. package/dist/astro/ThemeRuntime.astro +24 -0
  8. package/dist/astro/blocks/CarouselBlock.astro +4 -6
  9. package/dist/astro/blocks/CarouselItemBlock.astro +2 -2
  10. package/dist/astro/blocks/ColumnBlock.astro +2 -3
  11. package/dist/astro/blocks/FormBlock.astro +5 -5
  12. package/dist/astro/blocks/GridBlock.astro +2 -2
  13. package/dist/astro/blocks/HeroBlock.astro +2 -3
  14. package/dist/astro/blocks/ImageBlock.astro +2 -3
  15. package/dist/astro/blocks/ListItemFallback.astro +4 -4
  16. package/dist/astro/blocks/NumberSpinnerBlock.astro +24 -18
  17. package/dist/astro/blocks/SemanticElement.astro +35 -0
  18. package/dist/astro/blocks/SiteLogoBlock.astro +53 -0
  19. package/dist/astro/blocks/TextBlock.astro +27 -23
  20. package/dist/astro/blocks/ThemeToggleBlock.astro +23 -0
  21. package/dist/astro/blocks/helpers.d.ts +7 -0
  22. package/dist/astro/blocks/helpers.d.ts.map +1 -1
  23. package/dist/astro/blocks/helpers.js +49 -36
  24. package/dist/astro/blocks/helpers.js.map +1 -1
  25. package/dist/astro/blocks/helpers.ts +61 -40
  26. package/dist/block-renderers.d.ts +18 -2
  27. package/dist/block-renderers.d.ts.map +1 -1
  28. package/dist/block-renderers.js.map +1 -1
  29. package/dist/client.d.ts +2 -1
  30. package/dist/client.d.ts.map +1 -1
  31. package/dist/client.js +31 -0
  32. package/dist/client.js.map +1 -1
  33. package/dist/composition.d.ts +14 -0
  34. package/dist/composition.d.ts.map +1 -0
  35. package/dist/composition.js +86 -0
  36. package/dist/composition.js.map +1 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +2 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/styleflow.d.ts +2 -0
  42. package/dist/styleflow.d.ts.map +1 -1
  43. package/dist/styleflow.js +35 -1
  44. package/dist/styleflow.js.map +1 -1
  45. package/dist/theme-runtime.d.ts +16 -0
  46. package/dist/theme-runtime.d.ts.map +1 -0
  47. package/dist/theme-runtime.js +77 -0
  48. package/dist/theme-runtime.js.map +1 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/dist/types.d.ts +73 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js.map +1 -1
  53. package/package.json +7 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 1.0.0-beta.6
6
+
7
+ - Added required `htmlProps`, `styleflowProps`, composition and deterministic
8
+ render context to every certified block adapter.
9
+ - Added single-emission composition stylesheets for validated WordPress block
10
+ CSS, plus theme-aware positive/negative Site Logo rendering.
11
+ - Added the no-hydration multi-theme runtime and `sf/theme-toggle` adapter.
12
+ - Made `<S.*>` the only public Astro authoring syntax in package sources.
13
+ - Added `FlowpressCompositionV1`, typed `resolvedData`,
14
+ `FlowpressClient.getComposition()` and recursive production fail-closed
15
+ rendering with exact compatibility commands in development.
16
+
5
17
  ## 1.0.0-beta.5
6
18
 
7
19
  - Updated the exact `@styleflow.app/astro` dependency to `1.0.0-beta.4`,
@@ -44,7 +56,6 @@
44
56
  - Updated the exact `@styleflow.app/astro` dependency to `1.0.0-beta.1`,
45
57
  keeping consumer trees on one runtime while adopting deterministic CLI
46
58
  reports. Runtime behavior is unchanged.
47
-
48
59
  ## 1.0.0-beta.0
49
60
 
50
61
  - Adopted the grouped Styleflow vNext project contract and the exact Astro v1
package/README.md CHANGED
@@ -9,8 +9,8 @@ The package must not import from an application or project plugin.
9
9
  ## Install
10
10
 
11
11
  ```bash
12
- pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.5 \
13
- @styleflow.app/astro@1.0.0-beta.4
12
+ pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.6 \
13
+ @styleflow.app/astro@1.0.0-beta.5
14
14
  ```
15
15
 
16
16
  Prereleases are pinned exactly. The package expects Astro 7 or newer and the
@@ -79,6 +79,44 @@ grid and dead controls are hidden. If a legacy carousel contains any direct
79
79
  child other than `sf/carousel-item`, no interactive instance is initialized:
80
80
  the complete content renders statically, with a development diagnostic.
81
81
 
82
+ ## Site Editor composition
83
+
84
+ `FlowpressClient.getComposition(uri, locale)` reads the public
85
+ `FlowpressCompositionV1` contract. `CompositionRenderer.astro` walks its block
86
+ tree through an explicit `FlowpressBlockAdapters` registry, passes authored
87
+ attributes and `resolvedData` separately, and never renders `innerHtml` as a
88
+ fallback. Blocking WordPress diagnostics always fail the render. Missing or
89
+ draft adapters, absent block-type descriptors and schema-fingerprint drift
90
+ are visible diagnostics in development and fail closed in production with
91
+ the exact scaffold, refresh or approval command. The fingerprint comparison
92
+ is repeated by Astro and does not rely only on the CMS gate.
93
+ Before invoking an adapter, the renderer resolves the universal Styleflow
94
+ attributes through the same framework-neutral validator used by the built-in
95
+ renderer and passes them as `styleflowProps`. Project adapters can therefore
96
+ apply the authored semantic context to their real root without accepting raw
97
+ WordPress visual values.
98
+
99
+ Every adapter also receives required normalized `htmlProps` (`anchor` → `id`
100
+ and the additional classes), the full composition and a deterministic render
101
+ context. WordPress-processed block custom CSS is emitted once at the root.
102
+ `ThemeRuntime.astro` applies the persisted contract theme before paint and
103
+ progressively enhances `sf/theme-toggle` without hydrating React. The shared
104
+ Site Logo adapter renders positive and negative variants while inherited CSS
105
+ variables make the nearest nested theme authoritative.
106
+
107
+ ```astro
108
+ ---
109
+ import CompositionRenderer from "@styleflow.app/flowpress-astro-core/CompositionRenderer.astro";
110
+ import { blockAdapters } from "./generated/block-adapter-registry";
111
+ ---
112
+
113
+ <CompositionRenderer composition={composition} adapters={blockAdapters} />
114
+ ```
115
+
116
+ Static adapters produce no JavaScript by default. A project opts into client
117
+ runtime only inside adapters classified as interactive; React views rendered
118
+ without `client:*` remain server-only HTML.
119
+
82
120
  Heading, rich-text, quote and matching core blocks accept Gutenberg alignment
83
121
  from either `style.typography.textAlign` or the FlowPress `textAlign` field.
84
122
  Only `left`, `center`, `right`, `justify`, `start` and `end` reach CSS.
@@ -167,7 +205,7 @@ The TypeScript template-hierarchy suite is self-contained. Its additional PHP
167
205
  parity test runs when the sibling `flowpress-wordpress-core` checkout is
168
206
  available locally and is skipped in a standalone package checkout.
169
207
 
170
- Publish v1 betas on `next` only after Components and WordPress Core are
208
+ Publish `1.0.0-beta.6` on `next` only after Components and WordPress Core are
171
209
  available. Inspect `npm view`, `gitHead`, integrity and the downloaded tarball.
172
210
  Promote to `latest` only after the DMO canary and rollback gate.
173
211
 
@@ -1,19 +1,22 @@
1
1
  ---
2
2
  import type { SfSeo } from "../types.js";
3
3
  import S from "@styleflow.app/astro/S.astro";
4
+ import ThemeRuntime from "./ThemeRuntime.astro";
4
5
 
5
6
  interface Props {
6
7
  lang?: string;
7
8
  seo?: SfSeo | null;
8
9
  siteName?: string;
9
10
  theme?: string;
11
+ themes?: string[];
10
12
  }
11
13
 
12
14
  const {
13
15
  lang = "it",
14
16
  seo = null,
15
17
  siteName = import.meta.env.PROJECT_NAME ?? "FlowPress",
16
- theme
18
+ theme,
19
+ themes = theme ? [theme] : []
17
20
  } = Astro.props;
18
21
  const title = seo?.title || siteName;
19
22
  const description = seo?.description || "";
@@ -26,6 +29,7 @@ const jsonLd = seo?.jsonLd ? JSON.stringify(seo.jsonLd) : null;
26
29
  <head>
27
30
  <meta charset="UTF-8" />
28
31
  <meta name="viewport" content="width=device-width" />
32
+ {theme && themes.length ? <ThemeRuntime themes={themes} defaultTheme={theme} /> : null}
29
33
  <title>{title}</title>
30
34
  {description && <meta name="description" content={description} />}
31
35
  {robots && <meta name="robots" content={robots} />}
@@ -0,0 +1,126 @@
1
+ ---
2
+ import type { FlowpressBlockAdapters } from "../block-renderers.js";
3
+ import {
4
+ flowpressAdapterCompatibilityIssue,
5
+ flowpressBlockHtmlProps,
6
+ flowpressCompositionLogoStyles,
7
+ flowpressBlockTypeDescriptors
8
+ } from "../composition.js";
9
+ import {
10
+ resolveFlowpressBlockStyleflowProps,
11
+ type StyleflowContract
12
+ } from "../styleflow.js";
13
+ import type { FlowpressBlock, FlowpressCompositionV1 } from "../types.js";
14
+
15
+ interface Props {
16
+ composition: FlowpressCompositionV1;
17
+ adapters: FlowpressBlockAdapters;
18
+ blocks?: FlowpressBlock[];
19
+ contract?: StyleflowContract;
20
+ parentBlockName?: string;
21
+ production?: boolean;
22
+ depth?: number;
23
+ }
24
+
25
+ const {
26
+ composition,
27
+ adapters,
28
+ blocks = composition.blocks,
29
+ contract,
30
+ parentBlockName,
31
+ production = import.meta.env.PROD,
32
+ depth = 0
33
+ } = Astro.props;
34
+
35
+ const blocking = composition.diagnostics.filter((diagnostic) => diagnostic.blocking);
36
+ if (blocking.length > 0 && production) {
37
+ throw new Error(
38
+ `FlowPress composition ${composition.sourceDigest} is not renderable:\n`
39
+ + blocking.map(({ code, message }) => `- ${code}: ${message}`).join("\n")
40
+ );
41
+ }
42
+ const descriptors = flowpressBlockTypeDescriptors(composition);
43
+ const logoStyles = parentBlockName === undefined
44
+ ? flowpressCompositionLogoStyles(composition)
45
+ : "";
46
+ ---
47
+
48
+ {parentBlockName === undefined && composition.stylesheets.map((stylesheet) => (
49
+ <style data-flowpress-composition-style={stylesheet.id} set:html={stylesheet.css} />
50
+ ))}
51
+ {logoStyles ? <style data-flowpress-logo-styles set:html={logoStyles} /> : null}
52
+
53
+ {blocking.length > 0 && !production && parentBlockName === undefined ? (
54
+ <aside data-flowpress-composition-diagnostics role="status">
55
+ <strong>FlowPress composition diagnostics</strong>
56
+ <ul>
57
+ {blocking.map(({ code, message, command }) => (
58
+ <li data-diagnostic-code={code}>
59
+ {message}
60
+ {command ? <code>{command}</code> : null}
61
+ </li>
62
+ ))}
63
+ </ul>
64
+ </aside>
65
+ ) : null}
66
+
67
+ {blocks.map((block) => {
68
+ const adapter = adapters[block.name];
69
+ const issue = flowpressAdapterCompatibilityIssue(block.name, descriptors, adapters);
70
+ if (issue && production) throw new Error(issue.message);
71
+ if (!adapter) {
72
+ return (
73
+ <aside data-flowpress-compatibility={issue?.code ?? "missing-adapter"} data-block-name={block.name} role="status">
74
+ <strong>Missing block adapter: {block.name}</strong>
75
+ <code>{issue?.command}</code>
76
+ </aside>
77
+ );
78
+ }
79
+ const Adapter = adapter.renderer;
80
+ const children = block.innerBlocks ?? [];
81
+ const styleflowProps = resolveFlowpressBlockStyleflowProps(
82
+ block.attributes,
83
+ contract,
84
+ {},
85
+ {
86
+ strict: production,
87
+ onDiagnostic: (diagnostic) => console.warn(
88
+ `[${diagnostic.code}] ${diagnostic.message}`
89
+ )
90
+ }
91
+ );
92
+ const htmlProps = flowpressBlockHtmlProps(block.attributes);
93
+ return (
94
+ <>
95
+ {issue && (
96
+ <aside data-flowpress-compatibility={issue.code} data-block-name={block.name} role="status">
97
+ <strong>{issue.message}</strong>
98
+ <code>{issue.command}</code>
99
+ </aside>
100
+ )}
101
+ <Adapter
102
+ block={block}
103
+ parentBlockName={parentBlockName}
104
+ contract={contract}
105
+ composition={composition}
106
+ styleflowProps={styleflowProps}
107
+ htmlProps={htmlProps}
108
+ context={{
109
+ ...(parentBlockName ? { parentBlockName } : {}),
110
+ depth,
111
+ production
112
+ }}
113
+ >
114
+ <Astro.self
115
+ composition={composition}
116
+ adapters={adapters}
117
+ blocks={children}
118
+ contract={contract}
119
+ parentBlockName={block.name}
120
+ production={production}
121
+ depth={depth + 1}
122
+ />
123
+ </Adapter>
124
+ </>
125
+ );
126
+ })}
@@ -11,7 +11,12 @@ const hasGutembergBlocks = Array.isArray(content?.contentBlocks) && content.cont
11
11
  //TODO SET GLOBAL LOCAL ON ENV
12
12
  ---
13
13
 
14
- <BaseLayout lang={route.locale || "it"} seo={route.seo} theme={styleflowContract?.defaultTheme}>
14
+ <BaseLayout
15
+ lang={route.locale || "it"}
16
+ seo={route.seo}
17
+ theme={styleflowContract?.defaultTheme}
18
+ themes={styleflowContract?.semantic.themes}
19
+ >
15
20
 
16
21
  {hasGutembergBlocks ? (
17
22
  <GutenbergRenderer
@@ -5,7 +5,8 @@ import type {
5
5
  FlowpressIconRenderer
6
6
  } from "../block-renderers.js";
7
7
  import type { StyleflowContract } from "../styleflow.js";
8
- import type { FlowpressBlock } from "../types.js";
8
+ import type { FlowpressBlock, FlowpressBlockHtmlProps } from "../types.js";
9
+ import type { DynamicStyleflowProps } from "../styleflow.js";
9
10
  import ColumnBlock from "./blocks/ColumnBlock.astro";
10
11
  import CarouselBlock from "./blocks/CarouselBlock.astro";
11
12
  import CarouselItemBlock from "./blocks/CarouselItemBlock.astro";
@@ -29,9 +30,18 @@ interface Props {
29
30
  contract?: StyleflowContract;
30
31
  blockRenderers?: FlowpressBlockRenderers;
31
32
  iconRenderer?: FlowpressIconRenderer;
33
+ rootHtmlProps?: FlowpressBlockHtmlProps;
34
+ rootStyleflowProps?: DynamicStyleflowProps;
32
35
  }
33
36
 
34
- const { blocks, contract, blockRenderers = {}, iconRenderer: IconRenderer } = Astro.props;
37
+ const {
38
+ blocks,
39
+ contract,
40
+ blockRenderers = {},
41
+ iconRenderer: IconRenderer,
42
+ rootHtmlProps,
43
+ rootStyleflowProps
44
+ } = Astro.props;
35
45
 
36
46
  function youtubeEmbedUrl(value: unknown): string | null {
37
47
  try {
@@ -51,11 +61,15 @@ function youtubeEmbedUrl(value: unknown): string | null {
51
61
  }
52
62
  ---
53
63
 
54
- {blocks.map((block) => {
64
+ {blocks.map((block, index) => {
55
65
  const name = block.name.replace(/^sf\//, "");
56
66
  const children = block.innerBlocks ?? [];
57
- const genericSemantic = () => semanticProps(block.attributes, contract);
58
- const htmlAttributes = blockHtmlAttributes(block.attributes);
67
+ const semantic = index === 0 && blocks.length === 1 && rootStyleflowProps
68
+ ? rootStyleflowProps
69
+ : semanticProps(block.attributes, contract);
70
+ const htmlAttributes = index === 0 && blocks.length === 1 && rootHtmlProps
71
+ ? rootHtmlProps
72
+ : blockHtmlAttributes(block.attributes);
59
73
  const CustomBlockRenderer = blockRenderers[block.name];
60
74
 
61
75
  if (CustomBlockRenderer) {
@@ -193,8 +207,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
193
207
  </>
194
208
  );
195
209
  return href ? (
196
- <S
197
- as="a"
210
+ <S.a
198
211
  href={href}
199
212
  target={opensInNewTab ? "_blank" : undefined}
200
213
  rel={opensInNewTab ? "noopener noreferrer" : undefined}
@@ -202,17 +215,16 @@ function youtubeEmbedUrl(value: unknown): string | null {
202
215
  {...htmlAttributes}
203
216
  >
204
217
  {buttonContent}
205
- </S>
218
+ </S.a>
206
219
  ) : (
207
- <S
208
- as="span"
220
+ <S.span
209
221
  aria-disabled="true"
210
222
  data-flowpress-link-unresolved
211
223
  {...buttonSemantic}
212
224
  {...htmlAttributes}
213
225
  >
214
226
  {buttonContent}
215
- </S>
227
+ </S.span>
216
228
  );
217
229
  }
218
230
 
@@ -259,7 +271,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
259
271
  ? block.attributes.images as Array<Record<string, unknown>>
260
272
  : [];
261
273
  return (
262
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
274
+ <S.div {...semantic} {...htmlAttributes}>
263
275
  {images.map((image) => image.url ? (
264
276
  <img
265
277
  src={String(image.url)}
@@ -268,7 +280,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
268
280
  decoding="async"
269
281
  />
270
282
  ) : null)}
271
- </S>
283
+ </S.div>
272
284
  );
273
285
  }
274
286
 
@@ -276,7 +288,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
276
288
  const video = objectValue(block, "video");
277
289
  const poster = objectValue(block, "poster");
278
290
  return video.url ? (
279
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
291
+ <S.div {...semantic} {...htmlAttributes}>
280
292
  <video
281
293
  src={String(video.url)}
282
294
  poster={poster.url ? String(poster.url) : undefined}
@@ -285,14 +297,14 @@ function youtubeEmbedUrl(value: unknown): string | null {
285
297
  muted={Boolean(block.attributes.autoplay)}
286
298
  playsinline
287
299
  />
288
- </S>
300
+ </S.div>
289
301
  ) : null;
290
302
  }
291
303
 
292
304
  if (name === "video-youtube") {
293
305
  const embed = youtubeEmbedUrl(block.attributes.url);
294
306
  return embed ? (
295
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
307
+ <S.div {...semantic} {...htmlAttributes}>
296
308
  <iframe
297
309
  src={embed}
298
310
  title={String(block.attributes.title ?? "YouTube video")}
@@ -300,15 +312,15 @@ function youtubeEmbedUrl(value: unknown): string | null {
300
312
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
301
313
  allowfullscreen
302
314
  />
303
- </S>
315
+ </S.div>
304
316
  ) : null;
305
317
  }
306
318
 
307
319
  if (name === "chip") {
308
320
  return (
309
- <S as="span" {...genericSemantic()} {...htmlAttributes}>
321
+ <S.span {...semantic} {...htmlAttributes}>
310
322
  {textValue(block, "label")}
311
- </S>
323
+ </S.span>
312
324
  );
313
325
  }
314
326
 
@@ -316,14 +328,14 @@ function youtubeEmbedUrl(value: unknown): string | null {
316
328
  const media = objectValue(block, "image");
317
329
  const href = linkValue(block);
318
330
  return (
319
- <S as="article" {...genericSemantic()} {...htmlAttributes}>
331
+ <S.article {...semantic} {...htmlAttributes}>
320
332
  {media.url && (
321
333
  <img src={String(media.url)} alt={String(media.alt ?? "")} loading="lazy" />
322
334
  )}
323
- {block.attributes.title && <S as="h3">{String(block.attributes.title)}</S>}
324
- {block.attributes.text && <S as="p">{String(block.attributes.text)}</S>}
325
- {href && <S as="a" href={href}>Scopri</S>}
326
- </S>
335
+ {block.attributes.title && <S.h3>{String(block.attributes.title)}</S.h3>}
336
+ {block.attributes.text && <S.p>{String(block.attributes.text)}</S.p>}
337
+ {href && <S.a href={href}>Scopri</S.a>}
338
+ </S.article>
327
339
  );
328
340
  }
329
341
 
@@ -335,9 +347,9 @@ function youtubeEmbedUrl(value: unknown): string | null {
335
347
  ? block.attributes.resolvedBlocks as FlowpressBlock[][]
336
348
  : [];
337
349
  return (
338
- <S as="ul" {...genericSemantic()} {...htmlAttributes}>
350
+ <S.ul {...semantic} {...htmlAttributes}>
339
351
  {items.map((item, index) => (
340
- <S as="li">
352
+ <S.li>
341
353
  {resolvedBlocks[index]?.length ? (
342
354
  <Astro.self
343
355
  blocks={resolvedBlocks[index]}
@@ -348,9 +360,9 @@ function youtubeEmbedUrl(value: unknown): string | null {
348
360
  ) : (
349
361
  <ListItemFallback item={item} />
350
362
  )}
351
- </S>
363
+ </S.li>
352
364
  ))}
353
- </S>
365
+ </S.ul>
354
366
  );
355
367
  }
356
368
 
@@ -380,7 +392,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
380
392
 
381
393
  if (name === "accordion-item") {
382
394
  return (
383
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
395
+ <S.div {...semantic} {...htmlAttributes}>
384
396
  <details>
385
397
  <summary>{String(block.attributes.title ?? "")}</summary>
386
398
  <Astro.self
@@ -390,7 +402,7 @@ function youtubeEmbedUrl(value: unknown): string | null {
390
402
  iconRenderer={IconRenderer}
391
403
  />
392
404
  </details>
393
- </S>
405
+ </S.div>
394
406
  );
395
407
  }
396
408
 
@@ -411,42 +423,41 @@ function youtubeEmbedUrl(value: unknown): string | null {
411
423
  + `?key=${encodeURIComponent(googleMapsApiKey)}`
412
424
  + `&q=${encodeURIComponent(query)}`;
413
425
  return (
414
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
426
+ <S.div {...semantic} {...htmlAttributes}>
415
427
  <iframe
416
428
  src={source}
417
429
  title={address || "Map"}
418
430
  loading="lazy"
419
431
  referrerpolicy="no-referrer-when-downgrade"
420
432
  />
421
- </S>
433
+ </S.div>
422
434
  );
423
435
  }
424
436
  return query ? (
425
- <S as="div" {...genericSemantic()} {...htmlAttributes}>
426
- <S
427
- as="a"
437
+ <S.div {...semantic} {...htmlAttributes}>
438
+ <S.a
428
439
  href={`https://www.openstreetmap.org/search?query=${encodeURIComponent(query)}`}
429
440
  rel="noreferrer"
430
441
  >
431
442
  {address || query}
432
- </S>
433
- </S>
443
+ </S.a>
444
+ </S.div>
434
445
  ) : null;
435
446
  }
436
447
 
437
- const layoutTag = name === "section" ? "section" : "div";
438
- return (
439
- <S as={layoutTag} {...genericSemantic()} {...htmlAttributes}>
440
- {block.innerHtml && children.length === 0
441
- ? <S as="div" set:html={block.innerHtml} />
442
- : (
443
- <Astro.self
444
- blocks={children}
445
- contract={contract}
446
- blockRenderers={blockRenderers}
447
- iconRenderer={IconRenderer}
448
- />
449
- )}
450
- </S>
448
+ const fallbackContent = block.innerHtml && children.length === 0
449
+ ? <S.div set:html={block.innerHtml} />
450
+ : (
451
+ <Astro.self
452
+ blocks={children}
453
+ contract={contract}
454
+ blockRenderers={blockRenderers}
455
+ iconRenderer={IconRenderer}
456
+ />
457
+ );
458
+ return name === "section" ? (
459
+ <S.section {...semantic} {...htmlAttributes}>{fallbackContent}</S.section>
460
+ ) : (
461
+ <S.div {...semantic} {...htmlAttributes}>{fallbackContent}</S.div>
451
462
  );
452
463
  })}
@@ -0,0 +1,24 @@
1
+ ---
2
+ import { styleflowThemeBootstrapScript } from "../theme-runtime.js";
3
+
4
+ interface Props {
5
+ themes: string[];
6
+ defaultTheme: string;
7
+ }
8
+
9
+ const { themes, defaultTheme } = Astro.props;
10
+ const bootstrap = styleflowThemeBootstrapScript(themes, defaultTheme);
11
+ ---
12
+
13
+ <script is:inline set:html={bootstrap}></script>
14
+ <script>
15
+ import { initializeStyleflowThemeRuntime } from "../theme-runtime.js";
16
+
17
+ const initialize = () => initializeStyleflowThemeRuntime();
18
+ if (document.readyState === "loading") {
19
+ document.addEventListener("DOMContentLoaded", initialize, { once: true });
20
+ } else {
21
+ initialize();
22
+ }
23
+ document.addEventListener("astro:page-load", initialize);
24
+ </script>
@@ -44,8 +44,7 @@ if (!validChildren && import.meta.env.DEV) {
44
44
 
45
45
  {validChildren ? (
46
46
  <flowpress-carousel>
47
- <S
48
- as="section"
47
+ <S.section
49
48
  {...semantic}
50
49
  {...htmlAttributes}
51
50
  aria-label={settings.ariaLabel}
@@ -79,11 +78,10 @@ if (!validChildren && import.meta.env.DEV) {
79
78
  <span class="splide__toggle__pause">Pausa</span>
80
79
  </button>
81
80
  ) : null}
82
- </S>
81
+ </S.section>
83
82
  </flowpress-carousel>
84
83
  ) : (
85
- <S
86
- as="section"
84
+ <S.section
87
85
  {...semantic}
88
86
  {...htmlAttributes}
89
87
  aria-label={settings.ariaLabel}
@@ -92,7 +90,7 @@ if (!validChildren && import.meta.env.DEV) {
92
90
  style={style}
93
91
  >
94
92
  <slot />
95
- </S>
93
+ </S.section>
96
94
  )}
97
95
 
98
96
  <script>
@@ -17,9 +17,9 @@ const htmlAttributes = blockHtmlAttributes(
17
17
  );
18
18
  ---
19
19
 
20
- <S as="li" {...semantic} {...htmlAttributes}>
20
+ <S.li {...semantic} {...htmlAttributes}>
21
21
  <slot />
22
- </S>
22
+ </S.li>
23
23
 
24
24
  <style>
25
25
  .flowpress-carousel-item { min-width: 0; }
@@ -33,8 +33,7 @@ const style = [
33
33
  ].join(";");
34
34
  ---
35
35
 
36
- <S
37
- as="div"
36
+ <S.div
38
37
  {...semantic}
39
38
  {...htmlAttributes}
40
39
  data-partial={partial}
@@ -42,7 +41,7 @@ const style = [
42
41
  style={style}
43
42
  >
44
43
  <slot />
45
- </S>
44
+ </S.div>
46
45
 
47
46
  <style>
48
47
  .flowpress-column {
@@ -39,11 +39,11 @@ const optionLabel = (field: FormField, index: number): string =>
39
39
  ---
40
40
 
41
41
  {schema?.available ? (
42
- <S as="div" {...semantic} {...htmlAttributes}>
42
+ <S.div {...semantic} {...htmlAttributes}>
43
43
  <flowpress-cf7-form>
44
44
  <form action={schema.submitUrl} method="post" enctype="multipart/form-data" novalidate>
45
45
  {block.attributes.title || schema.title ? (
46
- <S as="h2">{String(block.attributes.title || schema.title)}</S>
46
+ <S.h2>{String(block.attributes.title || schema.title)}</S.h2>
47
47
  ) : null}
48
48
  {schema.fields.map((field) => {
49
49
  if (field.type === "submit") {
@@ -130,11 +130,11 @@ const optionLabel = (field: FormField, index: number): string =>
130
130
  <p data-form-status role="status" aria-live="polite"></p>
131
131
  </form>
132
132
  </flowpress-cf7-form>
133
- </S>
133
+ </S.div>
134
134
  ) : (
135
- <S as="p" {...semantic} {...htmlAttributes} role="status">
135
+ <S.p {...semantic} {...htmlAttributes} role="status">
136
136
  Il modulo non è temporaneamente disponibile.
137
- </S>
137
+ </S.p>
138
138
  )}
139
139
 
140
140
  <script>
@@ -38,9 +38,9 @@ const style = [
38
38
  ].join(";");
39
39
  ---
40
40
 
41
- <S as="div" {...semantic} {...htmlAttributes} data-align={align} style={style}>
41
+ <S.div {...semantic} {...htmlAttributes} data-align={align} style={style}>
42
42
  <slot />
43
- </S>
43
+ </S.div>
44
44
 
45
45
  <style>
46
46
  .flowpress-grid {
@@ -34,8 +34,7 @@ const scrimEnabled = Boolean(attributes.scrimEnabled) && backgroundType !== "col
34
34
  const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity) || 6));
35
35
  ---
36
36
 
37
- <S
38
- as="section"
37
+ <S.section
39
38
  {...semantic}
40
39
  {...htmlAttributes}
41
40
  style={[
@@ -71,7 +70,7 @@ const scrimIntensity = Math.min(10, Math.max(1, Number(attributes.scrimIntensity
71
70
  <div class="flowpress-hero__content">
72
71
  <slot />
73
72
  </div>
74
- </S>
73
+ </S.section>
75
74
 
76
75
  <style>
77
76
  .flowpress-hero {