@sarimarcus/content-sites-ui 0.16.0 → 0.18.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/CHANGELOG.md +14 -0
- package/README.md +55 -2
- package/package.json +7 -3
- package/src/affiliate/EmeraldLoader.astro +40 -0
- package/src/affiliate/index.ts +4 -1
- package/src/affiliate/resources.ts +7 -0
- package/src/affiliate/travelpayoutsStyler.ts +557 -0
- package/src/composed/ArrowLinkGrid.astro +36 -0
- package/src/composed/index.ts +1 -0
- package/src/editorial/ArticleFeatureCard.astro +77 -0
- package/src/editorial/ArticlesListingPage.astro +40 -0
- package/src/editorial/NotFoundPage.astro +98 -0
- package/src/editorial/StaticProsePage.astro +48 -0
- package/src/editorial/index.ts +4 -0
- package/src/layout/ConsentAnalytics.astro +121 -0
- package/src/layout/DocumentShell.astro +171 -0
- package/src/layout/ListingShell.astro +32 -0
- package/src/layout/analytics.ts +151 -0
- package/src/layout/index.ts +7 -0
- package/src/layout/resources.ts +19 -0
- package/src/search/SearchPage.astro +222 -0
- package/src/search/index.ts +2 -0
- package/src/search/searchPage.ts +591 -0
- package/src/styles/not-found.css +151 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All packages in the platform release in lockstep; entries are per release version.
|
|
4
4
|
|
|
5
|
+
## 0.18.0 (2026-10-02)
|
|
6
|
+
|
|
7
|
+
- New page templates in `./editorial`: `StaticProsePage` (legal and site-information pages: heading, optional eyebrow, prose slot, `after` slot, closing CTA box; not Pagefind-indexed), `NotFoundPage` (the 404 body, every string a prop; its decoration ships as `./not-found.css` so a barrel import never carries it to other pages), `ArticlesListingPage` (article index and topic hub shell: breadcrumb band, header, `<main>`, Pagefind body optional) and `ArticleFeatureCard` (SLA-2744).
|
|
8
|
+
- `./search`: `SearchPage`, the search page markup (hero, type chips, optional audience row, result states, type-icon template), with its client controller `initSearchPage` on the new `./search-page` entry point (SLA-2744).
|
|
9
|
+
- `./layout`: `ListingShell`, the listing hub frame inside one Pagefind-ignore wrapper; StructuredData stays a direct Layout child so its JSON-LD renders in `<head>` (SLA-2744).
|
|
10
|
+
- `./composed`: `ArrowLinkGrid`, a grid of bordered link tiles (SLA-2744).
|
|
11
|
+
|
|
12
|
+
## 0.17.0 (2026-10-01)
|
|
13
|
+
|
|
14
|
+
- New `DocumentShell` (`./layout`): the whole HTML document a site page renders into: description, robots (`noindex, follow` when noindex), canonical from the page path, RSS and favicons, Open Graph / article / Twitter (handle optional), title, non-critical stylesheets, the Emerald loader, the skip link, navigation and footer wrapped out of the Pagefind index, `#main-content`, and consent-gated analytics. Slots `head-start`, `fonts`, `head`, `body-start`, `nav`, `footer`. It imports no stylesheet and emits no bundled script, so a site's thin Layout keeps its bundle names (SLA-2741).
|
|
15
|
+
- New `ConsentAnalytics` (`./layout`): the inline GTM + Consent Mode v2 + Silktide bootstrap (consent default before the gtm.js event in one synchronous step; `window.openCookiePreferences`), production only, plus the `<noscript>` GTM iframe. `layout/resources.ts` declares the hosts it loads (SLA-2741).
|
|
16
|
+
- New `EmeraldLoader` (`./affiliate`): the Travelpayouts Emerald script from the site's trs; `emeraldResources` declares its host (SLA-2741).
|
|
17
|
+
- New entry points `./analytics` (`setupAnalytics`, `track`, `pageTypeOf`, delegated, FAQ and scroll-depth tracking) and `./travelpayouts-styler` (`setupTravelpayoutsStyler`, palette, fonts and copy as options), moved from the sites. FAQ tracking now fires: it tested `dataset.faqTrack`, which is empty on the bare `data-faq-track` attribute ui `FAQ` renders (SLA-2741).
|
|
18
|
+
|
|
5
19
|
## 0.16.0 (2026-10-01)
|
|
6
20
|
|
|
7
21
|
- New `./navigation`: `SiteHeader` (fixed header, labelled main `<nav>`, logo/desktop/actions/mobile slots, mobile toggle, mobile panel after the header with `data-pagefind-ignore`, `surfaceClass`, Ctrl/Cmd+K → `searchPath?focus=1`), `NavDropdown` (click, hover with a 150 ms close delay, ArrowDown/ArrowUp/Escape, outside click, focus leaving; a click on a hover-opened dropdown keeps it open), `MobileNavSection` (single-open accordion, `aria-controls`), `NavMenuLink` / `MobileNavLink` (panel rows by `NavTone`), `SiteFooter` (sections, `<nav aria-label="Legal">`, cookie-preferences button through `data-cookie-preferences`, no inline handler), `isActivePath`, and the `SiteIdentity` / `NavLink` / `NavGroup` / `FooterSection` / `NavTone` contracts. One bundled controller, idempotent on `astro:page-load`, any number of headers per page; the toggle and chevron icons now switch state (SLA-2735).
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @sarimarcus/content-sites-ui
|
|
2
2
|
|
|
3
3
|
Universal Astro UI shared by every content site. Named entry points, one per folder: `primitives`,
|
|
4
|
-
`composed`, `editorial`, `affiliate`, `seo`, `layout`, `navigation`, `search` and `types
|
|
4
|
+
`composed`, `editorial`, `affiliate`, `seo`, `layout`, `navigation`, `search` and `types`, plus the script
|
|
5
|
+
entry points `analytics`, `travelpayouts-styler` and `search-page`. For example,
|
|
5
6
|
`import { Container } from '@sarimarcus/content-sites-ui/layout'`. `src/internal` is never exported,
|
|
6
7
|
and `src/styles` holds the Tailwind entry.
|
|
7
8
|
|
|
@@ -73,7 +74,59 @@ state and one bundled controller (idempotent on `astro:page-load`, any number of
|
|
|
73
74
|
`<nav aria-label="Legal">` ending with the cookie-preferences button, which calls `window.openCookiePreferences()`
|
|
74
75
|
(the site's consent loader defines it). Defaults paint the `inverse` roles; class props restyle each part.
|
|
75
76
|
- `appendResults` renders Pagefind result cards from the site's type taxonomy; `SearchSuggestions` renders the
|
|
76
|
-
empty-state chips.
|
|
77
|
+
empty-state chips. `SearchPage` is the whole page body (hero, type chips, an audience row when `audienceFacets` is
|
|
78
|
+
passed, result states); the route keeps Layout (`noindex`), imports `search.css` and calls `initSearchPage` from
|
|
79
|
+
`@sarimarcus/content-sites-ui/search-page` in its own `<script>` with the same taxonomy and copy. The copy, the
|
|
80
|
+
taxonomy and the index stay in the site.
|
|
81
|
+
|
|
82
|
+
## Page templates
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { StaticProsePage, NotFoundPage, ArticlesListingPage, ArticleFeatureCard } from '@sarimarcus/content-sites-ui/editorial';
|
|
86
|
+
import { ListingShell } from '@sarimarcus/content-sites-ui/layout';
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The route file keeps `getStaticPaths`, queries, URLs, `<Layout title description>` (the SEO length gate reads it) and
|
|
90
|
+
`<StructuredData slot="head">` as a direct Layout child; the template receives normalised props and slots. Hrefs and
|
|
91
|
+
prose stay in the route so the site's link and outbound-domain scanners still see them.
|
|
92
|
+
|
|
93
|
+
- `StaticProsePage`: privacy, terms, contact, editorial standards. Prose in the default slot, `after` slot, optional
|
|
94
|
+
`cta` box. Carries no Pagefind body.
|
|
95
|
+
- `NotFoundPage`: the 404 body; the route passes `noindex` to Layout and imports `@sarimarcus/content-sites-ui/not-found.css`.
|
|
96
|
+
- `ArticlesListingPage` / `ArticleFeatureCard`: article index, topic hubs and their paginated pages.
|
|
97
|
+
- `ListingShell`: listing hubs (breadcrumb, `hero`, `before-main`, Container'd `<main>`, `after-main`), one outer
|
|
98
|
+
`data-pagefind-ignore`.
|
|
99
|
+
|
|
100
|
+
A component exported from a barrel must not carry a `<style>` block: Astro ships it to every page that imports the
|
|
101
|
+
barrel. Put such rules in a stylesheet entry the one route imports.
|
|
102
|
+
|
|
103
|
+
## Document shell
|
|
104
|
+
|
|
105
|
+
```astro
|
|
106
|
+
---
|
|
107
|
+
import { DocumentShell } from '@sarimarcus/content-sites-ui/layout';
|
|
108
|
+
---
|
|
109
|
+
<DocumentShell title={title} description={description} siteName={site.siteName} siteUrl={site.url} themeColor="#FAFAF8"
|
|
110
|
+
gtmId={site.analytics.gtmId} consentPrompt="…" consentTypes={consentTypes} skipLinkLabel="Skip to main content">
|
|
111
|
+
<Fragment slot="fonts">…</Fragment>
|
|
112
|
+
<Navigation slot="nav" />
|
|
113
|
+
<slot />
|
|
114
|
+
<Footer slot="footer" />
|
|
115
|
+
</DocumentShell>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`DocumentShell` renders the whole document: description, robots (`noindex, follow` when `noindex`), canonical (from
|
|
119
|
+
the page path unless `canonicalUrl`), RSS and favicons, Open Graph / article / Twitter (`twitterHandle` optional),
|
|
120
|
+
title, non-critical `stylesheets`, the Emerald loader (`travelpayoutsTrs`), the skip link, the navigation and footer
|
|
121
|
+
wrapped in `data-pagefind-ignore`, `#main-content` and `ConsentAnalytics`. Slots: `head-start`, `fonts`, `head`,
|
|
122
|
+
`body-start`, `nav`, `footer`, default. It imports no stylesheet and emits no bundled script, so the site's thin
|
|
123
|
+
Layout keeps both (and with them its bundle names).
|
|
124
|
+
|
|
125
|
+
`ConsentAnalytics` is inline on purpose: the Consent Mode default must reach the dataLayer before the `gtm.js` event
|
|
126
|
+
in one synchronous step. It defines `window.openCookiePreferences` for the footer button. `./analytics`
|
|
127
|
+
(`setupAnalytics`, `track`, `pageTypeOf`) and `./travelpayouts-styler` (`setupTravelpayoutsStyler`) are their own
|
|
128
|
+
entry points so a client `<script>` imports no Astro component. The hosts the shell loads are declared in
|
|
129
|
+
`layout/resources.ts`; `scripts/validate-csp-coverage.mjs` checks every site's CSP against them.
|
|
77
130
|
|
|
78
131
|
## Search and consent styles
|
|
79
132
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sarimarcus/content-sites-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Universal Astro UI components, layout shell and design tokens shared by the content sites.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,8 +30,11 @@
|
|
|
30
30
|
"./composed": "./src/composed/index.ts",
|
|
31
31
|
"./editorial": "./src/editorial/index.ts",
|
|
32
32
|
"./affiliate": "./src/affiliate/index.ts",
|
|
33
|
+
"./travelpayouts-styler": "./src/affiliate/travelpayoutsStyler.ts",
|
|
33
34
|
"./seo": "./src/seo/index.ts",
|
|
34
35
|
"./layout": "./src/layout/index.ts",
|
|
36
|
+
"./analytics": "./src/layout/analytics.ts",
|
|
37
|
+
"./search-page": "./src/search/searchPage.ts",
|
|
35
38
|
"./navigation": "./src/navigation/index.ts",
|
|
36
39
|
"./search": "./src/search/index.ts",
|
|
37
40
|
"./types": "./src/types/index.ts",
|
|
@@ -39,12 +42,13 @@
|
|
|
39
42
|
"./widths.css": "./src/styles/widths.css",
|
|
40
43
|
"./theme.css": "./src/styles/theme.css",
|
|
41
44
|
"./consent.css": "./src/styles/consent.css",
|
|
42
|
-
"./search.css": "./src/styles/search.css"
|
|
45
|
+
"./search.css": "./src/styles/search.css",
|
|
46
|
+
"./not-found.css": "./src/styles/not-found.css"
|
|
43
47
|
},
|
|
44
48
|
"peerDependencies": {
|
|
45
49
|
"@lucide/astro": "^1.23.0",
|
|
46
50
|
"astro": "^7.0.6",
|
|
47
51
|
"tailwindcss": "^4.1.18",
|
|
48
|
-
"@sarimarcus/content-sites-core": "0.
|
|
52
|
+
"@sarimarcus/content-sites-core": "0.18.0"
|
|
49
53
|
}
|
|
50
54
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The Travelpayouts Emerald script that turns the site's affiliate links into tracked ones. Deferred off the critical
|
|
4
|
+
* path: it loads on the first interaction, or once the page is idle after load. `trs` is the site's Travelpayouts id.
|
|
5
|
+
*/
|
|
6
|
+
import { EMERALD_ORIGIN } from './resources.ts';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
trs: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { trs } = Astro.props;
|
|
13
|
+
const emeraldOrigin = EMERALD_ORIGIN;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<script is:inline define:vars={{ trs, emeraldOrigin }}>
|
|
17
|
+
(function () {
|
|
18
|
+
if (!/^\d+$/.test(trs)) return;
|
|
19
|
+
var loaded = false;
|
|
20
|
+
function loadAffiliate() {
|
|
21
|
+
if (loaded) return;
|
|
22
|
+
loaded = true;
|
|
23
|
+
var script = document.createElement('script');
|
|
24
|
+
script.async = 1;
|
|
25
|
+
script.src = emeraldOrigin + '/' + btoa(trs) + '.js?t=' + trs;
|
|
26
|
+
document.head.appendChild(script);
|
|
27
|
+
}
|
|
28
|
+
var opts = { once: true, passive: true, capture: true };
|
|
29
|
+
['pointerdown', 'keydown', 'touchstart', 'scroll'].forEach(function (evt) {
|
|
30
|
+
window.addEventListener(evt, loadAffiliate, opts);
|
|
31
|
+
});
|
|
32
|
+
window.addEventListener('load', function () {
|
|
33
|
+
if ('requestIdleCallback' in window) {
|
|
34
|
+
requestIdleCallback(loadAffiliate, { timeout: 5000 });
|
|
35
|
+
} else {
|
|
36
|
+
setTimeout(loadAffiliate, 4000);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
})();
|
|
40
|
+
</script>
|
package/src/affiliate/index.ts
CHANGED
|
@@ -11,7 +11,10 @@ export { default as AffiliateCardSection } from './AffiliateCardSection.astro';
|
|
|
11
11
|
export { default as AffiliateCarousel } from './AffiliateCarousel.astro';
|
|
12
12
|
export { default as AffiliateComparisonTable } from './AffiliateComparisonTable.astro';
|
|
13
13
|
export { default as HotelAffiliateCTA } from './HotelAffiliateCTA.astro';
|
|
14
|
-
export { externalResources } from './resources';
|
|
14
|
+
export { externalResources, emeraldResources, EMERALD_ORIGIN } from './resources';
|
|
15
15
|
export type { WidgetKind, CspDirective, WidgetResources } from './resources';
|
|
16
16
|
export type { AffiliateCardLabels, AffiliateCardItem, HotelCtaLabels, ComparisonRow, ComparisonTableLabels } from './types';
|
|
17
17
|
export { selectActivities, type SelectableActivity, type SelectOptions } from './select';
|
|
18
|
+
export { default as EmeraldLoader } from './EmeraldLoader.astro';
|
|
19
|
+
export { setupTravelpayoutsStyler } from './travelpayoutsStyler.ts';
|
|
20
|
+
export type { TravelpayoutsStylerOptions, TravelpayoutsStylerColors } from './travelpayoutsStyler.ts';
|
|
@@ -28,3 +28,10 @@ export const externalResources: Record<WidgetKind, WidgetResources> = {
|
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
|
|
32
|
+
/** The Travelpayouts Emerald loader every page carries (EmeraldLoader): https://emrldtp.com/<base64 trs>.js?t=<trs>. */
|
|
33
|
+
export const EMERALD_ORIGIN = 'https://emrldtp.com';
|
|
34
|
+
|
|
35
|
+
export const emeraldResources: Partial<Record<CspDirective, readonly string[]>> = {
|
|
36
|
+
'script-src': [EMERALD_ORIGIN],
|
|
37
|
+
};
|