domma-cms 1.8.43 → 1.8.45
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/CHANGELOG.md +9 -0
- package/admin/js/templates/docs/usage-blocks.html +29 -0
- package/docs/starter-blocks.md +203 -0
- package/package.json +1 -1
- package/server/blocks/catalog.json +98 -0
- package/server/blocks/domma-block-banner.css +65 -0
- package/server/blocks/domma-block-banner.html +22 -0
- package/server/blocks/domma-block-banner.meta.json +7 -0
- package/server/blocks/domma-block-contact.css +18 -0
- package/server/blocks/domma-block-contact.html +15 -0
- package/server/blocks/domma-block-contact.meta.json +7 -0
- package/server/blocks/domma-block-cta.css +8 -0
- package/server/blocks/domma-block-cta.html +17 -0
- package/server/blocks/domma-block-cta.meta.json +7 -0
- package/server/blocks/domma-block-faq.css +13 -0
- package/server/blocks/domma-block-faq.html +17 -0
- package/server/blocks/domma-block-faq.meta.json +7 -0
- package/server/blocks/domma-block-features.css +18 -0
- package/server/blocks/domma-block-features.html +19 -0
- package/server/blocks/domma-block-features.meta.json +7 -0
- package/server/blocks/domma-block-hero.css +15 -0
- package/server/blocks/domma-block-hero.html +13 -0
- package/server/blocks/domma-block-hero.meta.json +7 -0
- package/server/blocks/domma-block-image.css +8 -0
- package/server/blocks/domma-block-image.html +12 -0
- package/server/blocks/domma-block-image.meta.json +7 -0
- package/server/blocks/domma-block-logos.css +13 -0
- package/server/blocks/domma-block-logos.html +18 -0
- package/server/blocks/domma-block-logos.meta.json +7 -0
- package/server/blocks/domma-block-pricing-card.css +20 -0
- package/server/blocks/domma-block-pricing-card.html +15 -0
- package/server/blocks/domma-block-pricing-card.meta.json +7 -0
- package/server/blocks/domma-block-service-card.css +17 -0
- package/server/blocks/domma-block-service-card.html +15 -0
- package/server/blocks/domma-block-service-card.meta.json +7 -0
- package/server/blocks/domma-block-split.css +26 -0
- package/server/blocks/domma-block-split.html +22 -0
- package/server/blocks/domma-block-split.meta.json +7 -0
- package/server/blocks/domma-block-stats.css +20 -0
- package/server/blocks/domma-block-stats.html +16 -0
- package/server/blocks/domma-block-stats.meta.json +7 -0
- package/server/blocks/domma-block-steps.css +19 -0
- package/server/blocks/domma-block-steps.html +17 -0
- package/server/blocks/domma-block-steps.meta.json +7 -0
- package/server/blocks/domma-block-team-card.css +13 -0
- package/server/blocks/domma-block-team-card.html +14 -0
- package/server/blocks/domma-block-team-card.meta.json +7 -0
- package/server/blocks/domma-block-testimonial.css +13 -0
- package/server/blocks/domma-block-testimonial.html +12 -0
- package/server/blocks/domma-block-testimonial.meta.json +7 -0
- package/server/blocks/domma-block-text.css +9 -0
- package/server/blocks/domma-block-text.html +11 -0
- package/server/blocks/domma-block-text.meta.json +7 -0
- package/server/services/blocks.js +2 -0
- package/server/services/markdown.js +25 -3
- package/server/services/starter-blocks.js +18 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.db-hero {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
padding: clamp(2rem, 6vw, 5rem) 1.25rem; text-align: center;
|
|
6
|
+
}
|
|
7
|
+
.db-hero-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
8
|
+
.db-hero-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
9
|
+
.db-hero-blurb:empty { display: none; }
|
|
10
|
+
.db-hero-heading { max-width: 54rem; margin-inline: auto; font-size: clamp(2rem, 5vw, 4rem); }
|
|
11
|
+
.db-hero-blurb { max-width: 42rem; margin-inline: auto; font-size: 1.15rem; }
|
|
12
|
+
.db-hero a.db-hero-link { display: inline-block; max-width: 100%; box-sizing: border-box; margin-top: 1.5rem; padding: .8rem 1.25rem; border: 2px solid currentColor; border-radius: var(--dm-radius, .5rem); color: inherit; font-weight: 600; text-decoration: none; line-height: 1.4; }
|
|
13
|
+
.db-hero a.db-hero-link:hover { text-decoration: underline; }
|
|
14
|
+
.db-hero a.db-hero-link:focus-visible { outline: 3px solid currentColor; outline-offset: 4px; }
|
|
15
|
+
.db-hero a.db-hero-link[href=""], .db-hero a.db-hero-link:not([href]), .db-hero a.db-hero-link:empty { display: none; }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-hero - A centred page introduction with an optional button.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Main page heading (h1; use once per page)
|
|
6
|
+
blurb - Short introduction, as plain text
|
|
7
|
+
link - Optional button destination
|
|
8
|
+
label - Optional button text
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
[domma-block-hero header="Welcome to your next chapter" blurb="Say what you do and who you help." link="/contact" label="Let us help" /]
|
|
12
|
+
-->
|
|
13
|
+
<section class="db-hero"><h1 class="db-hero-heading">{{header}}</h1><p class="db-hero-blurb">{{blurb}}</p><a class="db-hero-link db-hero-btn" href="{{link}}">{{label}}</a></section>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
.db-image {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
}
|
|
6
|
+
.db-image img { display: block; width: 100%; height: auto; border-radius: var(--dm-radius-xl, 1rem); }
|
|
7
|
+
.db-image figcaption { margin-top: .75rem; font-size: .95rem; line-height: 1.6; text-align: center; white-space: pre-line; }
|
|
8
|
+
.db-image img[src=""], .db-image img:not([src]), .db-image figcaption:empty { display: none; }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-image - An uncropped image with an optional caption.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
image - Image address from your Media library
|
|
6
|
+
alt - Image description; empty for a decorative image
|
|
7
|
+
caption - Optional image caption, as plain text
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
[domma-block-image image="/media/your-work.jpg" alt="Describe what visitors can see" caption="Add a little context to the image." /]
|
|
11
|
+
-->
|
|
12
|
+
<figure class="db-image"><img src="{{image}}" alt="{{alt}}" loading="lazy" decoding="async"><figcaption>{{caption}}</figcaption></figure>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.db-logos {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
text-align: center;
|
|
6
|
+
}
|
|
7
|
+
.db-logos-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
8
|
+
.db-logos-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
9
|
+
.db-logos-blurb:empty { display: none; }
|
|
10
|
+
.db-logos-heading:empty { display: none; }
|
|
11
|
+
.db-logos-grid { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2rem; padding: 1.5rem; }
|
|
12
|
+
.db-logos-grid img { display: block; width: 10rem; max-width: 100%; height: 5rem; object-fit: contain; }
|
|
13
|
+
.db-logos-grid img[src=""], .db-logos-grid img:not([src]) { display: none; }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-logos - A responsive row of up to four organisation logos.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Optional heading (h2)
|
|
6
|
+
image1 - Logo 1 image address
|
|
7
|
+
alt1 - Logo 1 organisation name
|
|
8
|
+
image2 - Logo 2 image address
|
|
9
|
+
alt2 - Logo 2 organisation name
|
|
10
|
+
image3 - Logo 3 image address
|
|
11
|
+
alt3 - Logo 3 organisation name
|
|
12
|
+
image4 - Logo 4 image address
|
|
13
|
+
alt4 - Logo 4 organisation name
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
[domma-block-logos header="Who we work with" image1="/media/partner-logo.svg" alt1="Partner name" /]
|
|
17
|
+
-->
|
|
18
|
+
<section class="db-logos"><h2 class="db-logos-heading">{{header}}</h2><div class="db-logos-grid"><img src="{{image1}}" alt="{{alt1}}" loading="lazy" decoding="async"><img src="{{image2}}" alt="{{alt2}}" loading="lazy" decoding="async"><img src="{{image3}}" alt="{{alt3}}" loading="lazy" decoding="async"><img src="{{image4}}" alt="{{alt4}}" loading="lazy" decoding="async"></div></section>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.db-price {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
padding: clamp(1.25rem, 3vw, 2rem);
|
|
6
|
+
border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5));
|
|
7
|
+
border-radius: var(--dm-radius-xl, 1rem);
|
|
8
|
+
background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
9
|
+
}
|
|
10
|
+
.db-price-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
11
|
+
.db-price-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
12
|
+
.db-price-blurb:empty { display: none; }
|
|
13
|
+
.db-price-heading { font-size: 1.4rem; }
|
|
14
|
+
.db-price-amount { margin: 0 0 .5rem; font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; line-height: 1.2; }
|
|
15
|
+
.db-price-period { margin: 0 0 1.5rem; line-height: 1.6; }
|
|
16
|
+
.db-price-period:empty { display: none; }
|
|
17
|
+
.db-price a.db-price-link { display: inline-block; max-width: 100%; box-sizing: border-box; margin-top: 1.5rem; padding: .8rem 1.25rem; border: 2px solid currentColor; border-radius: var(--dm-radius, .5rem); color: inherit; font-weight: 600; text-decoration: none; line-height: 1.4; }
|
|
18
|
+
.db-price a.db-price-link:hover { text-decoration: underline; }
|
|
19
|
+
.db-price a.db-price-link:focus-visible { outline: 3px solid currentColor; outline-offset: 4px; }
|
|
20
|
+
.db-price a.db-price-link[href=""], .db-price a.db-price-link:not([href]), .db-price a.db-price-link:empty { display: none; }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-pricing-card - A clear price or package card with an optional enquiry button.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Package heading (h3)
|
|
6
|
+
blurb - What is included, as plain text
|
|
7
|
+
price - Price text, including currency and any tax wording
|
|
8
|
+
period - Optional billing period or pricing note
|
|
9
|
+
link - Optional button destination
|
|
10
|
+
label - Optional button text
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
[domma-block-pricing-card header="Your package" price="Ask for a quote" blurb="Describe exactly what is included." link="/contact" label="Talk it through" /]
|
|
14
|
+
-->
|
|
15
|
+
<article class="db-price"><h3 class="db-price-heading">{{header}}</h3><p class="db-price-amount">{{price}}</p><p class="db-price-period">{{period}}</p><p class="db-price-blurb">{{blurb}}</p><a class="db-price-link db-price-btn" href="{{link}}">{{label}}</a></article>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.db-service {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
overflow: hidden; border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5)); border-radius: var(--dm-radius-xl, 1rem); background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
6
|
+
}
|
|
7
|
+
.db-service-image { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
|
|
8
|
+
.db-service-image[src=""], .db-service-image:not([src]) { display: none; }
|
|
9
|
+
.db-service-body { padding: 1.5rem; }
|
|
10
|
+
.db-service-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
11
|
+
.db-service-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
12
|
+
.db-service-blurb:empty { display: none; }
|
|
13
|
+
.db-service-heading { font-size: 1.4rem; }
|
|
14
|
+
.db-service a.db-service-link { display: inline-block; max-width: 100%; box-sizing: border-box; margin-top: 1.5rem; padding: .8rem 1.25rem; border: 2px solid currentColor; border-radius: var(--dm-radius, .5rem); color: inherit; font-weight: 600; text-decoration: none; line-height: 1.4; }
|
|
15
|
+
.db-service a.db-service-link:hover { text-decoration: underline; }
|
|
16
|
+
.db-service a.db-service-link:focus-visible { outline: 3px solid currentColor; outline-offset: 4px; }
|
|
17
|
+
.db-service a.db-service-link[href=""], .db-service a.db-service-link:not([href]), .db-service a.db-service-link:empty { display: none; }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-service-card - A reusable service card with an optional photo and button.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Card heading (h3)
|
|
6
|
+
blurb - Service description, as plain text
|
|
7
|
+
image - Image address from your Media library
|
|
8
|
+
alt - Image description; empty for a decorative image
|
|
9
|
+
link - Optional button destination
|
|
10
|
+
label - Optional button text
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
[domma-block-service-card header="Your service" blurb="Explain the benefit in a sentence or two." link="/services" label="Find out more" /]
|
|
14
|
+
-->
|
|
15
|
+
<article class="db-service"><img class="db-service-image" src="{{image}}" alt="{{alt}}" loading="lazy" decoding="async"><div class="db-service-body"><h3 class="db-service-heading">{{header}}</h3><p class="db-service-blurb">{{blurb}}</p><a class="db-service-link db-service-btn" href="{{link}}">{{label}}</a></div></article>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.db-split {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
display: grid;
|
|
4
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
5
|
+
align-items: center;
|
|
6
|
+
gap: clamp(1.5rem, 4vw, 3.5rem);
|
|
7
|
+
color: var(--dm-text, #152832);
|
|
8
|
+
}
|
|
9
|
+
.db-split-image {
|
|
10
|
+
display: block;
|
|
11
|
+
width: 100%;
|
|
12
|
+
aspect-ratio: 4 / 3;
|
|
13
|
+
object-fit: cover;
|
|
14
|
+
border-radius: var(--dm-radius-xl, 1rem);
|
|
15
|
+
}
|
|
16
|
+
.db-split-image[src=""], .db-split-image:not([src]) { display: none; }
|
|
17
|
+
.db-split-body { min-width: 0; padding: .5rem 0; }
|
|
18
|
+
.db-split-heading { margin: 0 0 1rem; font-size: clamp(1.75rem, 3vw, 2.5rem); line-height: 1.2; color: inherit; overflow-wrap: anywhere; }
|
|
19
|
+
.db-split-blurb { margin: 0; font-size: 1.1rem; line-height: 1.75; white-space: pre-line; overflow-wrap: anywhere; }
|
|
20
|
+
.db-split a.db-split-link { display: inline-block; margin-top: 1.5rem; padding: .8rem 1.25rem; border: 2px solid currentColor; border-radius: var(--dm-radius, .5rem); color: inherit; font-weight: 600; text-decoration: none; overflow-wrap: anywhere; }
|
|
21
|
+
.db-split a.db-split-link:hover { text-decoration: underline; }
|
|
22
|
+
.db-split a.db-split-link:focus-visible { outline: 3px solid currentColor; outline-offset: 4px; }
|
|
23
|
+
.db-split a.db-split-link[href=""], .db-split a.db-split-link:not([href]), .db-split a.db-split-link:empty { display: none; }
|
|
24
|
+
@media (max-width: 767px) {
|
|
25
|
+
.db-split { grid-template-columns: minmax(0, 1fr); }
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-split - A simple image and text section that stacks on phones.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Section heading (h2)
|
|
6
|
+
blurb - Section introduction, as plain text
|
|
7
|
+
image - Image address from your media library
|
|
8
|
+
alt - Description of the image
|
|
9
|
+
link - Optional button destination
|
|
10
|
+
label - Optional button text
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
[domma-block-split header="Made for your business" blurb="Tell visitors how you can help." image="/media/about.jpg" alt="Our workspace" link="/about" label="Meet us" /]
|
|
14
|
+
-->
|
|
15
|
+
<section class="db-split">
|
|
16
|
+
<img class="db-split-image" src="{{image}}" alt="{{alt}}" loading="lazy" decoding="async">
|
|
17
|
+
<div class="db-split-body">
|
|
18
|
+
<h2 class="db-split-heading">{{header}}</h2>
|
|
19
|
+
<p class="db-split-blurb">{{blurb}}</p>
|
|
20
|
+
<a class="db-split-link db-split-btn" href="{{link}}">{{label}}</a>
|
|
21
|
+
</div>
|
|
22
|
+
</section>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.db-stats {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
text-align: center;
|
|
6
|
+
}
|
|
7
|
+
.db-stats-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
8
|
+
.db-stats-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
9
|
+
.db-stats-blurb:empty { display: none; }
|
|
10
|
+
.db-stats-heading:empty { display: none; }
|
|
11
|
+
.db-stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 1.25rem; margin: 0; }
|
|
12
|
+
.db-stats-item { display: flex; flex-direction: column; gap: .5rem;
|
|
13
|
+
padding: clamp(1.25rem, 3vw, 2rem);
|
|
14
|
+
border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5));
|
|
15
|
+
border-radius: var(--dm-radius-xl, 1rem);
|
|
16
|
+
background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
17
|
+
}
|
|
18
|
+
.db-stats-item dt { order: 2; font-weight: 400; line-height: 1.6; }
|
|
19
|
+
.db-stats-item dd { order: 1; margin: 0; font-size: clamp(2rem, 4vw, 3rem); font-weight: 700; line-height: 1.2; }
|
|
20
|
+
.db-stats-item:has(dd:empty) { display: none; }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-stats - Up to three useful figures with clear labels.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Optional section heading (h2)
|
|
6
|
+
value1 - Figure 1, including any unit
|
|
7
|
+
label1 - Figure 1 explanation
|
|
8
|
+
value2 - Figure 2, including any unit
|
|
9
|
+
label2 - Figure 2 explanation
|
|
10
|
+
value3 - Figure 3, including any unit
|
|
11
|
+
label3 - Figure 3 explanation
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
[domma-block-stats header="A few useful facts" value1="Your figure" label1="Explain what it represents" /]
|
|
15
|
+
-->
|
|
16
|
+
<section class="db-stats"><h2 class="db-stats-heading">{{header}}</h2><dl class="db-stats-grid"><div class="db-stats-item"><dt>{{label1}}</dt><dd>{{value1}}</dd></div><div class="db-stats-item"><dt>{{label2}}</dt><dd>{{value2}}</dd></div><div class="db-stats-item"><dt>{{label3}}</dt><dd>{{value3}}</dd></div></dl></section>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.db-steps {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
}
|
|
6
|
+
.db-steps-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
7
|
+
.db-steps-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
8
|
+
.db-steps-blurb:empty { display: none; }
|
|
9
|
+
.db-steps-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); gap: 1.25rem; counter-reset: db-step; list-style: none; margin: 1.5rem 0 0; padding: 0; }
|
|
10
|
+
.db-steps-item { counter-increment: db-step;
|
|
11
|
+
padding: clamp(1.25rem, 3vw, 2rem);
|
|
12
|
+
border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5));
|
|
13
|
+
border-radius: var(--dm-radius-xl, 1rem);
|
|
14
|
+
background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
15
|
+
}
|
|
16
|
+
.db-steps-item::before { content: counter(db-step, decimal-leading-zero); display: block; margin-bottom: 1rem; font-size: 1.75rem; font-weight: 700; }
|
|
17
|
+
.db-steps-item h3 { margin: 0 0 .75rem; color: inherit; font-size: 1.25rem; }
|
|
18
|
+
.db-steps-item p { margin: 0; line-height: 1.7; white-space: pre-line; }
|
|
19
|
+
.db-steps-item:has(h3:empty) { display: none; }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-steps - A clear, numbered process with up to three steps.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Section heading (h2)
|
|
6
|
+
blurb - Introduction, as plain text
|
|
7
|
+
title1 - Step 1 heading
|
|
8
|
+
text1 - Step 1 description
|
|
9
|
+
title2 - Step 2 heading
|
|
10
|
+
text2 - Step 2 description
|
|
11
|
+
title3 - Step 3 heading
|
|
12
|
+
text3 - Step 3 description
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
[domma-block-steps header="Getting started" blurb="A simple way forward." title1="Say hello" text1="Tell us what you need." title2="Make a plan" text2="Agree the details together." title3="Get going" text3="Put your plan into action." /]
|
|
16
|
+
-->
|
|
17
|
+
<section class="db-steps"><h2 class="db-steps-heading">{{header}}</h2><p class="db-steps-blurb">{{blurb}}</p><ol class="db-steps-list"><li class="db-steps-item"><h3>{{title1}}</h3><p>{{text1}}</p></li><li class="db-steps-item"><h3>{{title2}}</h3><p>{{text2}}</p></li><li class="db-steps-item"><h3>{{title3}}</h3><p>{{text3}}</p></li></ol></section>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.db-team {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5)); border-radius: var(--dm-radius-xl, 1rem); overflow: hidden; background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
6
|
+
}
|
|
7
|
+
.db-team-image { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; }
|
|
8
|
+
.db-team-image[src=""], .db-team-image:not([src]) { display: none; }
|
|
9
|
+
.db-team-body { padding: 1.5rem; }
|
|
10
|
+
.db-team-name { margin: 0 0 .5rem; font-size: 1.4rem; color: inherit; }
|
|
11
|
+
.db-team-role { margin: 0 0 1rem; font-weight: 600; }
|
|
12
|
+
.db-team-blurb { margin: 0; white-space: pre-line; line-height: 1.7; }
|
|
13
|
+
.db-team-role:empty, .db-team-blurb:empty { display: none; }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-team-card - A team member photo, role and short introduction.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
name - Person name (h3)
|
|
6
|
+
role - Role or specialism
|
|
7
|
+
blurb - Short introduction, as plain text
|
|
8
|
+
image - Image address from your Media library
|
|
9
|
+
alt - Image description; empty for a decorative image
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
[domma-block-team-card name="Team member" role="Role or specialism" blurb="A short, friendly introduction." image="/media/team-member.jpg" alt="Portrait of your team member" /]
|
|
13
|
+
-->
|
|
14
|
+
<article class="db-team"><img class="db-team-image" src="{{image}}" alt="{{alt}}" loading="lazy" decoding="async"><div class="db-team-body"><h3 class="db-team-name">{{name}}</h3><p class="db-team-role">{{role}}</p><p class="db-team-blurb">{{blurb}}</p></div></article>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.db-testimonial {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
padding: clamp(1.25rem, 3vw, 2rem);
|
|
6
|
+
border: 1px solid var(--dm-card-border, var(--dm-border, #d5e0e5));
|
|
7
|
+
border-radius: var(--dm-radius-xl, 1rem);
|
|
8
|
+
background: var(--dm-card-bg, var(--dm-surface, #f5f8fa));
|
|
9
|
+
}
|
|
10
|
+
.db-testimonial-quote { margin: 0; padding: 0; border: 0; color: inherit; font-size: clamp(1.15rem, 2vw, 1.6rem); line-height: 1.65; white-space: pre-line; }
|
|
11
|
+
.db-testimonial-attribution { display: grid; gap: .3rem; margin-top: 1.25rem; }
|
|
12
|
+
.db-testimonial-attribution span { font-size: .95rem; }
|
|
13
|
+
.db-testimonial-attribution strong:empty, .db-testimonial-attribution span:empty, .db-testimonial-attribution:has(strong:empty):has(span:empty) { display: none; }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-testimonial - A customer quotation with an optional attribution.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
quote - An approved customer quotation, as plain text
|
|
6
|
+
name - Customer name
|
|
7
|
+
detail - Optional role or business
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
[domma-block-testimonial quote="Replace this with a customer quotation you have permission to share." name="Customer name" detail="Business or role" /]
|
|
11
|
+
-->
|
|
12
|
+
<figure class="db-testimonial"><blockquote class="db-testimonial-quote">{{quote}}</blockquote><figcaption class="db-testimonial-attribution"><strong>{{name}}</strong><span>{{detail}}</span></figcaption></figure>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
.db-text {
|
|
2
|
+
margin-block: 1.5rem;
|
|
3
|
+
color: var(--dm-text, #152832);
|
|
4
|
+
overflow-wrap: anywhere;
|
|
5
|
+
max-width: 48rem; margin-inline: auto; padding-block: 1rem;
|
|
6
|
+
}
|
|
7
|
+
.db-text-heading { margin: 0 0 1rem; color: inherit; font-size: clamp(1.6rem, 3vw, 2.4rem); line-height: 1.2; }
|
|
8
|
+
.db-text-blurb { margin: 0; line-height: 1.75; white-space: pre-line; }
|
|
9
|
+
.db-text-blurb:empty { display: none; }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
domma-block-text - A readable text section for your story or an introduction.
|
|
3
|
+
|
|
4
|
+
Parameters:
|
|
5
|
+
header - Section heading (h2)
|
|
6
|
+
blurb - Introduction, as plain text
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
[domma-block-text header="Our story" blurb="Share a little about your business and the people behind it." /]
|
|
10
|
+
-->
|
|
11
|
+
<section class="db-text"><h2 class="db-text-heading">{{header}}</h2><p class="db-text-blurb">{{blurb}}</p></section>
|
|
@@ -7,6 +7,7 @@ import fs from 'fs/promises';
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import {fileURLToPath} from 'url';
|
|
9
9
|
import * as cache from './cache/index.js';
|
|
10
|
+
import {seedStarterBlocks} from './starter-blocks.js';
|
|
10
11
|
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
const BLOCKS_DIR = path.resolve(__dirname, '../../content/blocks');
|
|
@@ -427,6 +428,7 @@ export async function deleteBlock(name) {
|
|
|
427
428
|
* Skips any file that already exists to preserve user customisations.
|
|
428
429
|
*/
|
|
429
430
|
export async function seedDefaultBlocks() {
|
|
431
|
+
await seedStarterBlocks(BLOCKS_DIR);
|
|
430
432
|
for (const [name, content] of Object.entries(BLOCKS)) {
|
|
431
433
|
await seedBlock(name, content);
|
|
432
434
|
}
|
|
@@ -423,10 +423,29 @@ export function buildBlockStyleTag(blockName, css) {
|
|
|
423
423
|
* @param {object} e - Collection entry { id, data, meta }
|
|
424
424
|
* @returns {string}
|
|
425
425
|
*/
|
|
426
|
+
/**
|
|
427
|
+
* A filled block goes into Markdown as HTML, and a blank line ends an HTML block:
|
|
428
|
+
* what follows, indented, marked then shows as code - raw tags on the page. A
|
|
429
|
+
* placeholder alone on its line with nothing to fill it (` {{bio}}` with no
|
|
430
|
+
* bio) made exactly that blank line, so such a line goes. The template's OWN
|
|
431
|
+
* blank lines stay: they are how a template hands a Markdown value to marked.
|
|
432
|
+
* Each line is filled by itself, in one pass as before.
|
|
433
|
+
*/
|
|
434
|
+
function fillTemplateLines(template, fill) {
|
|
435
|
+
const PLACEHOLDER = /\{\{\{(\w+)\}\}\}|\{\{([\w_]+)(?::([^{}]+))?\}\}/g;
|
|
436
|
+
const out = [];
|
|
437
|
+
for (const line of template.split('\n')) {
|
|
438
|
+
const filled = line.replace(PLACEHOLDER, fill);
|
|
439
|
+
if (!filled.trim() && line.trim() && /\{\{/.test(line)) continue;
|
|
440
|
+
out.push(filled);
|
|
441
|
+
}
|
|
442
|
+
return out.join('\n');
|
|
443
|
+
}
|
|
444
|
+
|
|
426
445
|
function interpolateBlockTemplate(blockTemplate, e) {
|
|
427
446
|
// One pass: a raw value put in (pre-rendered HTML) is never read again for
|
|
428
447
|
// placeholders - text that shows "{{title}}" keeps it.
|
|
429
|
-
return blockTemplate
|
|
448
|
+
return fillTemplateLines(blockTemplate, (_, raw, key, fmt) => {
|
|
430
449
|
// Entry values never carry a shortcode into the page (inertShortcodes);
|
|
431
450
|
// the template's own markup is untouched.
|
|
432
451
|
if (raw !== undefined) return inertShortcodes(String(e.data?.[raw] ?? ''));
|
|
@@ -510,7 +529,7 @@ function renderCollectionBlocks(entries, blockTemplate, emptyMsg, ctaOpts, cols,
|
|
|
510
529
|
function renderBlockFromAttrs(tmpl, attrs, blockName = '') {
|
|
511
530
|
// Triple-brace raw pass-through and `{{field}}` (optional `:format`, e.g. `{{date:DD MMM YYYY}}`)
|
|
512
531
|
// in one pass, so a raw value is never read again for placeholders.
|
|
513
|
-
const rendered = tmpl
|
|
532
|
+
const rendered = fillTemplateLines(tmpl, (_, rawKey, key, fmt) => {
|
|
514
533
|
if (rawKey !== undefined) return String(attrs[rawKey] ?? '');
|
|
515
534
|
const raw = attrs[key] ?? '';
|
|
516
535
|
if (fmt) {
|
|
@@ -1967,7 +1986,10 @@ export function scrubCodeRegions(markdown, preserveIndented = false) {
|
|
|
1967
1986
|
// Inline code spans (single or multiple backticks, non-greedy)
|
|
1968
1987
|
scrubbed = scrubbed.replace(/`+[^`\n]+`+/g, placeholder);
|
|
1969
1988
|
|
|
1970
|
-
|
|
1989
|
+
// A stored region can hold an earlier placeholder of this same call - an indented
|
|
1990
|
+
// line (preserveIndented) holding an inline <code> - so what comes back is
|
|
1991
|
+
// restored too. Restoring it once left "SC1:0" on the page.
|
|
1992
|
+
const restore = (s) => s.replace(new RegExp(`\\x02SC${callId}:(\\d+)\\x03`, 'g'), (_, i) => restore(store[parseInt(i, 10)]));
|
|
1971
1993
|
return {scrubbed, restore};
|
|
1972
1994
|
}
|
|
1973
1995
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Editable starting points for new pages, copied without replacing site edits. */
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const sourceDir = fileURLToPath(new URL('../blocks/', import.meta.url));
|
|
7
|
+
|
|
8
|
+
export async function seedStarterBlocks(destination) {
|
|
9
|
+
await fs.mkdir(destination, {recursive: true});
|
|
10
|
+
for (const name of await fs.readdir(sourceDir)) {
|
|
11
|
+
if (!/^domma-block-[a-z-]+\.(html|css|meta\.json)$/.test(name)) continue;
|
|
12
|
+
try {
|
|
13
|
+
await fs.copyFile(path.join(sourceDir, name), path.join(destination, name), fs.constants.COPYFILE_EXCL);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
if (error.code !== 'EEXIST') throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|