@sleekcms/sync 2.10.0 → 2.11.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/dist/AGENT.md +99 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,7 +205,7 @@ No manual deploy. No waiting. SleekCMS rebuilds on every sync and the preview re
|
|
|
205
205
|
│ ├── css/<name>.css # Stylesheets
|
|
206
206
|
│ └── js/<name>.js # Scripts
|
|
207
207
|
│
|
|
208
|
-
└── setup.json # Site setup: default page title, Tailwind on/off, sitemap on/off
|
|
208
|
+
└── setup.json # Site setup: default page title, Tailwind on/off, sitemap on/off, cookie consent, analytics ids
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
### File naming
|
|
@@ -414,18 +414,34 @@ Content lives in `src/content/` as plain JSON. Edit any content file and save
|
|
|
414
414
|
|
|
415
415
|
## Site setup (`src/setup.json`)
|
|
416
416
|
|
|
417
|
-
Site-wide decisions made once — the default page title, whether Tailwind is compiled,
|
|
417
|
+
Site-wide decisions made once — the default page title, whether Tailwind is compiled, whether a sitemap is published, the cookie consent switch, and analytics tag ids — live in `src/setup.json`, mirrored from **Settings → Website** in the CMS. The file is JSON with a comment above each key explaining it:
|
|
418
418
|
|
|
419
419
|
```json5
|
|
420
420
|
{
|
|
421
421
|
title: "Acme Dental", // default <title> for pages without their own; null = the site name
|
|
422
422
|
tw_enabled: true, // compile src/public/css/tailwind.css and inject it into every page
|
|
423
423
|
sitemap_enabled: false, // publish sitemap.xml + robots.txt on every build
|
|
424
|
+
cookie_consent: false, // consent banner; holds analytics + data-sleekcms-consent scripts until accepted
|
|
425
|
+
analytics: { // tag ids injected into every page; include only the ones you use
|
|
426
|
+
ga4: "G-XXXXXXXXXX",
|
|
427
|
+
},
|
|
424
428
|
}
|
|
425
429
|
```
|
|
426
430
|
|
|
427
431
|
Edit the values and save — it syncs like any other file. Keys you leave out keep their current value. Which pages are excluded from the sitemap is managed in the CMS UI, not in this file.
|
|
428
432
|
|
|
433
|
+
## Cookie consent
|
|
434
|
+
|
|
435
|
+
Set `cookie_consent: true` in `src/setup.json` and tag any script or iframe that sets cookies with `data-sleekcms-consent="<category>"`. At build time SleekCMS neutralizes the tagged elements, injects a consent banner into every page, and activates each one only after the visitor accepts its category. The analytics ids in `setup.json` are tagged `analytics` automatically.
|
|
436
|
+
|
|
437
|
+
```ejs
|
|
438
|
+
<% script({ src: 'https://example.com/widget.js', 'data-sleekcms-consent': 'marketing' }) %>
|
|
439
|
+
<iframe data-sleekcms-consent="marketing" src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
|
|
440
|
+
<a data-sleekcms-consent href="#">Cookie settings</a> <!-- reopens the preferences panel; put one in the footer -->
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Category names are yours (`analytics`, `marketing`, …); the banner offers exactly the ones found on the site. An optional empty `<div data-sleekcms-consent data-position="bottom" data-theme="auto" data-policy-url="/privacy"></div>` in the layout customizes position (`bottom`, `bottom-left`, `bottom-right`, `modal`), theme, policy link, locale and button labels, and `--sleek-consent-bg/text/accent/on-accent/radius` CSS variables restyle it. The full reference is in the `AGENT.md` that `sleekcms sync` writes into your workspace.
|
|
444
|
+
|
|
429
445
|
## Images
|
|
430
446
|
|
|
431
447
|
Images are part of content, not a separate asset pipeline. Drop a shortcut string into any `image` field and the sync engine resolves it to a full `{ url, alt }` object on save:
|
package/dist/AGENT.md
CHANGED
|
@@ -96,7 +96,7 @@ content/entries/<key>+.json Collection entry — ONE file holding a JSON arr
|
|
|
96
96
|
content/images.json Site-level reusable images: handle → shortcut (§7)
|
|
97
97
|
content/videos.json Site-level videos: handle → pexels:/pixabay: ref (user uploads show as embed_url) (§7)
|
|
98
98
|
|
|
99
|
-
setup.json Site setup (JSON, comments allowed): default page title, Tailwind on/off, sitemap on/off — edit values in place, never delete
|
|
99
|
+
setup.json Site setup (JSON, comments allowed): default page title, Tailwind on/off, sitemap on/off, cookie consent on/off, analytics tag ids — edit values in place, never delete
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
**Collection storage differs by kind — do not confuse them:**
|
|
@@ -115,6 +115,29 @@ setup.json Site setup (JSON, comments allowed): default pag
|
|
|
115
115
|
| `title` | string or `null` | Default `<title>` for pages that don't set their own (via `title()` or the SEO block). `null` = the site name. |
|
|
116
116
|
| `tw_enabled` | boolean | `true` compiles `css/tailwind.css` and injects it into every page — required for any site styled with Tailwind utilities. |
|
|
117
117
|
| `sitemap_enabled` | boolean | `true` publishes `sitemap.xml` + `robots.txt` on every build. Turn it on for public sites and SEO work. |
|
|
118
|
+
| `cookie_consent` | boolean | `true` injects a consent banner into every page and holds the analytics tags, plus any script or iframe tagged `data-sleekcms-consent`, until the visitor accepts its category. A toggle only — banner text, position and styling are authored in HTML (§14). |
|
|
119
|
+
| `analytics` | object | Marketing/analytics tag ids injected into every published page. |
|
|
120
|
+
|
|
121
|
+
`analytics` is a flat map — include only the keys the site uses:
|
|
122
|
+
|
|
123
|
+
| Key | Id |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `ga4` | Google Analytics 4 measurement id (`"G-XXXXXXXXXX"`) |
|
|
126
|
+
| `gtm` | Google Tag Manager container id (`"GTM-XXXXXXX"`) |
|
|
127
|
+
| `meta_pixel` | Meta (Facebook) Pixel id (digits) |
|
|
128
|
+
| `google_ads` | Google Ads conversion id (`"AW-XXXXXXXXX"`) |
|
|
129
|
+
| `gsc` | Google Search Console verification token |
|
|
130
|
+
| `posthog` | PostHog project API key (`"phc_..."`) |
|
|
131
|
+
| `posthog_host` | PostHog host — optional, defaults to PostHog US cloud |
|
|
132
|
+
|
|
133
|
+
```json5
|
|
134
|
+
analytics: {
|
|
135
|
+
ga4: "G-ABCD1234",
|
|
136
|
+
gtm: "GTM-ABC1234",
|
|
137
|
+
},
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Omit a key (or the whole `analytics` object) to leave that integration alone; write `analytics: {}` to clear every one. Unrecognized keys and unusable values are dropped with a warning, and a malformed id is skipped at build time rather than breaking the build. Put tag ids here rather than pasting vendor snippets into a layout — the build injects the right script for each id, and with `cookie_consent: true` it gates them behind the banner automatically (§14).
|
|
118
141
|
|
|
119
142
|
Edit values in place and keep the keys. Keys you omit keep their stored values; unknown keys are ignored. Which pages are excluded from the sitemap is a CMS UI setting, not a key in this file. Never delete the file.
|
|
120
143
|
|
|
@@ -415,6 +438,8 @@ Callable from **any** view (page, block, entry, layout); duplicates are removed
|
|
|
415
438
|
|
|
416
439
|
`script()`/`link()` also accept a predefined library name — `gsap` (includes ScrollTrigger, pre-registered), `aos` (auto-`AOS.init()`, JS+CSS), `alpinejs`, `swiper`, `animejs`, `chartjs`, `threejs` (import map — pair with a `type: 'module'` script), `lottie`, `typedjs`, `splide`, `htmx`, `vanilla-tilt` — either call injects all of the library's CDN assets, deduped. Prefer these names over hand-picked CDN URLs, and only add a library the site actually uses.
|
|
417
440
|
|
|
441
|
+
`script()` also takes an attributes object — `script({ src: '/js/app.js', defer: '' })`, `script({ content: '...', type: 'module' })` — so a tag that needs attributes still never needs a raw `<script>`. That is how a third-party script is put behind cookie consent: `script({ src: 'https://…', 'data-sleekcms-consent': 'marketing' })` (§14).
|
|
442
|
+
|
|
418
443
|
```ejs
|
|
419
444
|
<% script('aos') %> <!-- injects aos.js + aos.css + AOS.init() -->
|
|
420
445
|
<section data-aos="fade-up">
|
|
@@ -447,7 +472,7 @@ Make every site strongly SEO- and sharing-friendly. The standard pattern is a re
|
|
|
447
472
|
|
|
448
473
|
Add `seo: block(seo)` to page models and render it **once from the layout head**: `<% if (item.seo) { %><%- render(item.seo) %><% } %>` in `layouts/common.ejs` (as in the layout example above) — not per page view.
|
|
449
474
|
|
|
450
|
-
|
|
475
|
+
Three site-level SEO settings live in `setup.json` (§4), not in views: `sitemap_enabled: true` publishes `sitemap.xml` + `robots.txt` on every build; `title` is the site's default `<title>` for pages whose SEO block leaves the title empty (`null` = the site name); and a Google Search Console verification token goes in `analytics.gsc`. Sitemap page exclusions are chosen in the CMS UI — there is no file setting for them.
|
|
451
476
|
|
|
452
477
|
## 10. Forms
|
|
453
478
|
|
|
@@ -552,7 +577,76 @@ Snipcart powers stores. Use it when the business **sells products** and the user
|
|
|
552
577
|
`data-item-url` is the product's own page path (`path(item)`), and that page must contain the same buy button — Snipcart re-crawls it to validate orders. Repeat the full `data-item-*` set everywhere the product can be added (listing cards too).
|
|
553
578
|
4. **Header controls.** Add both the cart button (`class="snipcart-checkout"` with `snipcart-items-count`) and the customer sign-in link (`class="snipcart-customer-signin"`) to the site header.
|
|
554
579
|
|
|
555
|
-
## 14.
|
|
580
|
+
## 14. Cookie Consent
|
|
581
|
+
|
|
582
|
+
Use this when the user wants a **cookie banner** — GDPR / ePrivacy consent for tracking scripts and third-party embeds. One switch turns it on; everything else is HTML. Tag each script or iframe that needs consent with `data-sleekcms-consent="<category>"`, and at build time SleekCMS ships it inert, injects a consent banner and runtime into every page, and activates it only after the visitor accepts that category. Choices are stored in a first-party cookie and honoured on every return visit. **Never add a banner speculatively** — only when the user asks, or the site clearly needs one.
|
|
583
|
+
|
|
584
|
+
**Turn it on** — `cookie_consent: true` in `setup.json` (§4), the same switch as Settings > Website > Analytics in the CMS. Off (the default): nothing is injected and tagged scripts run as normal.
|
|
585
|
+
|
|
586
|
+
**Tag scripts via `script()`** — views never contain raw `<script>` tags, so the category goes in the attributes object:
|
|
587
|
+
|
|
588
|
+
```ejs
|
|
589
|
+
<% script({ src: 'https://example.com/widget.js', 'data-sleekcms-consent': 'marketing' }) %>
|
|
590
|
+
<% script({ content: "fbq('track', 'PageView');", 'data-sleekcms-consent': 'analytics' }) %>
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
- Category names are yours to define (`analytics`, `marketing`, `personalization`, …): lowercase letters, digits, `-` and `_`. Reserved, never a category: `necessary`, `preferences`, `config`.
|
|
594
|
+
- The banner offers exactly the categories found on the site, each individually acceptable or declinable.
|
|
595
|
+
- A bare `data-sleekcms-consent` (no value) defaults to `analytics` with a build warning — always write the value.
|
|
596
|
+
- Scripts that don't need consent (strictly necessary) get no attribute.
|
|
597
|
+
- Analytics configured in `setup.json` (GA4, GTM, Meta Pixel, PostHog, …) is tagged `analytics` automatically. A tag snippet pasted by hand is not — tag it yourself, or better, move the id into `setup.json`.
|
|
598
|
+
|
|
599
|
+
**Gated embeds (iframes)** take the attribute directly in markup — YouTube, Google Maps, social embeds. Until consent is granted the iframe shows a "click to load" placeholder in its reserved space instead of contacting the third party:
|
|
600
|
+
|
|
601
|
+
```html
|
|
602
|
+
<iframe data-sleekcms-consent="marketing" src="https://www.youtube.com/embed/VIDEO_ID" class="aspect-video w-full" allowfullscreen></iframe>
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**Customise the banner** — optional. One **empty** config element in the layout body; the build reads it and removes it from the output, so it never renders or affects layout:
|
|
606
|
+
|
|
607
|
+
```html
|
|
608
|
+
<div data-sleekcms-consent data-position="bottom" data-theme="auto" data-policy-url="/privacy"></div>
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
| Attribute | Values | Default |
|
|
612
|
+
|---|---|---|
|
|
613
|
+
| `data-position` | `bottom`, `bottom-left`, `bottom-right`, `modal` | `bottom` |
|
|
614
|
+
| `data-theme` | `light`, `dark`, `auto` | `auto` |
|
|
615
|
+
| `data-policy-url` | path to the privacy / cookie policy page | none (link hidden) |
|
|
616
|
+
| `data-locale` | banner language, e.g. `en`, `de` | auto-detect |
|
|
617
|
+
| `data-message` | banner text | built-in default |
|
|
618
|
+
| `data-accept-label` | accept button text | "Accept" |
|
|
619
|
+
| `data-decline-label` | decline button text | "Decline" |
|
|
620
|
+
|
|
621
|
+
No config element → all defaults. One per site: a second one is ignored with a build warning. Built-in banner languages: `en`, `de`, `fr`, `es`, `it`, `nl`, `pt`, `pl` — picked from `<html lang>`, then the browser; `data-locale` overrides.
|
|
622
|
+
|
|
623
|
+
**Styling** — the banner reads five CSS custom properties. Set them on `:root` in `css/tailwind.css`, from the site's `@theme` tokens:
|
|
624
|
+
|
|
625
|
+
```css
|
|
626
|
+
@layer base {
|
|
627
|
+
:root {
|
|
628
|
+
--sleek-consent-bg: #1a1a2e;
|
|
629
|
+
--sleek-consent-text: #ffffff;
|
|
630
|
+
--sleek-consent-accent: #4f8cff;
|
|
631
|
+
--sleek-consent-on-accent: #ffffff; /* text on the accent button */
|
|
632
|
+
--sleek-consent-radius: 8px;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**"Cookie settings" link** — regulation requires a way to change consent later, so put one in the footer; the runtime opens the preferences panel:
|
|
638
|
+
|
|
639
|
+
```html
|
|
640
|
+
<a data-sleekcms-consent href="#">Cookie settings</a>
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
**Behaviour** — first visit: banner shows, no gated script runs until the visitor chooses (no choice = no consent). Return visits: stored choices apply, no banner. Granting later activates without a reload; revoking reloads, since already-run scripts can't be undone. Adding or renaming a category re-prompts visitors on their next visit.
|
|
644
|
+
|
|
645
|
+
**JavaScript API** (custom integrations only — configuration belongs in HTML): `sleekcms.consent.get()` → `{ necessary: true, analytics: true, marketing: false }`, `sleekcms.consent.open()` opens the preferences panel, `sleekcms.consent.on('change', cb)` fires with the granted categories on every change. The runtime tag loads at the end of `<body>`; code that runs before it should listen for the `sleekcms:consent` window event instead — the same snapshot in `event.detail`, fired on load and on every change.
|
|
646
|
+
|
|
647
|
+
**Notes** — gating happens at build time: tagged scripts ship as `type="text/plain"` with their `src` stashed, and gated iframes ship without `src`, so nothing can fire before consent even on slow connections. The banner is a fixed overlay (or modal), so no layout shift. `<noscript>` content cannot be gated, and `document.write` inside a gated script is unsupported. SleekCMS provides the consent *mechanism*; which script belongs to which category, and the privacy policy itself, are the site owner's call — ask when unsure, and link the policy page via `data-policy-url`.
|
|
648
|
+
|
|
649
|
+
## 15. Critical Rules Checklist
|
|
556
650
|
|
|
557
651
|
1. **`+` is part of the key.** A collection's `+` appears on the model, the view, and the content file/folder: `models/pages/blog+.model`, `pages/blog+.ejs`, `content/pages/blog+/<slug>.json`. A file without the `+` is a *different*, single-type key.
|
|
558
652
|
2. Models first: a content field that isn't in the `.model` won't sync. Update the model, then the content.
|
|
@@ -568,4 +662,5 @@ Snipcart powers stores. Use it when the business **sells products** and the user
|
|
|
568
662
|
12. Image shortcuts (`"iconify:..."`, `"pexels:..."`, etc.) resolve **only** in `image`/`icon`-typed fields. CMS-editable icons are declared `icon: icon`, never `icon: text` — render with `svg(item.icon)`; fixed UI icons need no field: `svg('iconify:...')` directly in the view.
|
|
569
663
|
13. Use entries (`entry(key+)`) for shared content (authors, testimonials, nav, footer) — not blocks. Block models cannot contain `block(...)` or `stack` fields; use blocks only when a group of fields also benefits from a reusable view.
|
|
570
664
|
14. Always create an RSS feed for blogs (`rss.xml` key) and make every site strongly SEO- and sharing-friendly (reusable `seo` block rendered once from the layout head).
|
|
571
|
-
15. Site-wide setup — default page title, Tailwind on/off, sitemap on/off — lives in `setup.json` (§4). Change values there rather than hard-coding a site title into the layout, and never delete the file. Sitemap page exclusions are UI-only.
|
|
665
|
+
15. Site-wide setup — default page title, Tailwind on/off, sitemap on/off, cookie consent on/off, analytics tag ids — lives in `setup.json` (§4). Change values there rather than hard-coding a site title into the layout, and never delete the file. Sitemap page exclusions are UI-only.
|
|
666
|
+
16. Cookie consent (§14) is a `setup.json` switch plus `data-sleekcms-consent` attributes — tag third-party scripts through `script({ …, 'data-sleekcms-consent': 'category' })`, never a raw tag, and add a footer "Cookie settings" link. Only when the user asks for a banner.
|