@contentrain/astro-kit 0.39.0 → 0.41.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.
- package/README.md +18 -2
- package/catalog.json +99 -18
- package/components/_shared/Section.astro +13 -9
- package/components/_shared/SectionHeader.astro +3 -2
- package/components/_shared/behaviors.ts +38 -2
- package/components/_shared/embed-frame.ts +22 -0
- package/components/{embed/embed.ts → _shared/embed-target.ts} +30 -0
- package/components/_shared/instance-id.ts +24 -0
- package/components/_shared/muted.ts +19 -0
- package/components/band/Band.astro +10 -7
- package/components/band/meta.json +2 -0
- package/components/behavior/Behavior.astro +15 -2
- package/components/behavior/fixtures.json +5 -3
- package/components/behavior/meta.json +9 -0
- package/components/card-grid/CardGrid.astro +13 -9
- package/components/card-grid/meta.json +7 -0
- package/components/contact-details/ContactDetails.astro +7 -3
- package/components/contact-details/meta.json +2 -0
- package/components/contact-form/ContactForm.astro +11 -7
- package/components/contact-form/meta.json +2 -0
- package/components/cta/Cta.astro +14 -10
- package/components/cta/meta.json +2 -2
- package/components/dialog/Dialog.astro +6 -3
- package/components/dialog/meta.json +2 -0
- package/components/embed/Embed.astro +7 -10
- package/components/embed/meta.json +1 -1
- package/components/faq/Faq.astro +8 -4
- package/components/faq/meta.json +1 -1
- package/components/feature-list/FeatureList.astro +10 -6
- package/components/feature-list/meta.json +3 -1
- package/components/footer/Footer.astro +4 -3
- package/components/footer/meta.json +1 -0
- package/components/gallery/Gallery.astro +8 -4
- package/components/gallery/meta.json +2 -0
- package/components/header/Header.astro +8 -4
- package/components/header/meta.json +1 -1
- package/components/hero/Hero.astro +13 -9
- package/components/hero/meta.json +3 -1
- package/components/language-switcher/LanguageSwitcher.astro +3 -2
- package/components/language-switcher/meta.json +1 -1
- package/components/logo-cloud/LogoCloud.astro +9 -5
- package/components/logo-cloud/meta.json +3 -1
- package/components/nav/Nav.astro +10 -6
- package/components/nav/meta.json +2 -0
- package/components/newsletter/Newsletter.astro +9 -5
- package/components/newsletter/meta.json +2 -0
- package/components/post-list/PostList.astro +7 -3
- package/components/post-list/meta.json +2 -0
- package/components/pricing/Pricing.astro +15 -11
- package/components/pricing/meta.json +3 -1
- package/components/search-box/SearchBox.astro +4 -1
- package/components/search-box/meta.json +1 -1
- package/components/slider/Slider.astro +9 -2
- package/components/slider/SliderTrack.astro +71 -15
- package/components/slider/fixtures.json +2 -1
- package/components/slider/meta.json +5 -4
- package/components/social-links/SocialLinks.astro +2 -1
- package/components/social-links/meta.json +3 -2
- package/components/split/Split.astro +9 -5
- package/components/split/meta.json +3 -1
- package/components/stats/Stats.astro +11 -7
- package/components/stats/meta.json +5 -1
- package/components/steps/Steps.astro +9 -5
- package/components/steps/meta.json +3 -1
- package/components/tabs/Tabs.astro +8 -4
- package/components/tabs/meta.json +1 -1
- package/components/team/Team.astro +10 -6
- package/components/team/meta.json +4 -2
- package/components/testimonial/Testimonial.astro +9 -5
- package/components/testimonial/fixtures.json +1 -1
- package/components/testimonial/meta.json +3 -1
- package/mapping/classic.json +14 -7
- package/mapping/divi.json +6 -3
- package/mapping/elementor.json +14 -7
- package/mapping/gutenberg.json +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ They read its design tokens and pass its gates: `astro check` with the
|
|
|
25
25
|
| `tabs` | Tabbed panels; readable as headed sections without JavaScript | Zag.js |
|
|
26
26
|
| `testimonial` | Quotes with author and role | none |
|
|
27
27
|
| `gallery` | Image grid with a popover lightbox | none |
|
|
28
|
-
| `slider` | Carousel on a scroll-snap row: image, video (played only on request) or text-card slides | Embla |
|
|
28
|
+
| `slider` | Carousel on a scroll-snap row: image, video (a file played only on request, or a YouTube/Vimeo link loaded only on click) or text-card slides | Embla |
|
|
29
29
|
| `contact-form` | Contentrain Studio form in a section | Studio runtime |
|
|
30
30
|
| `newsletter` | Sign-up posting straight to the list provider (Mailchimp, Kit, MailerLite, Brevo, Buttondown) | none |
|
|
31
31
|
| `embed` | Video, map or player loaded on click; a provider link until then (YouTube via nocookie) | a few lines, no dependency |
|
|
@@ -33,7 +33,7 @@ They read its design tokens and pass its gates: `astro check` with the
|
|
|
33
33
|
| `breadcrumb` | Ancestors, then the current page, optional JSON-LD | none |
|
|
34
34
|
| `band` | The styled fallback section: heading, lead, image, a grid of items (figure, title, detail, text, points, link) and buttons, in the measured frame | none |
|
|
35
35
|
| `dialog` | A popup the visitor opens: a button and a modal `<dialog>` with text, buttons and a `form` slot; never opens by itself | a few lines, no dependency |
|
|
36
|
-
| `behavior` | A behavior island: a source widget no component carries (slider, tabs, accordion, counter, toggle, filter, in-page dialog, anchor links, image lightbox), kept as its own markup and wired by one vanilla module; static and readable without it | `_shared/behaviors.ts`, no dependency |
|
|
36
|
+
| `behavior` | A behavior island: a source widget no component carries (slider, tabs, accordion, counter, toggle, filter, in-page dialog, anchor links, image lightbox, a video player kept as a poster link that plays in place on click), kept as its own markup and wired by one vanilla module; static and readable without it | `_shared/behaviors.ts`, no dependency |
|
|
37
37
|
|
|
38
38
|
Each component lives in `components/<id>/`:
|
|
39
39
|
|
|
@@ -81,6 +81,22 @@ The starter sets it to the accent; a site whose accent does not reach 4.5:1 on i
|
|
|
81
81
|
surface sets a readable one. A dark section (`tone="inverse"`) reads
|
|
82
82
|
`--color-accent-text-inverse` in its place.
|
|
83
83
|
|
|
84
|
+
Quieter text (an intro, a caption, a role, a footer link) is a colour role, never
|
|
85
|
+
opacity: dimmed text lays its colour over the band, and on a mid-tone band that falls
|
|
86
|
+
under 4.5:1. It reads `--color-ink-muted` on a light band, `--color-ink-muted-inverse`
|
|
87
|
+
on a dark one (the starter mixes the surface toward the ink; without it, the band's own
|
|
88
|
+
text colour) and the fill's own ink on an accent or brand band. A light surface inside
|
|
89
|
+
a band (a card, a form panel, a menu) is back on `--color-ink-muted`. The classes are
|
|
90
|
+
in `_shared/muted.ts`.
|
|
91
|
+
|
|
92
|
+
### Section hooks
|
|
93
|
+
|
|
94
|
+
Every section component renders its root with an `id` and `data-kit-section="<component id>"`
|
|
95
|
+
(`hero`, `card-grid`, `slider`, …), so a site's theme or a design layer can style one
|
|
96
|
+
kind of section, or one section, without touching the component. The id is the one the
|
|
97
|
+
page gives, or the component id counted per page (`hero`, then `hero-2`), the same on
|
|
98
|
+
every build.
|
|
99
|
+
|
|
84
100
|
A migrated site also carries its theme's type and shape, and components read
|
|
85
101
|
these with a fallback to the kit's own values, so a site without them looks
|
|
86
102
|
unchanged:
|
package/catalog.json
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"--radius-control",
|
|
24
24
|
"--radius-image",
|
|
25
25
|
"--spacing-gutter",
|
|
26
|
+
"--color-ink-muted-inverse",
|
|
26
27
|
"--text-nav",
|
|
27
28
|
"--text-footer",
|
|
28
29
|
"--text-heading-1",
|
|
@@ -103,7 +104,9 @@
|
|
|
103
104
|
"Button.astro",
|
|
104
105
|
"KitImage.astro",
|
|
105
106
|
"Section.astro",
|
|
107
|
+
"instance-id.ts",
|
|
106
108
|
"measured.ts",
|
|
109
|
+
"muted.ts",
|
|
107
110
|
"safe-url.ts",
|
|
108
111
|
"types.ts"
|
|
109
112
|
],
|
|
@@ -455,7 +458,11 @@
|
|
|
455
458
|
"shared": [
|
|
456
459
|
"Section.astro",
|
|
457
460
|
"behaviors.ts",
|
|
461
|
+
"embed-frame.ts",
|
|
462
|
+
"embed-target.ts",
|
|
463
|
+
"instance-id.ts",
|
|
458
464
|
"measured.ts",
|
|
465
|
+
"muted.ts",
|
|
459
466
|
"safe-url.ts",
|
|
460
467
|
"types.ts"
|
|
461
468
|
],
|
|
@@ -641,6 +648,11 @@
|
|
|
641
648
|
"description": "The section's minimum height."
|
|
642
649
|
}
|
|
643
650
|
}
|
|
651
|
+
},
|
|
652
|
+
"id": {
|
|
653
|
+
"type": "string",
|
|
654
|
+
"content": false,
|
|
655
|
+
"description": "Anchor id of the section. Without one, `behavior`, `behavior-2`, … in render order, so every section on a page has its own."
|
|
644
656
|
}
|
|
645
657
|
},
|
|
646
658
|
"slots": {},
|
|
@@ -740,7 +752,9 @@
|
|
|
740
752
|
"KitImage.astro",
|
|
741
753
|
"Section.astro",
|
|
742
754
|
"image.ts",
|
|
755
|
+
"instance-id.ts",
|
|
743
756
|
"measured.ts",
|
|
757
|
+
"muted.ts",
|
|
744
758
|
"safe-url.ts",
|
|
745
759
|
"types.ts"
|
|
746
760
|
],
|
|
@@ -962,6 +976,11 @@
|
|
|
962
976
|
"description": "The section's minimum height."
|
|
963
977
|
}
|
|
964
978
|
}
|
|
979
|
+
},
|
|
980
|
+
"id": {
|
|
981
|
+
"type": "string",
|
|
982
|
+
"content": false,
|
|
983
|
+
"description": "Anchor id of the section; the title gets `<id>-title`. Without one, `card-grid`, `card-grid-2`, … in render order, so every section on a page has its own."
|
|
965
984
|
}
|
|
966
985
|
},
|
|
967
986
|
"slots": {},
|
|
@@ -1085,7 +1104,9 @@
|
|
|
1085
1104
|
"uses": [],
|
|
1086
1105
|
"shared": [
|
|
1087
1106
|
"Section.astro",
|
|
1107
|
+
"instance-id.ts",
|
|
1088
1108
|
"measured.ts",
|
|
1109
|
+
"muted.ts",
|
|
1089
1110
|
"safe-url.ts",
|
|
1090
1111
|
"types.ts"
|
|
1091
1112
|
],
|
|
@@ -1317,7 +1338,9 @@
|
|
|
1317
1338
|
"shared": [
|
|
1318
1339
|
"KitImage.astro",
|
|
1319
1340
|
"Section.astro",
|
|
1341
|
+
"instance-id.ts",
|
|
1320
1342
|
"measured.ts",
|
|
1343
|
+
"muted.ts",
|
|
1321
1344
|
"safe-url.ts",
|
|
1322
1345
|
"types.ts"
|
|
1323
1346
|
],
|
|
@@ -1644,7 +1667,9 @@
|
|
|
1644
1667
|
"Button.astro",
|
|
1645
1668
|
"KitImage.astro",
|
|
1646
1669
|
"Section.astro",
|
|
1670
|
+
"instance-id.ts",
|
|
1647
1671
|
"measured.ts",
|
|
1672
|
+
"muted.ts",
|
|
1648
1673
|
"safe-url.ts",
|
|
1649
1674
|
"types.ts"
|
|
1650
1675
|
],
|
|
@@ -1899,7 +1924,7 @@
|
|
|
1899
1924
|
"id": {
|
|
1900
1925
|
"type": "string",
|
|
1901
1926
|
"content": false,
|
|
1902
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
1927
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `cta`, `cta-2`, … in render order, so every section on a page has its own."
|
|
1903
1928
|
}
|
|
1904
1929
|
},
|
|
1905
1930
|
"slots": {},
|
|
@@ -1945,7 +1970,9 @@
|
|
|
1945
1970
|
"shared": [
|
|
1946
1971
|
"Button.astro",
|
|
1947
1972
|
"Section.astro",
|
|
1973
|
+
"instance-id.ts",
|
|
1948
1974
|
"measured.ts",
|
|
1975
|
+
"muted.ts",
|
|
1949
1976
|
"safe-url.ts",
|
|
1950
1977
|
"types.ts"
|
|
1951
1978
|
],
|
|
@@ -2226,6 +2253,8 @@
|
|
|
2226
2253
|
"uses": [],
|
|
2227
2254
|
"shared": [
|
|
2228
2255
|
"KitImage.astro",
|
|
2256
|
+
"embed-frame.ts",
|
|
2257
|
+
"embed-target.ts",
|
|
2229
2258
|
"safe-url.ts",
|
|
2230
2259
|
"types.ts"
|
|
2231
2260
|
],
|
|
@@ -2323,8 +2352,7 @@
|
|
|
2323
2352
|
},
|
|
2324
2353
|
"a11y": "The poster is one link named '<action>: <title>' that opens the provider's page without script. With script, Enter or a click puts the iframe in its place with the title as its accessible name and moves focus into it. Modified clicks (new tab) keep the link behaviour. The play icon is decorative.",
|
|
2325
2354
|
"files": [
|
|
2326
|
-
"Embed.astro"
|
|
2327
|
-
"embed.ts"
|
|
2355
|
+
"Embed.astro"
|
|
2328
2356
|
]
|
|
2329
2357
|
},
|
|
2330
2358
|
{
|
|
@@ -2335,7 +2363,9 @@
|
|
|
2335
2363
|
"uses": [],
|
|
2336
2364
|
"shared": [
|
|
2337
2365
|
"Section.astro",
|
|
2366
|
+
"instance-id.ts",
|
|
2338
2367
|
"measured.ts",
|
|
2368
|
+
"muted.ts",
|
|
2339
2369
|
"safe-url.ts",
|
|
2340
2370
|
"types.ts"
|
|
2341
2371
|
],
|
|
@@ -2608,7 +2638,9 @@
|
|
|
2608
2638
|
"shared": [
|
|
2609
2639
|
"KitImage.astro",
|
|
2610
2640
|
"Section.astro",
|
|
2641
|
+
"instance-id.ts",
|
|
2611
2642
|
"measured.ts",
|
|
2643
|
+
"muted.ts",
|
|
2612
2644
|
"safe-url.ts",
|
|
2613
2645
|
"types.ts"
|
|
2614
2646
|
],
|
|
@@ -2820,7 +2852,7 @@
|
|
|
2820
2852
|
"id": {
|
|
2821
2853
|
"type": "string",
|
|
2822
2854
|
"content": false,
|
|
2823
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
2855
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `feature-list`, `feature-list-2`, … in render order, so every section on a page has its own."
|
|
2824
2856
|
}
|
|
2825
2857
|
},
|
|
2826
2858
|
"slots": {},
|
|
@@ -2868,6 +2900,7 @@
|
|
|
2868
2900
|
"category": "chrome",
|
|
2869
2901
|
"uses": [],
|
|
2870
2902
|
"shared": [
|
|
2903
|
+
"muted.ts",
|
|
2871
2904
|
"safe-url.ts",
|
|
2872
2905
|
"types.ts"
|
|
2873
2906
|
],
|
|
@@ -3090,7 +3123,9 @@
|
|
|
3090
3123
|
"KitImage.astro",
|
|
3091
3124
|
"Section.astro",
|
|
3092
3125
|
"image.ts",
|
|
3126
|
+
"instance-id.ts",
|
|
3093
3127
|
"measured.ts",
|
|
3128
|
+
"muted.ts",
|
|
3094
3129
|
"safe-url.ts",
|
|
3095
3130
|
"types.ts"
|
|
3096
3131
|
],
|
|
@@ -3386,6 +3421,8 @@
|
|
|
3386
3421
|
"shared": [
|
|
3387
3422
|
"Button.astro",
|
|
3388
3423
|
"KitImage.astro",
|
|
3424
|
+
"instance-id.ts",
|
|
3425
|
+
"muted.ts",
|
|
3389
3426
|
"safe-url.ts",
|
|
3390
3427
|
"types.ts"
|
|
3391
3428
|
],
|
|
@@ -3616,7 +3653,9 @@
|
|
|
3616
3653
|
"Button.astro",
|
|
3617
3654
|
"KitImage.astro",
|
|
3618
3655
|
"Section.astro",
|
|
3656
|
+
"instance-id.ts",
|
|
3619
3657
|
"measured.ts",
|
|
3658
|
+
"muted.ts",
|
|
3620
3659
|
"safe-url.ts",
|
|
3621
3660
|
"types.ts"
|
|
3622
3661
|
],
|
|
@@ -3806,7 +3845,7 @@
|
|
|
3806
3845
|
"id": {
|
|
3807
3846
|
"type": "string",
|
|
3808
3847
|
"content": false,
|
|
3809
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
3848
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `hero`, `hero-2`, … in render order, so every section on a page has its own."
|
|
3810
3849
|
},
|
|
3811
3850
|
"minHeight": {
|
|
3812
3851
|
"type": "integer",
|
|
@@ -3973,6 +4012,7 @@
|
|
|
3973
4012
|
"category": "chrome",
|
|
3974
4013
|
"uses": [],
|
|
3975
4014
|
"shared": [
|
|
4015
|
+
"muted.ts",
|
|
3976
4016
|
"safe-url.ts"
|
|
3977
4017
|
],
|
|
3978
4018
|
"variants": {
|
|
@@ -4054,7 +4094,9 @@
|
|
|
4054
4094
|
"KitImage.astro",
|
|
4055
4095
|
"Section.astro",
|
|
4056
4096
|
"image.ts",
|
|
4097
|
+
"instance-id.ts",
|
|
4057
4098
|
"measured.ts",
|
|
4099
|
+
"muted.ts",
|
|
4058
4100
|
"safe-url.ts",
|
|
4059
4101
|
"types.ts"
|
|
4060
4102
|
],
|
|
@@ -4263,7 +4305,7 @@
|
|
|
4263
4305
|
"id": {
|
|
4264
4306
|
"type": "string",
|
|
4265
4307
|
"content": false,
|
|
4266
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
4308
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `logo-cloud`, `logo-cloud-2`, … in render order, so every section on a page has its own."
|
|
4267
4309
|
}
|
|
4268
4310
|
},
|
|
4269
4311
|
"slots": {},
|
|
@@ -4309,6 +4351,8 @@
|
|
|
4309
4351
|
"category": "chrome",
|
|
4310
4352
|
"uses": [],
|
|
4311
4353
|
"shared": [
|
|
4354
|
+
"instance-id.ts",
|
|
4355
|
+
"muted.ts",
|
|
4312
4356
|
"safe-url.ts",
|
|
4313
4357
|
"types.ts"
|
|
4314
4358
|
],
|
|
@@ -4434,7 +4478,9 @@
|
|
|
4434
4478
|
"uses": [],
|
|
4435
4479
|
"shared": [
|
|
4436
4480
|
"Section.astro",
|
|
4481
|
+
"instance-id.ts",
|
|
4437
4482
|
"measured.ts",
|
|
4483
|
+
"muted.ts",
|
|
4438
4484
|
"safe-url.ts",
|
|
4439
4485
|
"types.ts"
|
|
4440
4486
|
],
|
|
@@ -4924,7 +4970,9 @@
|
|
|
4924
4970
|
],
|
|
4925
4971
|
"shared": [
|
|
4926
4972
|
"Section.astro",
|
|
4973
|
+
"instance-id.ts",
|
|
4927
4974
|
"measured.ts",
|
|
4975
|
+
"muted.ts",
|
|
4928
4976
|
"safe-url.ts",
|
|
4929
4977
|
"types.ts"
|
|
4930
4978
|
],
|
|
@@ -5244,7 +5292,9 @@
|
|
|
5244
5292
|
"shared": [
|
|
5245
5293
|
"Button.astro",
|
|
5246
5294
|
"Section.astro",
|
|
5295
|
+
"instance-id.ts",
|
|
5247
5296
|
"measured.ts",
|
|
5297
|
+
"muted.ts",
|
|
5248
5298
|
"safe-url.ts",
|
|
5249
5299
|
"types.ts"
|
|
5250
5300
|
],
|
|
@@ -5382,7 +5432,7 @@
|
|
|
5382
5432
|
"id": {
|
|
5383
5433
|
"type": "string",
|
|
5384
5434
|
"content": false,
|
|
5385
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
5435
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `pricing`, `pricing-2`, … in render order, so every section on a page has its own."
|
|
5386
5436
|
},
|
|
5387
5437
|
"compact": {
|
|
5388
5438
|
"type": "boolean",
|
|
@@ -5657,6 +5707,7 @@
|
|
|
5657
5707
|
"category": "chrome",
|
|
5658
5708
|
"uses": [],
|
|
5659
5709
|
"shared": [
|
|
5710
|
+
"instance-id.ts",
|
|
5660
5711
|
"safe-url.ts"
|
|
5661
5712
|
],
|
|
5662
5713
|
"variants": {
|
|
@@ -5733,7 +5784,7 @@
|
|
|
5733
5784
|
{
|
|
5734
5785
|
"id": "slider",
|
|
5735
5786
|
"name": "Slider",
|
|
5736
|
-
"description": "A carousel of images, videos or text cards, with optional title, text and button per slide. Without JavaScript it is a snapping horizontal scroller; Embla adds paging, arrows and dots.",
|
|
5787
|
+
"description": "A carousel of images, videos (a file, or a YouTube/Vimeo link loaded only on click) or text cards, with optional title, text and button per slide. Without JavaScript it is a snapping horizontal scroller; Embla adds paging, arrows and dots.",
|
|
5737
5788
|
"category": "section",
|
|
5738
5789
|
"uses": [],
|
|
5739
5790
|
"shared": [
|
|
@@ -5741,8 +5792,12 @@
|
|
|
5741
5792
|
"KitImage.astro",
|
|
5742
5793
|
"Section.astro",
|
|
5743
5794
|
"SectionHeader.astro",
|
|
5795
|
+
"embed-frame.ts",
|
|
5796
|
+
"embed-target.ts",
|
|
5744
5797
|
"image.ts",
|
|
5798
|
+
"instance-id.ts",
|
|
5745
5799
|
"measured.ts",
|
|
5800
|
+
"muted.ts",
|
|
5746
5801
|
"safe-url.ts",
|
|
5747
5802
|
"types.ts"
|
|
5748
5803
|
],
|
|
@@ -5797,6 +5852,11 @@
|
|
|
5797
5852
|
"label": "Video",
|
|
5798
5853
|
"description": "A video file (mp4, webm) shown in place of the image, which is its poster. It plays only when the reader starts it."
|
|
5799
5854
|
},
|
|
5855
|
+
"embed": {
|
|
5856
|
+
"type": "url",
|
|
5857
|
+
"label": "Video link",
|
|
5858
|
+
"description": "A video on another site: its YouTube, Vimeo, Loom or Wistia page (or youtube:<id> / vimeo:<id>). Shown as the image with a play button; nothing from that site loads until the reader clicks it. Wins over Video."
|
|
5859
|
+
},
|
|
5800
5860
|
"title": {
|
|
5801
5861
|
"type": "string",
|
|
5802
5862
|
"label": "Title",
|
|
@@ -5819,7 +5879,7 @@
|
|
|
5819
5879
|
}
|
|
5820
5880
|
}
|
|
5821
5881
|
},
|
|
5822
|
-
"description": "Slides: the image's address and text, an optional video (the image is its poster), and an optional link (ctaLabel + ctaHref). A slide with no image or video is a text card; one with none of them is not drawn."
|
|
5882
|
+
"description": "Slides: the image's address and text, an optional video file or video link (the image is its poster), and an optional link (ctaLabel + ctaHref). A slide with no image or video is a text card; one with none of them is not drawn."
|
|
5823
5883
|
},
|
|
5824
5884
|
"label": {
|
|
5825
5885
|
"type": "string",
|
|
@@ -5847,6 +5907,11 @@
|
|
|
5847
5907
|
"content": false,
|
|
5848
5908
|
"description": "ui-strings slider.of."
|
|
5849
5909
|
},
|
|
5910
|
+
"playLabel": {
|
|
5911
|
+
"type": "string",
|
|
5912
|
+
"content": false,
|
|
5913
|
+
"description": "ui-strings embed.play."
|
|
5914
|
+
},
|
|
5850
5915
|
"loop": {
|
|
5851
5916
|
"type": "boolean",
|
|
5852
5917
|
"default": false,
|
|
@@ -6055,7 +6120,7 @@
|
|
|
6055
6120
|
"classic/owl"
|
|
6056
6121
|
]
|
|
6057
6122
|
},
|
|
6058
|
-
"a11y": "WAI-ARIA carousel pattern: a labelled region with aria-roledescription=carousel, slides as labelled groups ('Slide 2 of 5'), labelled previous/next buttons disabled at the ends, and dot buttons marking the current slide. The track and the buttons are tied by aria-controls, the row takes focus (arrow keys page it, polite live announcement). There is no autoplay, so nothing moves unasked: a video slide loads nothing until the reader plays it, never starts by itself, keeps its native controls with its caption under them, and pauses when its slide is paged away; with prefers-reduced-motion slides change without animation. Without JavaScript every slide is reachable by scrolling.",
|
|
6123
|
+
"a11y": "WAI-ARIA carousel pattern: a labelled region with aria-roledescription=carousel, slides as labelled groups ('Slide 2 of 5'), labelled previous/next buttons disabled at the ends, and dot buttons marking the current slide. The track and the buttons are tied by aria-controls, the row takes focus (arrow keys page it, polite live announcement). There is no autoplay, so nothing moves unasked: a video slide loads nothing until the reader plays it, never starts by itself, keeps its native controls with its caption under them, and pauses when its slide is paged away; a video link is a labelled poster link ('Play: title') to the video's page that becomes the player only on a click (no request to the provider before it; YouTube from youtube-nocookie.com, Vimeo with dnt), keeps the poster's box so nothing moves, takes focus, and goes back to its poster when its slide is paged away; with prefers-reduced-motion slides change without animation. Without JavaScript every slide is reachable by scrolling.",
|
|
6059
6124
|
"files": [
|
|
6060
6125
|
"Slider.astro",
|
|
6061
6126
|
"SliderTrack.astro"
|
|
@@ -6065,6 +6130,7 @@
|
|
|
6065
6130
|
"slides[].image": "slides[].image",
|
|
6066
6131
|
"slides[].imageAlt": "slides[].image_alt",
|
|
6067
6132
|
"slides[].video": "slides[].video",
|
|
6133
|
+
"slides[].embed": "slides[].embed",
|
|
6068
6134
|
"slides[].title": "slides[].title",
|
|
6069
6135
|
"slides[].text": "slides[].text",
|
|
6070
6136
|
"slides[].ctaLabel": "slides[].cta_label",
|
|
@@ -6086,8 +6152,9 @@
|
|
|
6086
6152
|
"category": "chrome",
|
|
6087
6153
|
"uses": [],
|
|
6088
6154
|
"shared": [
|
|
6089
|
-
"
|
|
6090
|
-
"safe-url.ts"
|
|
6155
|
+
"muted.ts",
|
|
6156
|
+
"safe-url.ts",
|
|
6157
|
+
"social.ts"
|
|
6091
6158
|
],
|
|
6092
6159
|
"variants": {
|
|
6093
6160
|
"display": {
|
|
@@ -6189,7 +6256,9 @@
|
|
|
6189
6256
|
"Button.astro",
|
|
6190
6257
|
"KitImage.astro",
|
|
6191
6258
|
"Section.astro",
|
|
6259
|
+
"instance-id.ts",
|
|
6192
6260
|
"measured.ts",
|
|
6261
|
+
"muted.ts",
|
|
6193
6262
|
"safe-url.ts",
|
|
6194
6263
|
"types.ts"
|
|
6195
6264
|
],
|
|
@@ -6517,7 +6586,7 @@
|
|
|
6517
6586
|
"id": {
|
|
6518
6587
|
"type": "string",
|
|
6519
6588
|
"content": false,
|
|
6520
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
6589
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `split`, `split-2`, … in render order, so every section on a page has its own."
|
|
6521
6590
|
},
|
|
6522
6591
|
"sticky": {
|
|
6523
6592
|
"type": "boolean",
|
|
@@ -6586,8 +6655,12 @@
|
|
|
6586
6655
|
"KitImage.astro",
|
|
6587
6656
|
"Section.astro",
|
|
6588
6657
|
"behaviors.ts",
|
|
6658
|
+
"embed-frame.ts",
|
|
6659
|
+
"embed-target.ts",
|
|
6589
6660
|
"image.ts",
|
|
6661
|
+
"instance-id.ts",
|
|
6590
6662
|
"measured.ts",
|
|
6663
|
+
"muted.ts",
|
|
6591
6664
|
"safe-url.ts",
|
|
6592
6665
|
"types.ts"
|
|
6593
6666
|
],
|
|
@@ -6836,7 +6909,7 @@
|
|
|
6836
6909
|
"id": {
|
|
6837
6910
|
"type": "string",
|
|
6838
6911
|
"content": false,
|
|
6839
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
6912
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `stats`, `stats-2`, … in render order, so every section on a page has its own."
|
|
6840
6913
|
}
|
|
6841
6914
|
},
|
|
6842
6915
|
"slots": {},
|
|
@@ -6887,7 +6960,9 @@
|
|
|
6887
6960
|
"KitImage.astro",
|
|
6888
6961
|
"Section.astro",
|
|
6889
6962
|
"image.ts",
|
|
6963
|
+
"instance-id.ts",
|
|
6890
6964
|
"measured.ts",
|
|
6965
|
+
"muted.ts",
|
|
6891
6966
|
"safe-url.ts",
|
|
6892
6967
|
"types.ts"
|
|
6893
6968
|
],
|
|
@@ -6998,7 +7073,7 @@
|
|
|
6998
7073
|
"id": {
|
|
6999
7074
|
"type": "string",
|
|
7000
7075
|
"content": false,
|
|
7001
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
7076
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `steps`, `steps-2`, … in render order, so every section on a page has its own."
|
|
7002
7077
|
},
|
|
7003
7078
|
"currentLabel": {
|
|
7004
7079
|
"type": "string",
|
|
@@ -7167,7 +7242,9 @@
|
|
|
7167
7242
|
"Button.astro",
|
|
7168
7243
|
"Section.astro",
|
|
7169
7244
|
"SectionHeader.astro",
|
|
7245
|
+
"instance-id.ts",
|
|
7170
7246
|
"measured.ts",
|
|
7247
|
+
"muted.ts",
|
|
7171
7248
|
"safe-url.ts",
|
|
7172
7249
|
"types.ts"
|
|
7173
7250
|
],
|
|
@@ -7423,9 +7500,11 @@
|
|
|
7423
7500
|
"KitImage.astro",
|
|
7424
7501
|
"Section.astro",
|
|
7425
7502
|
"image.ts",
|
|
7503
|
+
"instance-id.ts",
|
|
7426
7504
|
"measured.ts",
|
|
7427
|
-
"
|
|
7505
|
+
"muted.ts",
|
|
7428
7506
|
"safe-url.ts",
|
|
7507
|
+
"social.ts",
|
|
7429
7508
|
"types.ts"
|
|
7430
7509
|
],
|
|
7431
7510
|
"variants": {
|
|
@@ -7546,7 +7625,7 @@
|
|
|
7546
7625
|
"id": {
|
|
7547
7626
|
"type": "string",
|
|
7548
7627
|
"content": false,
|
|
7549
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
7628
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `team`, `team-2`, … in render order, so every section on a page has its own."
|
|
7550
7629
|
},
|
|
7551
7630
|
"bioClamp": {
|
|
7552
7631
|
"type": "integer",
|
|
@@ -7730,7 +7809,9 @@
|
|
|
7730
7809
|
"KitImage.astro",
|
|
7731
7810
|
"Section.astro",
|
|
7732
7811
|
"image.ts",
|
|
7812
|
+
"instance-id.ts",
|
|
7733
7813
|
"measured.ts",
|
|
7814
|
+
"muted.ts",
|
|
7734
7815
|
"safe-url.ts",
|
|
7735
7816
|
"types.ts"
|
|
7736
7817
|
],
|
|
@@ -7972,7 +8053,7 @@
|
|
|
7972
8053
|
"id": {
|
|
7973
8054
|
"type": "string",
|
|
7974
8055
|
"content": false,
|
|
7975
|
-
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
8056
|
+
"description": "Anchor id of the section; the heading gets `<id>-title`. Without one, `testimonial`, `testimonial-2`, … in render order, so every section on a page has its own."
|
|
7976
8057
|
},
|
|
7977
8058
|
"ratingLabel": {
|
|
7978
8059
|
"type": "string",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// their order.
|
|
5
5
|
import { tv } from 'tailwind-variants'
|
|
6
6
|
import { measuredFrame } from './measured'
|
|
7
|
+
import { mutedAccent, mutedBrand, mutedInverse, mutedReset } from './muted'
|
|
7
8
|
import { safeSrc } from './safe-url'
|
|
8
9
|
import type { MeasuredStyle } from './types'
|
|
9
10
|
|
|
@@ -11,13 +12,14 @@ const section = tv({
|
|
|
11
12
|
base: '',
|
|
12
13
|
variants: {
|
|
13
14
|
tone: {
|
|
14
|
-
default:
|
|
15
|
-
muted:
|
|
16
|
-
// A dark band reads the accent's text colour made for it (`--color-accent-text-inverse`)
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
default: `bg-surface text-ink ${mutedReset}`,
|
|
16
|
+
muted: `bg-surface-muted text-ink ${mutedReset}`,
|
|
17
|
+
// A dark band reads the accent's text colour made for it (`--color-accent-text-inverse`), and its quieter text
|
|
18
|
+
// the muted role made for it (`muted.ts`).
|
|
19
|
+
inverse: `bg-ink text-surface [--color-accent-text:var(--color-accent-text-inverse)] ${mutedInverse}`,
|
|
20
|
+
accent: `bg-accent text-accent-ink ${mutedAccent}`,
|
|
19
21
|
// The site's brand colour where it is not the accent (a band the source paints in the builder's own colour).
|
|
20
|
-
brand:
|
|
22
|
+
brand: `bg-brand text-brand-ink ${mutedBrand}`,
|
|
21
23
|
// No fill of its own: the page's background and text colour show through.
|
|
22
24
|
none: '',
|
|
23
25
|
},
|
|
@@ -42,15 +44,17 @@ interface Props {
|
|
|
42
44
|
id?: string | undefined
|
|
43
45
|
class?: string | undefined
|
|
44
46
|
'aria-labelledby'?: string | undefined
|
|
47
|
+
/** The component this section is (`hero`, `card-grid`): `data-kit-section`'s value, the hook a theme or a design layer styles one kind of section by. */
|
|
48
|
+
kit?: string | undefined
|
|
45
49
|
/** The source section's measured style (`MeasuredStyle`): frame width, padding, fill, image, columns, gap. Wins over `tone`. */
|
|
46
50
|
measured?: MeasuredStyle | undefined
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
const { tone: given, spacing, width = 'page', as: Tag = 'section', id, class: className, 'aria-labelledby': labelledBy, measured } = Astro.props
|
|
53
|
+
const { tone: given, spacing, width = 'page', as: Tag = 'section', id, class: className, 'aria-labelledby': labelledBy, kit, measured } = Astro.props
|
|
50
54
|
const m = measuredFrame(measured)
|
|
51
55
|
// A measured fill is the section's own colour, its text set by the measured tone; a tone alone picks the kit's band.
|
|
52
56
|
const tone = m.color ? 'none' : m.tone === 'dark' ? 'inverse' : m.tone === 'light' ? 'default' : given
|
|
53
|
-
const text = m.color ? (m.tone === 'dark' ?
|
|
57
|
+
const text = m.color ? (m.tone === 'dark' ? `text-surface [--color-accent-text:var(--color-accent-text-inverse)] ${mutedInverse}` : `text-ink ${mutedReset}`) : undefined
|
|
54
58
|
const layered = m.image || m.overlay ? 'relative isolate overflow-hidden' : undefined
|
|
55
59
|
const style = [m.color && `background-color: ${m.color}`, m.style].filter(Boolean).join('; ') || undefined
|
|
56
60
|
// The reading column reads its own width token.
|
|
@@ -58,7 +62,7 @@ const frameStyle = width === 'prose' ? m.frameStyle?.replace('--container-page:'
|
|
|
58
62
|
const backdrop = safeSrc(m.image?.src)
|
|
59
63
|
const dataTone = m.color ? (m.tone === 'dark' ? 'inverse' : 'default') : tone ?? 'default'
|
|
60
64
|
---
|
|
61
|
-
<Tag id={id} aria-labelledby={labelledBy} class={section({ tone, spacing, class: [text, layered, className].filter(Boolean).join(' ') || undefined })} style={style} data-tone={dataTone} data-kit-section data-kit-spacing={spacing ?? 'md'} data-kit-measured={measured ? '' : undefined}>
|
|
65
|
+
<Tag id={id} aria-labelledby={labelledBy} class={section({ tone, spacing, class: [text, layered, className].filter(Boolean).join(' ') || undefined })} style={style} data-tone={dataTone} data-kit-section={kit ?? ''} data-kit-spacing={spacing ?? 'md'} data-kit-measured={measured ? '' : undefined}>
|
|
62
66
|
{m.image && backdrop && <img src={backdrop} alt="" loading="lazy" decoding="async" class="absolute inset-0 -z-10 h-full w-full" style={`object-fit: ${m.image.fit}${m.image.position ? `; object-position: ${m.image.position}` : ''}`} />}
|
|
63
67
|
{m.overlay && <div class="absolute inset-0 -z-10" style={`background: ${m.overlay}`} aria-hidden="true" />}
|
|
64
68
|
{width === 'none' ? <slot /> : <div class={width === 'prose' ? 'container-prose' : 'container-page'} style={frameStyle}><slot /></div>}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// section components that have no header of their own use it; with none of the four given it draws nothing.
|
|
4
4
|
import Button from './Button.astro'
|
|
5
5
|
import type { ActionInput } from './types'
|
|
6
|
+
import { muted } from './muted'
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
heading?: string | undefined
|
|
@@ -19,9 +20,9 @@ const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
|
19
20
|
---
|
|
20
21
|
{(heading || intro || eyebrow || actions.length > 0) && (
|
|
21
22
|
<header class="mb-10 flex max-w-2xl flex-col gap-3">
|
|
22
|
-
{eyebrow && <p class=
|
|
23
|
+
{eyebrow && <p class:list={['text-sm font-semibold tracking-wide uppercase', muted]}>{eyebrow}</p>}
|
|
23
24
|
{heading && <Heading id={headingId} class="text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl">{heading}</Heading>}
|
|
24
|
-
{intro && <p class=
|
|
25
|
+
{intro && <p class:list={['text-lg', muted]} data-kit-text>{intro}</p>}
|
|
25
26
|
{actions.length > 0 && <div class="mt-2 flex flex-wrap gap-3">{actions.map(action => <Button action={action} />)}</div>}
|
|
26
27
|
</header>
|
|
27
28
|
)}
|
|
@@ -4,14 +4,18 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Each island is a root the Behavior component prints: `data-cr-behavior` names the kind, and the item, title and
|
|
6
6
|
// panel selectors come from the engine (`data-cr-items`, `data-cr-title`, `data-cr-panel`), never from the source
|
|
7
|
-
// (the
|
|
8
|
-
// `details`, `hidden` and those attributes
|
|
7
|
+
// (the engine's island pass strips every `data-cr-*` the source markup carried before it wraps it). The module reads only ARIA,
|
|
8
|
+
// `details`, `hidden` and those attributes, plus `data-cr-video` on a link the engine wrote after that strip (a source
|
|
9
|
+
// video player kept as a poster link to its page).
|
|
9
10
|
//
|
|
10
11
|
// Rules (t13's PR-E checklist §4): text goes in through `textContent` only, never `innerHTML` or its family; no
|
|
11
12
|
// `eval`, `Function`, `fetch` or `location`; every selector is scoped to its island root and run in try/catch (a
|
|
12
13
|
// selector the browser refuses leaves that island static); `init` is idempotent; anything that fails leaves the
|
|
13
14
|
// island as it was printed: readable, every item in reading order. Reduced motion is honoured.
|
|
14
15
|
|
|
16
|
+
import { playerFrame, plainClick } from './embed-frame'
|
|
17
|
+
import { videoTarget } from './embed-target'
|
|
18
|
+
|
|
15
19
|
type Kind = 'disclosure' | 'tablist' | 'carousel' | 'dialog' | 'countup' | 'toggle' | 'filter' | 'anchor' | 'lightbox'
|
|
16
20
|
|
|
17
21
|
const KINDS: ReadonlySet<string> = new Set<Kind>(['disclosure', 'tablist', 'carousel', 'dialog', 'countup', 'toggle', 'filter', 'anchor', 'lightbox'])
|
|
@@ -232,6 +236,35 @@ function lightbox(root: HTMLElement): void {
|
|
|
232
236
|
}
|
|
233
237
|
}
|
|
234
238
|
|
|
239
|
+
/**
|
|
240
|
+
* A source video player the engine kept as a link to its page (`<a href=watch-url data-cr-video="youtube:<id>">`, a
|
|
241
|
+
* poster image inside): a plain click plays it in place. Nothing loads before the click, and the player's address is
|
|
242
|
+
* built from the checked provider and id (embed-target.ts), never from the link's `href` or the attribute's text: an
|
|
243
|
+
* id the parser refuses leaves the link as it is, opening the provider's page. The player takes the link's own box,
|
|
244
|
+
* so nothing around it moves.
|
|
245
|
+
*/
|
|
246
|
+
function video(root: HTMLElement): void {
|
|
247
|
+
for (const link of within(root, 'a[href][data-cr-video]') as HTMLAnchorElement[]) {
|
|
248
|
+
// Only `youtube:<id>` / `vimeo:<id>`, the form the engine writes: never an address, so no text of the attribute can
|
|
249
|
+
// reach the frame. One inside a nested root came with that (spoofed) root's markup: never ours to wire.
|
|
250
|
+
const value = link.dataset.crVideo ?? ''
|
|
251
|
+
const found = /^(?:youtube|vimeo):[\w-]+$/.test(value) && link.parentElement?.closest('[data-cr-behavior]') === root ? videoTarget(value) : null
|
|
252
|
+
if (!found) continue
|
|
253
|
+
link.addEventListener('click', (event) => {
|
|
254
|
+
if (!plainClick(event)) return
|
|
255
|
+
// A poster's own box; a link with no picture (a title only) plays at 16:9, never as tall as one line of text.
|
|
256
|
+
const box = link.querySelector('img') ? link.getBoundingClientRect() : null
|
|
257
|
+
const name = link.getAttribute('aria-label') || link.textContent?.trim() || link.querySelector('img')?.alt || found.provider
|
|
258
|
+
const frame = playerFrame(found.src, name, 'cr-behavior-video')
|
|
259
|
+
if (!frame) return
|
|
260
|
+
event.preventDefault()
|
|
261
|
+
Object.assign(frame.style, { display: 'block', width: '100%', height: 'auto', border: '0', aspectRatio: box && box.width > 0 && box.height > 0 ? `${box.width} / ${box.height}` : '16 / 9' })
|
|
262
|
+
link.replaceWith(frame)
|
|
263
|
+
frame.focus()
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
235
268
|
const WIRE: Record<Kind, (root: HTMLElement) => void> = { disclosure, tablist, carousel, dialog, countup, toggle, filter, anchor, lightbox }
|
|
236
269
|
|
|
237
270
|
/** Wires every island on the page once. Safe to call again (a view transition, a second script copy). */
|
|
@@ -247,6 +280,9 @@ export function initBehaviors(scope: ParentNode = document): void {
|
|
|
247
280
|
catch {
|
|
248
281
|
// Left as printed: the content is all there, it just does not move.
|
|
249
282
|
}
|
|
283
|
+
// A video link plays in place in any island: a player sat in a tab, a slide or a panel as often as on its own.
|
|
284
|
+
try { video(root) }
|
|
285
|
+
catch { /* its links stay links */ }
|
|
250
286
|
// A dialog opener inside an island of another kind (a popup button in a tab): opened too, never left dead, even
|
|
251
287
|
// where the island's own wiring failed.
|
|
252
288
|
if (kind !== 'dialog') {
|