@sankhyatronics/sankhya-cms 0.1.18 → 0.2.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/custom-elements.json +608 -15
- package/dist/bento-grid/index.d.ts +8 -1
- package/dist/bento-grid/index.js +2 -1
- package/dist/card/index.d.ts +3 -4
- package/dist/card/index.js +1 -1
- package/dist/{chunk-LEXZKGQX.js → chunk-2DXHPEB5.js} +1 -1
- package/dist/chunk-2DXHPEB5.js.map +1 -0
- package/dist/{chunk-R5G6KFTJ.js → chunk-2FJDNJ7R.js} +1 -1
- package/dist/chunk-2FJDNJ7R.js.map +1 -0
- package/dist/{chunk-ZN4LIKVG.js → chunk-3OEEOBHB.js} +1 -1
- package/dist/chunk-3OEEOBHB.js.map +1 -0
- package/dist/{chunk-BMHMNXIA.js → chunk-4AFOKKSU.js} +1 -1
- package/dist/chunk-4AFOKKSU.js.map +1 -0
- package/dist/{chunk-76AVBQUA.js → chunk-5C6C6K5U.js} +98 -4
- package/dist/chunk-5C6C6K5U.js.map +1 -0
- package/dist/{chunk-7NLZND6Q.js → chunk-5F5PTVWR.js} +27 -2
- package/dist/chunk-5F5PTVWR.js.map +1 -0
- package/dist/chunk-5MY6OWMK.js +125 -0
- package/dist/chunk-5MY6OWMK.js.map +1 -0
- package/dist/{chunk-FLY7TNRT.js → chunk-7NIWQWTA.js} +1 -1
- package/dist/chunk-7NIWQWTA.js.map +1 -0
- package/dist/{chunk-ALI26WOB.js → chunk-EE6PAN3B.js} +33 -2
- package/dist/chunk-EE6PAN3B.js.map +1 -0
- package/dist/{chunk-VWS53WY4.js → chunk-HMUZON6I.js} +49 -4
- package/dist/chunk-HMUZON6I.js.map +1 -0
- package/dist/chunk-O4FCRKDX.js +178 -0
- package/dist/chunk-O4FCRKDX.js.map +1 -0
- package/dist/{chunk-SMLR736E.js → chunk-OELI3IWC.js} +1 -1
- package/dist/chunk-OELI3IWC.js.map +1 -0
- package/dist/{chunk-JQY2G4QK.js → chunk-OLA35A2O.js} +1 -1
- package/dist/chunk-OLA35A2O.js.map +1 -0
- package/dist/{chunk-YQ42EBK5.js → chunk-PHEGSCOM.js} +1 -1
- package/dist/chunk-PHEGSCOM.js.map +1 -0
- package/dist/{chunk-5EBWMPJS.js → chunk-QLA4BH2B.js} +5 -5
- package/dist/chunk-QLA4BH2B.js.map +1 -0
- package/dist/{chunk-JKBWZLG4.js → chunk-SQ3OZO32.js} +141 -3
- package/dist/chunk-SQ3OZO32.js.map +1 -0
- package/dist/chunk-UAIUBPDN.js +11 -0
- package/dist/chunk-UAIUBPDN.js.map +1 -0
- package/dist/{chunk-XWB4CKDU.js → chunk-XUGEXERO.js} +6 -6
- package/dist/chunk-XUGEXERO.js.map +1 -0
- package/dist/chunk-ZRC5BVDX.js +219 -0
- package/dist/chunk-ZRC5BVDX.js.map +1 -0
- package/dist/cms-theme.css +7 -0
- package/dist/content-block/index.d.ts +2 -3
- package/dist/content-block/index.js +3 -2
- package/dist/dropdown/index.d.ts +8 -11
- package/dist/dropdown/index.js +1 -1
- package/dist/feature-split/index.js +2 -1
- package/dist/features-section/index.d.ts +4 -4
- package/dist/features-section/index.js +4 -3
- package/dist/flow/index.d.ts +35 -0
- package/dist/flow/index.js +9 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/header/index.d.ts +8 -15
- package/dist/header/index.js +1 -1
- package/dist/hero/index.d.ts +18 -3
- package/dist/hero/index.js +1 -1
- package/dist/icon-button/index.d.ts +2 -4
- package/dist/icon-button/index.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +36 -23
- package/dist/items-accordion/index.d.ts +5 -0
- package/dist/items-accordion/index.js +2 -1
- package/dist/markdown/index.d.ts +1 -2
- package/dist/markdown/index.js +2 -1
- package/dist/menu-grid/index.d.ts +2 -3
- package/dist/menu-grid/index.js +1 -1
- package/dist/menu-grid-item/index.d.ts +6 -8
- package/dist/menu-grid-item/index.js +1 -1
- package/dist/menu-item/index.d.ts +5 -7
- package/dist/menu-item/index.js +1 -1
- package/dist/promo-banner/index.d.ts +14 -3
- package/dist/promo-banner/index.js +1 -1
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/section/index.d.ts +3 -0
- package/dist/section/index.js +1 -1
- package/dist/steps/index.d.ts +33 -0
- package/dist/steps/index.js +10 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/tag-strip/index.d.ts +26 -0
- package/dist/tag-strip/index.js +9 -0
- package/dist/tag-strip/index.js.map +1 -0
- package/package.json +14 -2
- package/dist/chunk-5EBWMPJS.js.map +0 -1
- package/dist/chunk-76AVBQUA.js.map +0 -1
- package/dist/chunk-7NLZND6Q.js.map +0 -1
- package/dist/chunk-ALI26WOB.js.map +0 -1
- package/dist/chunk-BMHMNXIA.js.map +0 -1
- package/dist/chunk-FLY7TNRT.js.map +0 -1
- package/dist/chunk-JKBWZLG4.js.map +0 -1
- package/dist/chunk-JQY2G4QK.js.map +0 -1
- package/dist/chunk-LEXZKGQX.js.map +0 -1
- package/dist/chunk-R5G6KFTJ.js.map +0 -1
- package/dist/chunk-SMLR736E.js.map +0 -1
- package/dist/chunk-VWS53WY4.js.map +0 -1
- package/dist/chunk-XWB4CKDU.js.map +0 -1
- package/dist/chunk-YQ42EBK5.js.map +0 -1
- package/dist/chunk-ZN4LIKVG.js.map +0 -1
|
@@ -11,8 +11,9 @@ import {
|
|
|
11
11
|
} from "./chunk-EUXUH3YW.js";
|
|
12
12
|
|
|
13
13
|
// src/hero/Hero.ts
|
|
14
|
-
import { LitElement, css, html, nothing } from "lit";
|
|
14
|
+
import { LitElement, css, html, nothing, svg } from "lit";
|
|
15
15
|
import { customElement, property } from "lit/decorators.js";
|
|
16
|
+
var HIGHLIGHT_ICON = svg`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M20 6 9 17l-5-5" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>`;
|
|
16
17
|
var Hero = class extends LitElement {
|
|
17
18
|
constructor() {
|
|
18
19
|
super(...arguments);
|
|
@@ -28,6 +29,10 @@ var Hero = class extends LitElement {
|
|
|
28
29
|
this.eyebrow = "";
|
|
29
30
|
this.secondaryActionLabel = "";
|
|
30
31
|
this.secondaryHref = "";
|
|
32
|
+
this.badge = "";
|
|
33
|
+
this.highlights = [];
|
|
34
|
+
this.layout = "background";
|
|
35
|
+
this.imageAlt = "";
|
|
31
36
|
this.size = "auto";
|
|
32
37
|
this.contentPosition = "center";
|
|
33
38
|
this.overlay = "even";
|
|
@@ -35,7 +40,8 @@ var Hero = class extends LitElement {
|
|
|
35
40
|
this.onSecondaryClick = (event) => this.emitAction(event, this.secondaryHref, "secondary");
|
|
36
41
|
}
|
|
37
42
|
willUpdate() {
|
|
38
|
-
|
|
43
|
+
const asBackground = this.layout !== "split";
|
|
44
|
+
this.style.backgroundImage = asBackground && this.imageSrc ? `url(${JSON.stringify(this.imageSrc)})` : "";
|
|
39
45
|
this.style.setProperty("--st-hero-position", Hero.positions[this.focalPoint] ?? Hero.positions.center);
|
|
40
46
|
}
|
|
41
47
|
emitAction(event, href, kind) {
|
|
@@ -53,6 +59,7 @@ var Hero = class extends LitElement {
|
|
|
53
59
|
<div class="overlay" part="overlay"></div>
|
|
54
60
|
<div class="content" part="content">
|
|
55
61
|
${this.eyebrow ? html`<p class="eyebrow" part="eyebrow">${this.eyebrow}</p>` : nothing}
|
|
62
|
+
${this.badge ? html`<p class="badge" part="badge">${this.badge}</p>` : nothing}
|
|
56
63
|
<h1 class="title" part="title">${this.title}</h1>
|
|
57
64
|
${this.subtitle ? html`<p class="subtitle" part="subtitle">${this.subtitle}</p>` : nothing}
|
|
58
65
|
${this.actionLabel || this.secondaryActionLabel ? html`<div class="actions" part="actions">
|
|
@@ -61,8 +68,16 @@ var Hero = class extends LitElement {
|
|
|
61
68
|
>${this.secondaryActionLabel}</a
|
|
62
69
|
>` : nothing}
|
|
63
70
|
</div>` : nothing}
|
|
71
|
+
${this.highlights.length ? html`<ul class="highlights" part="highlights">
|
|
72
|
+
${this.highlights.map(
|
|
73
|
+
(text) => html`<li class="highlight" part="highlight">${HIGHLIGHT_ICON}<span>${text}</span></li>`
|
|
74
|
+
)}
|
|
75
|
+
</ul>` : nothing}
|
|
64
76
|
<slot></slot>
|
|
65
77
|
</div>
|
|
78
|
+
${this.layout === "split" && this.imageSrc ? html`<figure class="media" part="media">
|
|
79
|
+
<img class="image" part="image" src=${this.imageSrc} alt=${this.imageAlt} />
|
|
80
|
+
</figure>` : nothing}
|
|
66
81
|
`;
|
|
67
82
|
}
|
|
68
83
|
};
|
|
@@ -273,6 +288,117 @@ Hero.styles = css`
|
|
|
273
288
|
display: none;
|
|
274
289
|
}
|
|
275
290
|
|
|
291
|
+
.badge {
|
|
292
|
+
display: inline-flex;
|
|
293
|
+
align-items: center;
|
|
294
|
+
gap: var(--st-space-8);
|
|
295
|
+
margin: 0;
|
|
296
|
+
padding: 6px 14px;
|
|
297
|
+
border: 1px solid var(--st-color-border);
|
|
298
|
+
border-radius: var(--st-radius-full);
|
|
299
|
+
background: var(--st-color-surface);
|
|
300
|
+
color: var(--st-color-text-strong);
|
|
301
|
+
font-size: var(--st-text-sm);
|
|
302
|
+
font-weight: var(--st-font-weight-bold);
|
|
303
|
+
letter-spacing: var(--st-tracking-wider);
|
|
304
|
+
line-height: 1.2;
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
animation: st-hero-fade 0.8s 0.2s ease-out backwards;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.badge::before {
|
|
310
|
+
content: '';
|
|
311
|
+
flex: none;
|
|
312
|
+
width: 8px;
|
|
313
|
+
height: 8px;
|
|
314
|
+
border-radius: var(--st-radius-circle);
|
|
315
|
+
background: var(--st-cms-highlight);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.highlights {
|
|
319
|
+
display: flex;
|
|
320
|
+
flex-wrap: wrap;
|
|
321
|
+
justify-content: center;
|
|
322
|
+
gap: var(--st-space-12) var(--st-space-24);
|
|
323
|
+
margin: 0;
|
|
324
|
+
padding: 0;
|
|
325
|
+
list-style: none;
|
|
326
|
+
animation: st-hero-fade 0.8s 0.9s ease-out backwards;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.highlight {
|
|
330
|
+
display: inline-flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
gap: var(--st-space-8);
|
|
333
|
+
font-size: var(--st-text-md);
|
|
334
|
+
font-weight: var(--st-font-weight-semibold);
|
|
335
|
+
color: inherit;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.highlight svg {
|
|
339
|
+
flex: none;
|
|
340
|
+
width: 18px;
|
|
341
|
+
height: 18px;
|
|
342
|
+
color: var(--st-cms-highlight);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
:host([text-alignment='left']) .highlights {
|
|
346
|
+
justify-content: flex-start;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
:host([text-alignment='right']) .highlights {
|
|
350
|
+
justify-content: flex-end;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* layout="split": a two-column block on the page surface; the image replaces the background. */
|
|
354
|
+
:host([layout='split']) {
|
|
355
|
+
display: grid;
|
|
356
|
+
grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
|
|
357
|
+
align-items: center;
|
|
358
|
+
gap: var(--st-space-48);
|
|
359
|
+
overflow: visible;
|
|
360
|
+
padding: var(--st-cms-section-padding) var(--st-space-24);
|
|
361
|
+
background-color: var(--st-color-surface);
|
|
362
|
+
background-image: none;
|
|
363
|
+
color: var(--st-color-text-strong);
|
|
364
|
+
animation: none;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
:host([layout='split']) .overlay {
|
|
368
|
+
display: none;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
:host([layout='split']) .content {
|
|
372
|
+
width: 100%;
|
|
373
|
+
margin: 0;
|
|
374
|
+
padding: 0;
|
|
375
|
+
max-width: 560px;
|
|
376
|
+
color: var(--st-hero-text-color, var(--st-color-text-strong));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
:host([layout='split'][text-alignment='left']) .actions {
|
|
380
|
+
justify-content: flex-start;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
:host([layout='split']) .highlight svg {
|
|
384
|
+
color: var(--st-color-accent);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.media {
|
|
388
|
+
margin: 0;
|
|
389
|
+
min-width: 0;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.image {
|
|
393
|
+
display: block;
|
|
394
|
+
width: 100%;
|
|
395
|
+
height: auto;
|
|
396
|
+
aspect-ratio: 4 / 3;
|
|
397
|
+
object-fit: cover;
|
|
398
|
+
border-radius: var(--st-radius-lg);
|
|
399
|
+
box-shadow: 0 24px 48px -16px var(--st-shadow-color-lg);
|
|
400
|
+
}
|
|
401
|
+
|
|
276
402
|
@media (min-width: 768px) {
|
|
277
403
|
.content { padding: var(--st-space-32); }
|
|
278
404
|
}
|
|
@@ -329,6 +455,18 @@ __decorateClass([
|
|
|
329
455
|
__decorateClass([
|
|
330
456
|
property({ type: String, attribute: "secondary-href" })
|
|
331
457
|
], Hero.prototype, "secondaryHref", 2);
|
|
458
|
+
__decorateClass([
|
|
459
|
+
property({ type: String })
|
|
460
|
+
], Hero.prototype, "badge", 2);
|
|
461
|
+
__decorateClass([
|
|
462
|
+
property({ attribute: false })
|
|
463
|
+
], Hero.prototype, "highlights", 2);
|
|
464
|
+
__decorateClass([
|
|
465
|
+
property({ type: String, reflect: true })
|
|
466
|
+
], Hero.prototype, "layout", 2);
|
|
467
|
+
__decorateClass([
|
|
468
|
+
property({ type: String, attribute: "image-alt" })
|
|
469
|
+
], Hero.prototype, "imageAlt", 2);
|
|
332
470
|
__decorateClass([
|
|
333
471
|
property({ type: String, reflect: true })
|
|
334
472
|
], Hero.prototype, "size", 2);
|
|
@@ -345,4 +483,4 @@ Hero = __decorateClass([
|
|
|
345
483
|
export {
|
|
346
484
|
Hero
|
|
347
485
|
};
|
|
348
|
-
//# sourceMappingURL=chunk-
|
|
486
|
+
//# sourceMappingURL=chunk-SQ3OZO32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hero/Hero.ts"],"sourcesContent":["import { LitElement, css, html, nothing, svg } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\nimport { controlBaseStyles, rootFontFamilyStyles } from '../shared/styles';\nimport { ctaLinkStyles, paddingScaleStyles } from '../shared/section-styles';\nimport type { HeroActionEventDetail } from './HeroEvents';\n\nconst HIGHLIGHT_ICON = svg`<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M20 6 9 17l-5-5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg>`;\n\n/**\n * Full-width hero banner: background image with a darkening overlay, optional eyebrow, title,\n * optional subtitle and up to two call-to-action links. The entrance animation is CSS keyframes\n * (skipped under `prefers-reduced-motion`).\n *\n * `size`, `content-position` and `overlay=\"gradient\"` give the large editorial layout (copy on the\n * lower left over a gradient that darkens towards it) used by enterprise marketing heroes; the\n * defaults keep the original centered banner. Put several heroes in an `st-carousel` with\n * `indicator-style=\"tabs\"` for a tabbed hero carousel.\n *\n * `badge` is a pill above the title; `highlights` is a row of check-marked points under the buttons.\n * `layout=\"split\"` swaps the background for a two-column layout: copy on one side, `image-src` as an\n * `<img>` (alt from `image-alt`) on the other, on normal page colours with no overlay.\n *\n * @fires action - Cancelable, before navigating. Detail `{ href, kind }`, `kind` being `primary` or `secondary`.\n * @cssprop --st-hero-text-color - Text colour over the image.\n * @cssprop --st-hero-min-height - Minimum height when `size` is `large` (default `640px`).\n * @csspart eyebrow - The small label above the title.\n * @csspart badge - The pill above the title.\n * @csspart highlights - The row of check-marked points.\n * @csspart image - The image in `layout=\"split\"`.\n * @csspart secondary-action - The outlined second button.\n */\n@customElement('st-hero')\nexport class Hero extends LitElement {\n static styles = css`\n ${controlBaseStyles}\n ${paddingScaleStyles}\n\n @keyframes st-hero-zoom {\n from { opacity: 0; transform: scale(1.1); }\n to { opacity: 1; transform: scale(1); }\n }\n @keyframes st-hero-rise {\n from { opacity: 0; transform: translateY(30px); }\n to { opacity: 1; transform: none; }\n }\n @keyframes st-hero-fade {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n :host {\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n overflow: hidden;\n width: 100%;\n box-sizing: border-box;\n background-color: var(--st-color-text-strong);\n background-repeat: no-repeat;\n background-size: cover;\n background-position: var(--st-hero-position, center center);\n color: var(--st-color-on-solid);\n ${rootFontFamilyStyles}\n animation: st-hero-zoom 1.5s ease-out;\n }\n\n :host([inverted]) {\n background-color: var(--st-color-surface);\n color: var(--st-color-text-strong);\n }\n\n .overlay {\n position: absolute;\n inset: 0;\n z-index: 1;\n background: linear-gradient(\n to bottom,\n color-mix(in oklch, var(--st-color-text-strong) 40%, transparent),\n color-mix(in oklch, var(--st-color-text-strong) 20%, transparent),\n color-mix(in oklch, var(--st-color-text-strong) 40%, transparent)\n );\n }\n\n :host([inverted]) .overlay {\n display: none;\n }\n\n .content {\n display: flex;\n flex-direction: column;\n gap: var(--st-space-20);\n position: relative;\n z-index: 2;\n width: 80%;\n margin: 0 auto;\n text-align: center;\n align-items: center;\n /* Set here, not only on :host: text sits on the image/overlay, so a colour class the page puts\n on the host (e.g. a theme text colour) must not turn it dark. */\n color: var(--st-hero-text-color, var(--st-color-on-solid));\n }\n\n :host([inverted]) .content {\n color: var(--st-hero-text-color, var(--st-color-text-strong));\n }\n\n :host([text-alignment='left']) .content { text-align: left; align-items: flex-start; }\n :host([text-alignment='right']) .content { text-align: right; align-items: flex-end; }\n :host([text-alignment='justify']) .content { text-align: justify; align-items: stretch; }\n\n .title {\n font-size: var(--st-cms-text-display);\n font-weight: var(--st-font-weight-bold);\n line-height: 1.1;\n color: inherit;\n margin: 0;\n animation: st-hero-rise 0.8s 0.3s ease-out backwards;\n }\n\n .subtitle {\n font-size: var(--st-cms-text-xl);\n line-height: 1.5;\n color: inherit;\n margin: 0;\n animation: st-hero-fade 0.8s 0.5s ease-out backwards;\n }\n\n .actions {\n display: flex;\n justify-content: center;\n width: 100%;\n }\n\n .cta {\n ${ctaLinkStyles}\n background: var(--st-button-primary-bg);\n color: var(--st-button-primary-color);\n font-weight: var(--st-button-font-weight);\n font-size: var(--st-text-md);\n animation: st-hero-rise 0.6s 0.7s ease-out backwards;\n }\n\n .cta:hover {\n transform: scale(1.05);\n }\n\n .cta:active {\n transform: scale(0.98);\n }\n\n .eyebrow {\n margin: 0;\n font-size: var(--st-text-sm);\n font-weight: var(--st-font-weight-bold);\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: inherit;\n opacity: 0.85;\n animation: st-hero-fade 0.8s 0.2s ease-out backwards;\n }\n\n .actions {\n flex-wrap: wrap;\n gap: var(--st-space-12);\n }\n\n .cta.secondary {\n background: transparent;\n color: inherit;\n box-shadow: inset 0 0 0 1.5px currentColor;\n }\n\n .cta.secondary:hover {\n background: color-mix(in oklch, currentColor 12%, transparent);\n }\n\n /* size=\"large\" / \"full\": a tall editorial hero. */\n :host([size='large']) {\n min-height: var(--st-hero-min-height, 640px);\n }\n\n :host([size='full']) {\n min-height: 100svh;\n }\n\n :host([size='large']) .title,\n :host([size='full']) .title {\n font-size: clamp(2.5rem, 6vw, 5rem);\n letter-spacing: -0.02em;\n line-height: 1.05;\n }\n\n /* content-position=\"bottom\": copy sits in the lower part of the hero, on the container grid. */\n :host([content-position='bottom']) {\n align-items: flex-end;\n }\n\n :host([content-position='bottom']) .content {\n width: 100%;\n max-width: var(--st-cms-container-max);\n box-sizing: border-box;\n padding: var(--st-space-48) var(--st-space-24);\n }\n\n :host([content-position='bottom']) .title,\n :host([content-position='bottom']) .subtitle {\n max-width: 18ch;\n }\n\n :host([content-position='bottom']) .subtitle {\n max-width: 48ch;\n }\n\n :host([content-position='bottom']:not([text-alignment='center']):not([text-alignment='right'])) .actions {\n justify-content: flex-start;\n }\n\n :host([content-position='bottom'][text-alignment='center']) .title,\n :host([content-position='bottom'][text-alignment='center']) .subtitle {\n margin-inline: auto;\n }\n\n /* overlay=\"gradient\": darkest behind the copy, clearing towards the far side of the image. */\n :host([overlay='gradient']) .overlay {\n background:\n linear-gradient(to top, color-mix(in oklch, var(--st-color-text-strong) 85%, transparent), transparent 70%),\n linear-gradient(to right, color-mix(in oklch, var(--st-color-text-strong) 70%, transparent), transparent 75%);\n }\n\n :host([overlay='gradient'][text-alignment='right']) .overlay {\n background:\n linear-gradient(to top, color-mix(in oklch, var(--st-color-text-strong) 85%, transparent), transparent 70%),\n linear-gradient(to left, color-mix(in oklch, var(--st-color-text-strong) 70%, transparent), transparent 75%);\n }\n\n :host([overlay='none']) .overlay {\n display: none;\n }\n\n .badge {\n display: inline-flex;\n align-items: center;\n gap: var(--st-space-8);\n margin: 0;\n padding: 6px 14px;\n border: 1px solid var(--st-color-border);\n border-radius: var(--st-radius-full);\n background: var(--st-color-surface);\n color: var(--st-color-text-strong);\n font-size: var(--st-text-sm);\n font-weight: var(--st-font-weight-bold);\n letter-spacing: var(--st-tracking-wider);\n line-height: 1.2;\n text-transform: uppercase;\n animation: st-hero-fade 0.8s 0.2s ease-out backwards;\n }\n\n .badge::before {\n content: '';\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: var(--st-radius-circle);\n background: var(--st-cms-highlight);\n }\n\n .highlights {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: var(--st-space-12) var(--st-space-24);\n margin: 0;\n padding: 0;\n list-style: none;\n animation: st-hero-fade 0.8s 0.9s ease-out backwards;\n }\n\n .highlight {\n display: inline-flex;\n align-items: center;\n gap: var(--st-space-8);\n font-size: var(--st-text-md);\n font-weight: var(--st-font-weight-semibold);\n color: inherit;\n }\n\n .highlight svg {\n flex: none;\n width: 18px;\n height: 18px;\n color: var(--st-cms-highlight);\n }\n\n :host([text-alignment='left']) .highlights {\n justify-content: flex-start;\n }\n\n :host([text-alignment='right']) .highlights {\n justify-content: flex-end;\n }\n\n /* layout=\"split\": a two-column block on the page surface; the image replaces the background. */\n :host([layout='split']) {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));\n align-items: center;\n gap: var(--st-space-48);\n overflow: visible;\n padding: var(--st-cms-section-padding) var(--st-space-24);\n background-color: var(--st-color-surface);\n background-image: none;\n color: var(--st-color-text-strong);\n animation: none;\n }\n\n :host([layout='split']) .overlay {\n display: none;\n }\n\n :host([layout='split']) .content {\n width: 100%;\n margin: 0;\n padding: 0;\n max-width: 560px;\n color: var(--st-hero-text-color, var(--st-color-text-strong));\n }\n\n :host([layout='split'][text-alignment='left']) .actions {\n justify-content: flex-start;\n }\n\n :host([layout='split']) .highlight svg {\n color: var(--st-color-accent);\n }\n\n .media {\n margin: 0;\n min-width: 0;\n }\n\n .image {\n display: block;\n width: 100%;\n height: auto;\n aspect-ratio: 4 / 3;\n object-fit: cover;\n border-radius: var(--st-radius-lg);\n box-shadow: 0 24px 48px -16px var(--st-shadow-color-lg);\n }\n\n @media (min-width: 768px) {\n .content { padding: var(--st-space-32); }\n }\n @media (min-width: 1024px) {\n .content { padding: var(--st-space-48); }\n :host([content-position='bottom']) .content { padding: var(--st-space-48) var(--st-space-32) 6rem; }\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host, .eyebrow, .title, .subtitle, .cta { animation: none; }\n .cta { transition: none; }\n }\n `;\n\n @property({ type: String })\n title = '';\n\n @property({ type: String })\n subtitle = '';\n\n @property({ type: String, attribute: 'image-src' })\n imageSrc = '';\n\n @property({ type: String, attribute: 'action-label' })\n actionLabel = '';\n\n @property({ type: String })\n href = '';\n\n @property({ type: String, reflect: true, attribute: 'text-alignment' })\n textAlignment: 'left' | 'center' | 'right' | 'justify' = 'left';\n\n @property({ type: String, reflect: true })\n padding: 'none' | 'small' | 'medium' | 'large' = 'none';\n\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n @property({ type: String, attribute: 'focal-point' })\n focalPoint: 'top' | 'left' | 'right' | 'bottom' | 'center' = 'center';\n\n /** Small uppercase label above the title (e.g. an event or product name). */\n @property({ type: String })\n eyebrow = '';\n\n /** Label of an outlined second button next to the primary one. */\n @property({ type: String, attribute: 'secondary-action-label' })\n secondaryActionLabel = '';\n\n @property({ type: String, attribute: 'secondary-href' })\n secondaryHref = '';\n\n /** Small pill above the title (e.g. \"New\", \"Now available\"). */\n @property({ type: String })\n badge = '';\n\n /** Check-marked points shown in a row under the buttons. */\n @property({ attribute: false })\n highlights: string[] = [];\n\n /** `background` (default) puts `image-src` behind the copy; `split` puts copy and image side by side. */\n @property({ type: String, reflect: true })\n layout: 'background' | 'split' = 'background';\n\n /** Alt text of the image in `layout=\"split\"`. Empty marks it as decorative. */\n @property({ type: String, attribute: 'image-alt' })\n imageAlt = '';\n\n /** `auto` sizes to the content; `large` is a tall editorial hero (`--st-hero-min-height`); `full` fills the viewport. */\n @property({ type: String, reflect: true })\n size: 'auto' | 'large' | 'full' = 'auto';\n\n /** `center` keeps the copy in the middle; `bottom` puts it in the lower part of the hero, on the page container. */\n @property({ type: String, reflect: true, attribute: 'content-position' })\n contentPosition: 'center' | 'bottom' = 'center';\n\n /** `even` darkens the whole image; `gradient` darkens behind the copy only; `none` shows the image as is. */\n @property({ type: String, reflect: true })\n overlay: 'even' | 'gradient' | 'none' = 'even';\n\n private static positions = {\n top: 'center top',\n left: 'left center',\n right: 'right center',\n bottom: 'center bottom',\n center: 'center center'\n } as const;\n\n protected willUpdate(): void {\n const asBackground = this.layout !== 'split';\n this.style.backgroundImage = asBackground && this.imageSrc ? `url(${JSON.stringify(this.imageSrc)})` : '';\n this.style.setProperty('--st-hero-position', Hero.positions[this.focalPoint] ?? Hero.positions.center);\n }\n\n private emitAction(event: MouseEvent, href: string, kind: 'primary' | 'secondary'): void {\n const action = new CustomEvent<HeroActionEventDetail>('action', {\n detail: { href, kind },\n bubbles: true,\n composed: true,\n cancelable: true\n });\n this.dispatchEvent(action);\n if (action.defaultPrevented) event.preventDefault();\n }\n\n private onClick = (event: MouseEvent): void => this.emitAction(event, this.href, 'primary');\n\n private onSecondaryClick = (event: MouseEvent): void => this.emitAction(event, this.secondaryHref, 'secondary');\n\n render() {\n return html`\n <div class=\"overlay\" part=\"overlay\"></div>\n <div class=\"content\" part=\"content\">\n ${this.eyebrow ? html`<p class=\"eyebrow\" part=\"eyebrow\">${this.eyebrow}</p>` : nothing}\n ${this.badge ? html`<p class=\"badge\" part=\"badge\">${this.badge}</p>` : nothing}\n <h1 class=\"title\" part=\"title\">${this.title}</h1>\n ${this.subtitle ? html`<p class=\"subtitle\" part=\"subtitle\">${this.subtitle}</p>` : nothing}\n ${this.actionLabel || this.secondaryActionLabel\n ? html`<div class=\"actions\" part=\"actions\">\n ${this.actionLabel\n ? html`<a class=\"cta\" part=\"action\" href=${this.href || nothing} @click=${this.onClick}>${this.actionLabel}</a>`\n : nothing}\n ${this.secondaryActionLabel\n ? html`<a class=\"cta secondary\" part=\"secondary-action\" href=${this.secondaryHref || nothing} @click=${this.onSecondaryClick}\n >${this.secondaryActionLabel}</a\n >`\n : nothing}\n </div>`\n : nothing}\n ${this.highlights.length\n ? html`<ul class=\"highlights\" part=\"highlights\">\n ${this.highlights.map(\n (text) => html`<li class=\"highlight\" part=\"highlight\">${HIGHLIGHT_ICON}<span>${text}</span></li>`\n )}\n </ul>`\n : nothing}\n <slot></slot>\n </div>\n ${this.layout === 'split' && this.imageSrc\n ? html`<figure class=\"media\" part=\"media\">\n <img class=\"image\" part=\"image\" src=${this.imageSrc} alt=${this.imageAlt} />\n </figure>`\n : nothing}\n `;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/shared/markdown.ts
|
|
2
|
+
import DOMPurify from "dompurify";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
function renderMarkdown(source) {
|
|
5
|
+
return DOMPurify.sanitize(marked.parse(source, { gfm: true, breaks: true, async: false }));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
renderMarkdown
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=chunk-UAIUBPDN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/markdown.ts"],"sourcesContent":["import DOMPurify from 'dompurify';\nimport { marked } from 'marked';\n\n/**\n * Renders Markdown (GFM, single newlines become `<br>`) to HTML sanitized with DOMPurify. The one\n * place the CMS turns authored Markdown into markup; render the result with `unsafeHTML`.\n */\nexport function renderMarkdown(source: string): string {\n return DOMPurify.sanitize(marked.parse(source, { gfm: true, breaks: true, async: false }));\n}\n"],"mappings":";AAAA,OAAO,eAAe;AACtB,SAAS,cAAc;AAMhB,SAAS,eAAe,QAAwB;AACrD,SAAO,UAAU,SAAS,OAAO,MAAM,QAAQ,EAAE,KAAK,MAAM,QAAQ,MAAM,OAAO,MAAM,CAAC,CAAC;AAC3F;","names":[]}
|
|
@@ -62,7 +62,7 @@ var Header = class extends LitElement {
|
|
|
62
62
|
* own `inline` setting is left alone. */
|
|
63
63
|
this.inlinedDropdowns = /* @__PURE__ */ new Set();
|
|
64
64
|
/** While the hamburger layout is on, the dropdowns in the header open in the flow of the
|
|
65
|
-
* full-screen panel instead of floating over it (
|
|
65
|
+
* full-screen panel instead of floating over it (the touch-device layout). */
|
|
66
66
|
this.syncInlineDropdowns = () => {
|
|
67
67
|
const collapsed = this.compact || !!this.narrowQuery?.matches;
|
|
68
68
|
const dropdowns = Array.from(this.querySelectorAll("st-dropdown"));
|
|
@@ -255,8 +255,8 @@ Header.styles = css`
|
|
|
255
255
|
align-items: center;
|
|
256
256
|
/* The containing block of an st-dropdown[mega] panel, which spans the bar. */
|
|
257
257
|
position: relative;
|
|
258
|
-
/* Spacing between brand, menu and utility controls is the rows' own margins
|
|
259
|
-
|
|
258
|
+
/* Spacing between brand, menu and utility controls is the rows' own margins, not a
|
|
259
|
+
flex gap. */
|
|
260
260
|
gap: 0;
|
|
261
261
|
/* !important on the box-model properties only: a shadow host's own :host rule always loses
|
|
262
262
|
* to ANY rule the consuming page has for the same property on that element, regardless of
|
|
@@ -409,7 +409,7 @@ Header.styles = css`
|
|
|
409
409
|
display: contents;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
/* Desktop layout
|
|
412
|
+
/* Desktop layout: the menu starts 4rem after the brand and takes the
|
|
413
413
|
free width; the utility controls sit after a vertical divider. Between 901px and 1200px the
|
|
414
414
|
gaps tighten so the menu still fits down to the 900px hamburger breakpoint. */
|
|
415
415
|
.menu-row {
|
|
@@ -532,7 +532,7 @@ Header.styles = css`
|
|
|
532
532
|
color: var(--st-color-accent-text);
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
-
/* Hamburger layout
|
|
535
|
+
/* Hamburger layout: a full-screen layer under the toggle, menu above
|
|
536
536
|
the utility controls, the menu's dropdowns opening in the flow (see syncInlineDropdowns). */
|
|
537
537
|
@media (max-width: ${BREAKPOINT_TABLET}px) {
|
|
538
538
|
:host {
|
|
@@ -705,4 +705,4 @@ Header = __decorateClass([
|
|
|
705
705
|
export {
|
|
706
706
|
Header
|
|
707
707
|
};
|
|
708
|
-
//# sourceMappingURL=chunk-
|
|
708
|
+
//# sourceMappingURL=chunk-XUGEXERO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/header/Header.ts"],"sourcesContent":["import { LitElement, css, html, nothing } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\n\nimport { chevronIcon, closeIcon, menuIcon } from '../shared/icons';\nimport { BREAKPOINT_TABLET, buttonResetStyles, controlBaseStyles, dropdownPanelStyles, rootFontFamilyStyles } from '../shared/styles';\nimport type { LanguageChangedEventDetail } from './HeaderEvents';\n\n/**\n * The app-shell header bar — brand (image + title, or title alone) on the left, a `slot=\"menu\"`\n * for primary nav (e.g. `st-dropdown`/`st-menu-grid` mega-menus, or plain links) taking the\n * middle, and a `slot=\"utility\"` for account/app-switcher/theme controls on the right. `title` is\n * the primary brand prop; `brand-image-url` is optional. For a fixed nav-links/user-menu header,\n * use `st-app-header` instead.\n *\n * Layout: the menu starts 4rem after the brand, the utility\n * controls sit after a vertical divider, and below `BREAKPOINT_TABLET` (900px) `menu`/`utility`\n * collapse behind a hamburger toggle into a full-screen layer under the\n * toggle (closes on Escape or a click inside it), where the menu's `st-dropdown`s are switched to\n * `inline` so they open in the flow — the same two slotted elements just get repositioned by CSS\n * (`.mobile-panel`'s `display: contents` at desktop width vs. a real fixed-position box below\n * `BREAKPOINT_TABLET`), rather than the slots being duplicated in the template, since a shadow\n * root can only ever assign a given light-DOM child to the *first* slot of a given name.\n *\n * An optional `slot=\"brand-extra\"` sits right after the brand (e.g. an edition badge such as\n * \"🇮🇳 India Edition\"); unlike `menu`/`utility` it stays in the bar at every width.\n *\n * Also carries an optional built-in language switcher (`languages`/`currentLanguageCode`, fires\n * `language-change`). Hidden entirely when `languages` has fewer than two entries.\n * This component has no translation logic itself; the consuming app owns what a language `code`\n * means and which content it re-renders in response.\n *\n * @slot menu - Primary navigation.\n * @slot utility - Account, app-switcher and theme controls, after the divider.\n * @slot brand-extra - Shown right after the brand at every width (e.g. an edition badge).\n * @slot menu-icon - Replaces the hamburger on the narrow-screen toggle (the built-in glyph when empty).\n * @slot close-icon - Replaces the close (X) on the toggle while the menu is open (the built-in glyph when empty).\n * @cssprop --st-header-toggle-icon-size - Width and height of a slotted toggle icon (default 20px, the built-in glyph's size).\n */\n@customElement('st-header')\nexport class Header extends LitElement {\n static styles = css`\n ${controlBaseStyles}\n\n :host {\n display: flex;\n align-items: center;\n /* The containing block of an st-dropdown[mega] panel, which spans the bar. */\n position: relative;\n /* Spacing between brand, menu and utility controls is the rows' own margins, not a\n flex gap. */\n gap: 0;\n /* !important on the box-model properties only: a shadow host's own :host rule always loses\n * to ANY rule the consuming page has for the same property on that element, regardless of\n * specificity (see st-footer's own padding/border-top for the same fix). Without\n * box-sizing: border-box pinned here, a page that also sets its own padding on this host\n * (e.g. a p-2 utility class) adds that padding on TOP of the fixed 64px height instead of\n * being absorbed inside it — which is exactly what happens in Template Hub, the one app that\n * doesn't load Tailwind Preflight (Preflight's own box-sizing: border-box reset is what\n * silently keeps every other app's header at the intended 64px). */\n box-sizing: border-box !important;\n padding: 0 2rem !important;\n height: 64px !important;\n background: var(--st-color-surface);\n border-bottom: 1px solid var(--st-color-border) !important;\n ${rootFontFamilyStyles}\n color: var(--st-color-text-strong);\n }\n\n :host([sticky]) {\n position: sticky;\n top: 0;\n z-index: 100;\n }\n\n /* Toggle icons: a host's slotted icon, else the built-in glyph; only the current state's shows. */\n .toggle slot {\n display: contents;\n }\n\n .toggle slot[hidden] {\n display: none;\n }\n\n .toggle ::slotted(*) {\n flex: none;\n width: var(--st-header-toggle-icon-size, 20px);\n height: var(--st-header-toggle-icon-size, 20px);\n }\n\n :host([inverted]) {\n background: var(--st-color-text-strong);\n color: var(--st-color-on-solid);\n border-bottom-color: transparent;\n }\n\n :host([sticky]),\n :host([transparent]),\n :host([hide-on-scroll]) {\n transition:\n background-color 0.3s ease,\n color 0.3s ease,\n box-shadow 0.3s ease,\n border-color 0.3s ease,\n transform 0.3s ease;\n }\n\n /* Once the page has scrolled, a sticky/fixed header lifts off the content. */\n :host([scrolled][sticky]),\n :host([scrolled][transparent]),\n :host([scrolled][hide-on-scroll]) {\n box-shadow: 0 4px 16px var(--st-shadow-color-md);\n border-bottom-color: transparent !important;\n }\n\n /* transparent: fixed over the top of the page (e.g. a dark hero), see-through until scrolled. */\n :host([transparent]) {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n z-index: 100;\n }\n\n :host([transparent]:not([scrolled]):not([mobile-open])) {\n background: transparent;\n border-bottom-color: transparent !important;\n color: var(--st-header-transparent-color, var(--st-color-on-solid));\n /* Read by st-menu-item / st-dropdown triggers in the bar (their panels reset them). */\n --st-nav-color: var(--st-header-transparent-color, var(--st-color-on-solid));\n --st-nav-hover-bg: color-mix(in oklch, currentColor 14%, transparent);\n }\n\n :host([transparent]:not([scrolled]):not([mobile-open])) .utility-row {\n border-left-color: color-mix(in oklch, currentColor 35%, transparent);\n }\n\n :host([transparent]:not([scrolled]):not([mobile-open])) .lang-trigger {\n color: inherit;\n }\n\n :host([transparent]:not([scrolled]):not([mobile-open])) .lang-trigger:hover,\n :host([transparent]:not([scrolled]):not([mobile-open])) .toggle:hover {\n background: color-mix(in oklch, currentColor 14%, transparent);\n }\n\n /* hide-on-scroll: slides away while scrolling down, back as soon as the user scrolls up. */\n :host([hide-on-scroll]:not([sticky]):not([transparent])) {\n position: sticky;\n top: 0;\n z-index: 100;\n }\n\n :host([scroll-hidden]:not([mobile-open])) {\n transform: translateY(-100%);\n box-shadow: none;\n }\n\n .brand {\n display: flex;\n align-items: center;\n gap: var(--st-space-10);\n flex: none;\n text-decoration: none;\n color: inherit;\n }\n\n .brand-mark {\n width: var(--st-size-28);\n height: var(--st-size-28);\n border-radius: var(--st-radius-sm);\n flex: none;\n object-fit: contain;\n }\n\n .brand-title {\n font-size: var(--st-text-xl);\n font-weight: var(--st-font-weight-bold);\n letter-spacing: var(--st-tracking-tight);\n white-space: nowrap;\n }\n\n /* Optional slot=\"brand-extra\" (e.g. an edition badge) right after the brand. Outside the\n mobile panel, so it stays beside the logo at every width; no box at all when empty. */\n .brand-extra {\n display: flex;\n align-items: center;\n gap: var(--st-space-8);\n flex: none;\n margin-left: var(--st-space-10);\n white-space: nowrap;\n }\n\n .brand-extra.empty {\n display: none;\n }\n\n /* Desktop: a transparent pass-through so .menu-row/.utility-row lay out directly as :host's\n own flex children. Mobile (see below) turns this into the actual overlay box. */\n .mobile-panel {\n display: contents;\n }\n\n /* Desktop layout: the menu starts 4rem after the brand and takes the\n free width; the utility controls sit after a vertical divider. Between 901px and 1200px the\n gaps tighten so the menu still fits down to the 900px hamburger breakpoint. */\n .menu-row {\n flex: 1;\n display: flex;\n align-items: center;\n gap: 1.5rem;\n min-width: 0;\n margin-left: 4rem;\n }\n\n .utility-row {\n flex: none;\n display: flex;\n align-items: center;\n gap: 1rem;\n margin-left: 1.5rem;\n padding-left: 1.5rem;\n border-left: 1px solid var(--st-color-border);\n }\n\n :host([inverted]) .utility-row {\n border-left-color: currentColor;\n }\n\n .utility-row.empty {\n display: none;\n }\n\n @media (max-width: 1200px) {\n :host {\n padding: 0 1rem !important;\n }\n\n .menu-row {\n margin-left: 0.5rem;\n }\n\n .utility-row {\n margin-left: 0.5rem;\n padding-left: 0.5rem;\n }\n }\n\n @keyframes st-header-fade-in {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n\n .toggle {\n display: none;\n align-items: center;\n justify-content: center;\n flex: none;\n position: relative;\n z-index: 1002;\n width: var(--st-size-32);\n height: var(--st-size-32);\n margin-left: auto;\n padding: 0.5rem;\n border: 0;\n border-radius: var(--st-radius-sm);\n background: transparent;\n color: inherit;\n cursor: pointer;\n }\n\n .toggle:hover {\n background: var(--st-color-hover);\n }\n\n .lang-menu {\n position: relative;\n flex: none;\n }\n\n .lang-trigger {\n ${buttonResetStyles}\n display: flex;\n align-items: center;\n gap: var(--st-space-4);\n height: var(--st-size-32);\n padding: 0 var(--st-space-8);\n border-radius: var(--st-radius-sm);\n font-size: var(--st-text-base);\n font-weight: var(--st-font-weight-semibold);\n color: var(--st-color-text-muted);\n }\n\n .lang-trigger:hover {\n background: var(--st-color-hover);\n color: var(--st-color-text-strong);\n }\n\n .lang-dropdown {\n ${dropdownPanelStyles}\n min-width: 140px;\n }\n\n .lang-option {\n ${buttonResetStyles}\n display: block;\n width: 100%;\n padding: var(--st-space-10) var(--st-space-14);\n font-size: var(--st-text-base);\n font-weight: var(--st-font-weight-semibold);\n text-align: left;\n color: inherit;\n }\n\n .lang-option:hover {\n background: var(--st-color-hover);\n }\n\n .lang-option.active {\n color: var(--st-color-accent-text);\n }\n\n /* Hamburger layout: a full-screen layer under the toggle, menu above\n the utility controls, the menu's dropdowns opening in the flow (see syncInlineDropdowns). */\n @media (max-width: ${BREAKPOINT_TABLET}px) {\n :host {\n padding: 0 1rem !important;\n }\n\n .mobile-panel {\n display: none;\n }\n\n .toggle {\n display: inline-flex;\n }\n\n :host([mobile-open]) .mobile-panel {\n display: flex;\n flex-direction: column;\n gap: 2rem;\n position: fixed;\n inset: 0;\n background: var(--st-color-surface);\n padding: 5rem 1.5rem 2rem;\n overflow-y: auto;\n z-index: 1001;\n animation: st-header-fade-in 0.3s ease;\n }\n\n :host([mobile-open]) .menu-row,\n :host([mobile-open]) .utility-row {\n flex-direction: column;\n align-items: stretch;\n gap: 1.5rem;\n margin-left: 0;\n }\n\n :host([mobile-open]) .utility-row {\n padding: 1.5rem 0 0;\n border-left: 0;\n border-top: 1px solid var(--st-color-border);\n }\n\n /* Apps usually slot one flex-row wrapper per slot; stack its items as the source does. */\n :host([mobile-open]) ::slotted([slot='menu']),\n :host([mobile-open]) ::slotted([slot='utility']) {\n flex-direction: column !important;\n align-items: stretch !important;\n margin: 0 !important;\n }\n }\n\n /* The same collapsed layout, switched on by script when the menu no longer fits the bar at\n desktop width (see evaluateFit) — without it the menu spills over the utility controls. */\n :host([compact]) .mobile-panel {\n display: none;\n }\n\n :host([compact]) .toggle {\n display: inline-flex;\n }\n\n :host([compact][mobile-open]) .mobile-panel {\n display: flex;\n flex-direction: column;\n gap: 2rem;\n position: fixed;\n inset: 0;\n background: var(--st-color-surface);\n padding: 5rem 1.5rem 2rem;\n overflow-y: auto;\n z-index: 1001;\n animation: st-header-fade-in 0.3s ease;\n }\n\n :host([compact][mobile-open]) .menu-row,\n :host([compact][mobile-open]) .utility-row {\n flex-direction: column;\n align-items: stretch;\n gap: 1.5rem;\n margin-left: 0;\n }\n\n :host([compact][mobile-open]) .utility-row {\n padding: 1.5rem 0 0;\n border-left: 0;\n border-top: 1px solid var(--st-color-border);\n }\n\n :host([compact][mobile-open]) ::slotted([slot='menu']),\n :host([compact][mobile-open]) ::slotted([slot='utility']) {\n flex-direction: column !important;\n align-items: stretch !important;\n margin: 0 !important;\n }\n\n @media (prefers-reduced-motion: reduce) {\n :host,\n .mobile-panel {\n animation: none !important;\n transition: none !important;\n }\n }\n `;\n\n @property({ type: String })\n title = '';\n\n /** Optional logo image URL, shown before `title`. Omitted entirely when empty — none of this suite's apps has one today. Matches the source component's `imageSrc` (renamed here to pair with `title`/`logoHref`, since this component's only image use is the brand mark). */\n @property({ type: String, attribute: 'brand-image-url' })\n brandImageUrl = '';\n\n /** `alt` text for the brand image — matches the source component's `altText` (default `'Logo'`). */\n @property({ type: String, attribute: 'alt-text' })\n altText = 'Logo';\n\n /** Wraps the brand in an `<a>` when set (e.g. `/`); a plain non-link `<div>` otherwise. */\n @property({ type: String })\n logoHref = '';\n\n /** `target` for the brand `<a>` — matches the source component's `logoTarget` (default `_self`). No effect when `logoHref` isn't set. */\n @property({ type: String, attribute: 'logo-target' })\n logoTarget = '_self';\n\n /** Extra class(es) appended to the brand link/div — matches the source component's `logoClassName`. */\n @property({ type: String, attribute: 'logo-class-name' })\n logoClassName = '';\n\n /** Inverts the header's color roles (light-on-dark instead of dark-on-light) — matches the\n * source component's `inverted` (from its shared `BaseProps`). */\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n @property({ type: Boolean, reflect: true })\n sticky = false;\n\n /**\n * Fixes the header over the top of the page and makes it see-through, with light text\n * (`--st-header-transparent-color`), until the page scrolls past `scroll-threshold` — for a\n * header laid over a dark hero. It then takes its normal background and a shadow. The page\n * content starts under it, so the first section should leave room for the 64px bar.\n */\n @property({ type: Boolean, reflect: true })\n transparent = false;\n\n /** Slides the header out of view while the page scrolls down and back in when it scrolls up. */\n @property({ type: Boolean, reflect: true, attribute: 'hide-on-scroll' })\n hideOnScroll = false;\n\n /** Scroll distance in px after which the header counts as `scrolled`. */\n @property({ type: Number, attribute: 'scroll-threshold' })\n scrollThreshold = 8;\n\n /** True once the page has scrolled past `scroll-threshold`; reflected so pages can style `st-header[scrolled]`. Set by the header, never from outside. */\n @property({ type: Boolean, reflect: true })\n scrolled = false;\n\n /** True while `hide-on-scroll` has slid the header away. Set by the header, never from outside. */\n @property({ type: Boolean, reflect: true, attribute: 'scroll-hidden' })\n scrollHidden = false;\n\n private lastScrollY = 0;\n private scrollFrame = 0;\n\n private onScroll = (): void => {\n if (this.scrollFrame) return;\n this.scrollFrame = requestAnimationFrame(() => {\n this.scrollFrame = 0;\n const y = Math.max(0, window.scrollY);\n this.scrolled = y > this.scrollThreshold;\n if (this.hideOnScroll) {\n const delta = y - this.lastScrollY;\n // Ignore tiny jitters; never hide while still at the top of the page or with the menu open.\n if (Math.abs(delta) > 4) this.scrollHidden = delta > 0 && y > this.offsetHeight && !this.mobileOpen;\n } else {\n this.scrollHidden = false;\n }\n this.lastScrollY = y;\n });\n };\n\n /**\n * Language switcher options, e.g. `[{ code: 'en', label: 'English' }, ...]` — hidden entirely\n * when empty or when only one language is offered (same convention as `st-app-header`'s\n * `languages`, which this reuses verbatim). This component has no translation logic itself —\n * selecting an option fires `language-change` (detail: `{ code }`), and the consuming app owns\n * what `code` actually means and which content it re-renders.\n */\n @property({ attribute: false })\n languages: { code: string; label: string }[] = [];\n\n @property({ type: String, attribute: 'current-language-code' })\n currentLanguageCode = '';\n\n @property({ type: String, attribute: 'language-menu-label' })\n languageMenuLabel = 'Language';\n\n /** Internal open/closed state for the mobile nav overlay, reflected to an attribute purely so\n * the `:host([mobile-open])` CSS selector above can react to it — not meant to be set from\n * outside. */\n @property({ type: Boolean, reflect: true, attribute: 'mobile-open' })\n mobileOpen = false;\n\n /** True while the menu doesn't fit the bar at the current width: the header then shows the same\n * hamburger layout it uses on a narrow screen. Set by `evaluateFit`, never from outside. */\n @property({ type: Boolean, reflect: true })\n compact = false;\n\n @state() private languageMenuOpen = false;\n\n /** Whether `slot=\"utility\"` has content — an empty utility row (and its divider) is hidden. */\n @state() private hasUtility = false;\n\n /** Whether `slot=\"brand-extra\"` has content — an empty one takes no room beside the brand. */\n @state() private hasBrandExtra = false;\n\n private resizeObserver?: ResizeObserver;\n\n /** Matches below BREAKPOINT_TABLET, where the hamburger layout is always on. */\n private narrowQuery?: MediaQueryList;\n\n /** The dropdowns this header switched to `inline` — only these are switched back, so a caller's\n * own `inline` setting is left alone. */\n private inlinedDropdowns = new Set<HTMLElement & { inline: boolean; open: boolean }>();\n\n /** While the hamburger layout is on, the dropdowns in the header open in the flow of the\n * full-screen panel instead of floating over it (the touch-device layout). */\n private syncInlineDropdowns = (): void => {\n const collapsed = this.compact || !!this.narrowQuery?.matches;\n const dropdowns = Array.from(this.querySelectorAll('st-dropdown')) as (HTMLElement & { inline: boolean; open: boolean })[];\n if (collapsed) {\n for (const dropdown of dropdowns) {\n if (dropdown.inline) continue;\n dropdown.inline = true;\n this.inlinedDropdowns.add(dropdown);\n }\n } else {\n for (const dropdown of this.inlinedDropdowns) dropdown.inline = false;\n this.inlinedDropdowns.clear();\n }\n };\n\n private onBrandExtraSlotChange = (event: Event): void => {\n this.hasBrandExtra = (event.target as HTMLSlotElement).assignedElements({ flatten: true }).length > 0;\n this.evaluateFit();\n };\n\n private onUtilitySlotChange = (event: Event): void => {\n this.hasUtility = (event.target as HTMLSlotElement).assignedElements({ flatten: true }).length > 0;\n this.evaluateFit();\n };\n\n private onMenuSlotChange = (): void => {\n this.evaluateFit();\n this.syncInlineDropdowns();\n };\n\n protected updated(changed: Map<string, unknown>): void {\n if (changed.has('compact')) this.syncInlineDropdowns();\n // A mega dropdown's drill-down panel would otherwise still cover the menu next time it opens.\n if (changed.has('mobileOpen') && !this.mobileOpen) {\n for (const dropdown of this.inlinedDropdowns) dropdown.open = false;\n }\n }\n\n /** The bar width at which the menu last overflowed — the bar only expands again once it is\n * clearly wider than this, so it can't flip back and forth right at the threshold. */\n private collapsedAtWidth = 0;\n\n /** Collapses to the hamburger layout when the slotted menu overflows its row, and expands again\n * once there is room. The collapsed layout hides the menu, so the room it needs can't be\n * measured then; the width remembered when it collapsed stands in for it. */\n private evaluateFit = (): void => {\n const width = this.clientWidth;\n if (this.compact) {\n if (width > this.collapsedAtWidth + 24) {\n this.compact = false;\n this.mobileOpen = false;\n void this.updateComplete.then(this.evaluateFit);\n }\n return;\n }\n if (this.menuOverflows()) {\n this.collapsedAtWidth = width;\n this.compact = true;\n }\n };\n\n /** Whether the visible menu items reach past the right edge of the menu row. Measured from the\n * items' own boxes (the slotted elements and their direct children), not the row's scrollWidth:\n * a closed dropdown's absolutely positioned panel also counts towards scrollWidth, which would\n * report overflow at any width. */\n private menuOverflows(): boolean {\n const row = this.renderRoot.querySelector<HTMLElement>('.menu-row');\n const slot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"menu\"]');\n if (!row || !slot) return false;\n const limit = row.getBoundingClientRect().right + 1;\n let right = 0;\n for (const el of slot.assignedElements({ flatten: true })) {\n for (const box of [el, ...Array.from(el.children)]) {\n const rect = box.getBoundingClientRect();\n if (rect.width > 0) right = Math.max(right, rect.right);\n }\n }\n return right > limit;\n }\n\n protected firstUpdated(): void {\n this.resizeObserver = new ResizeObserver(this.evaluateFit);\n this.resizeObserver.observe(this);\n this.evaluateFit();\n this.syncInlineDropdowns();\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n window.addEventListener('keydown', this.closeOnEscape);\n window.addEventListener('click', this.closeLanguageMenuOnOutsideClick);\n window.addEventListener('scroll', this.onScroll, { passive: true });\n this.onScroll();\n this.narrowQuery = window.matchMedia(`(max-width: ${BREAKPOINT_TABLET}px)`);\n this.narrowQuery.addEventListener('change', this.syncInlineDropdowns);\n // Menus often render their dropdowns after the header (e.g. once the user's role loads),\n // deeper than a slotchange reports.\n this.lightDomObserver = new MutationObserver(this.syncInlineDropdowns);\n this.lightDomObserver.observe(this, { childList: true, subtree: true });\n }\n\n private lightDomObserver?: MutationObserver;\n\n disconnectedCallback(): void {\n this.lightDomObserver?.disconnect();\n this.narrowQuery?.removeEventListener('change', this.syncInlineDropdowns);\n this.resizeObserver?.disconnect();\n window.removeEventListener('keydown', this.closeOnEscape);\n window.removeEventListener('click', this.closeLanguageMenuOnOutsideClick);\n window.removeEventListener('scroll', this.onScroll);\n if (this.scrollFrame) cancelAnimationFrame(this.scrollFrame);\n this.scrollFrame = 0;\n super.disconnectedCallback();\n }\n\n private closeOnEscape = (event: KeyboardEvent): void => {\n if (this.mobileOpen && event.key === 'Escape') this.mobileOpen = false;\n if (this.languageMenuOpen && event.key === 'Escape') this.languageMenuOpen = false;\n };\n\n private closeLanguageMenuOnOutsideClick = (event: Event): void => {\n if (this.languageMenuOpen && !event.composedPath().includes(this)) this.languageMenuOpen = false;\n };\n\n private renderLanguageMenu() {\n if (this.languages.length < 2) return nothing;\n const current = this.languages.find((lang) => lang.code === this.currentLanguageCode);\n return html`\n <div class=\"lang-menu\" part=\"language-menu\">\n <button\n part=\"language-menu-trigger\"\n type=\"button\"\n class=\"lang-trigger\"\n aria-label=${this.languageMenuLabel}\n aria-expanded=${this.languageMenuOpen}\n @click=${(event: Event) => {\n event.stopPropagation();\n this.languageMenuOpen = !this.languageMenuOpen;\n }}\n >\n <span part=\"language-menu-current\">${current?.label ?? this.languageMenuLabel}</span>\n ${chevronIcon('down', 10)}\n </button>\n ${this.languageMenuOpen\n ? html`\n <div part=\"language-dropdown\" class=\"lang-dropdown\">\n ${this.languages.map(\n (lang) => html`\n <button\n part=\"language-option\"\n type=\"button\"\n class=\"lang-option ${lang.code === this.currentLanguageCode ? 'active' : ''}\"\n aria-current=${lang.code === this.currentLanguageCode}\n @click=${() => this.onLanguageSelect(lang.code)}\n >\n ${lang.label}\n </button>\n `\n )}\n </div>\n `\n : nothing}\n </div>\n `;\n }\n\n private onLanguageSelect(code: string): void {\n this.languageMenuOpen = false;\n if (code === this.currentLanguageCode) return;\n this.dispatchEvent(new CustomEvent<LanguageChangedEventDetail>('language-change', { detail: { code }, bubbles: true, composed: true }));\n }\n\n private renderBrand() {\n const content = html`\n ${this.brandImageUrl ? html`<img part=\"brand-mark\" class=\"brand-mark\" src=${this.brandImageUrl} alt=${this.altText} /> ` : nothing}\n <span part=\"brand-title\" class=\"brand-title\">${this.title}</span>\n `;\n return this.logoHref\n ? html`<a part=\"brand\" class=\"brand ${this.logoClassName}\" href=${this.logoHref} target=${this.logoTarget}>${content}</a>`\n : html`<div part=\"brand\" class=\"brand ${this.logoClassName}\">${content}</div>`;\n }\n\n render() {\n return html`\n ${this.renderBrand()}\n <div class=\"brand-extra ${this.hasBrandExtra ? '' : 'empty'}\" part=\"brand-extra\"><slot name=\"brand-extra\" @slotchange=${this.onBrandExtraSlotChange}></slot></div>\n\n <div class=\"mobile-panel\" part=\"mobile-panel\" @click=${() => (this.mobileOpen = false)}>\n <div class=\"menu-row\" part=\"menu-row\"><slot name=\"menu\" @slotchange=${this.onMenuSlotChange}></slot></div>\n <div class=\"utility-row ${this.hasUtility || this.languages.length >= 2 ? '' : 'empty'}\" part=\"utility-row\">\n ${this.renderLanguageMenu()}<slot name=\"utility\" @slotchange=${this.onUtilitySlotChange}></slot>\n </div>\n </div>\n\n <button\n part=\"toggle\"\n type=\"button\"\n class=\"toggle\"\n aria-label=${this.mobileOpen ? 'Close menu' : 'Open menu'}\n aria-expanded=${this.mobileOpen}\n @click=${() => (this.mobileOpen = !this.mobileOpen)}\n >\n <slot name=\"menu-icon\" ?hidden=${this.mobileOpen}>${menuIcon()}</slot>\n <slot name=\"close-icon\" ?hidden=${!this.mobileOpen}>${closeIcon()}</slot>\n </button>\n `;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sectionHeaderStyles,
|
|
3
|
+
sectionSubtitleStyles,
|
|
4
|
+
sectionTitleStyles
|
|
5
|
+
} from "./chunk-FZ36KL73.js";
|
|
6
|
+
import {
|
|
7
|
+
controlBaseStyles,
|
|
8
|
+
fontFamilyStyles
|
|
9
|
+
} from "./chunk-KMU6ABQF.js";
|
|
10
|
+
import {
|
|
11
|
+
__decorateClass
|
|
12
|
+
} from "./chunk-EUXUH3YW.js";
|
|
13
|
+
|
|
14
|
+
// src/steps/Steps.ts
|
|
15
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
16
|
+
import { customElement, property } from "lit/decorators.js";
|
|
17
|
+
var Steps = class extends LitElement {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(...arguments);
|
|
20
|
+
this.eyebrow = "";
|
|
21
|
+
this.title = "";
|
|
22
|
+
this.subtitle = "";
|
|
23
|
+
this.items = [];
|
|
24
|
+
this.highlightLast = false;
|
|
25
|
+
this.inverted = false;
|
|
26
|
+
}
|
|
27
|
+
render() {
|
|
28
|
+
const heading = this.eyebrow || this.title || this.subtitle;
|
|
29
|
+
const last = this.items.length - 1;
|
|
30
|
+
return html`
|
|
31
|
+
<div class="container" part="container">
|
|
32
|
+
${heading ? html`<div class="header" part="header">
|
|
33
|
+
${this.eyebrow ? html`<p class="eyebrow" part="eyebrow">${this.eyebrow}</p>` : nothing}
|
|
34
|
+
${this.title ? html`<h2 class="title" part="title">${this.title}</h2>` : nothing}
|
|
35
|
+
${this.subtitle ? html`<p class="subtitle" part="subtitle">${this.subtitle}</p>` : nothing}
|
|
36
|
+
</div>` : nothing}
|
|
37
|
+
<ol class="grid" part="grid" aria-label=${this.title || nothing}>
|
|
38
|
+
${this.items.map(
|
|
39
|
+
(step, index) => html`
|
|
40
|
+
<li class="card ${this.highlightLast && index === last ? "highlight" : ""}" part="card">
|
|
41
|
+
<span class="number" part="number" aria-hidden="true">${String(index + 1).padStart(2, "0")}</span>
|
|
42
|
+
<h3 class="card-title" part="card-title">${step.title}</h3>
|
|
43
|
+
${step.description ? html`<p class="description" part="description">${step.description}</p>` : nothing}
|
|
44
|
+
</li>
|
|
45
|
+
`
|
|
46
|
+
)}
|
|
47
|
+
</ol>
|
|
48
|
+
</div>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
Steps.styles = css`
|
|
53
|
+
${controlBaseStyles}
|
|
54
|
+
${fontFamilyStyles}
|
|
55
|
+
|
|
56
|
+
:host {
|
|
57
|
+
display: block;
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
/* !important: a page-level reset (e.g. Tailwind Preflight) otherwise zeroes a shadow host's
|
|
60
|
+
padding; override with --st-steps-padding instead. */
|
|
61
|
+
padding: var(--st-steps-padding, var(--st-cms-section-padding) var(--st-space-24)) !important;
|
|
62
|
+
background: var(--st-color-page);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:host([inverted]) {
|
|
66
|
+
background: var(--st-cms-band-dark-bg);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.container {
|
|
70
|
+
max-width: var(--st-cms-container-max);
|
|
71
|
+
margin: 0 auto;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.header {
|
|
75
|
+
${sectionHeaderStyles}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.eyebrow {
|
|
79
|
+
margin: 0 0 var(--st-space-12);
|
|
80
|
+
font-size: var(--st-text-sm);
|
|
81
|
+
font-weight: var(--st-font-weight-bold);
|
|
82
|
+
letter-spacing: 0.12em;
|
|
83
|
+
text-transform: uppercase;
|
|
84
|
+
color: var(--st-color-text-muted);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.title {
|
|
88
|
+
${sectionTitleStyles}
|
|
89
|
+
color: inherit;
|
|
90
|
+
letter-spacing: -0.02em;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.subtitle {
|
|
94
|
+
${sectionSubtitleStyles}
|
|
95
|
+
max-width: 60ch;
|
|
96
|
+
margin: 0 auto;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.grid {
|
|
100
|
+
list-style: none;
|
|
101
|
+
margin: 0;
|
|
102
|
+
padding: 0;
|
|
103
|
+
display: grid;
|
|
104
|
+
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
|
|
105
|
+
gap: var(--st-space-24);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.card {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
gap: var(--st-space-12);
|
|
112
|
+
box-sizing: border-box;
|
|
113
|
+
margin: 0;
|
|
114
|
+
padding: var(--st-space-32);
|
|
115
|
+
border: 1px solid var(--st-color-border);
|
|
116
|
+
border-radius: var(--st-radius-lg);
|
|
117
|
+
background: var(--st-color-surface);
|
|
118
|
+
color: var(--st-color-text);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.number {
|
|
122
|
+
font-size: var(--st-cms-text-3xl);
|
|
123
|
+
font-weight: var(--st-font-weight-bold);
|
|
124
|
+
line-height: 1;
|
|
125
|
+
letter-spacing: -0.02em;
|
|
126
|
+
color: var(--st-color-accent);
|
|
127
|
+
font-variant-numeric: tabular-nums;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.card-title {
|
|
131
|
+
margin: var(--st-space-8) 0 0;
|
|
132
|
+
font-size: var(--st-text-xl);
|
|
133
|
+
font-weight: var(--st-font-weight-semibold);
|
|
134
|
+
line-height: 1.3;
|
|
135
|
+
color: var(--st-color-text-strong);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.description {
|
|
139
|
+
margin: 0;
|
|
140
|
+
font-size: var(--st-text-md);
|
|
141
|
+
line-height: 1.6;
|
|
142
|
+
color: var(--st-color-text-muted);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* inverted: a dark band with light cards; the number moves to the cyan highlight so it reads on navy. */
|
|
146
|
+
:host([inverted]) .card {
|
|
147
|
+
background: color-mix(in oklch, var(--st-cms-band-dark-color) 6%, transparent);
|
|
148
|
+
border-color: color-mix(in oklch, var(--st-cms-band-dark-color) 16%, transparent);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:host([inverted]) .number {
|
|
152
|
+
color: var(--st-cms-highlight);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
:host([inverted]) .title,
|
|
156
|
+
:host([inverted]) .card-title {
|
|
157
|
+
color: var(--st-cms-band-dark-color);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
:host([inverted]) .eyebrow,
|
|
161
|
+
:host([inverted]) .subtitle,
|
|
162
|
+
:host([inverted]) .description {
|
|
163
|
+
color: color-mix(in oklch, var(--st-cms-band-dark-color) 75%, transparent);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* highlightLast: the final card is the dark call-out. Declared after the inverted rules so it wins. */
|
|
167
|
+
.card.highlight {
|
|
168
|
+
background: var(--st-cms-navy);
|
|
169
|
+
border-color: var(--st-cms-navy);
|
|
170
|
+
color: var(--st-cms-on-dark);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.card.highlight .number {
|
|
174
|
+
color: var(--st-cms-highlight);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.card.highlight .card-title {
|
|
178
|
+
color: var(--st-cms-on-dark);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.card.highlight .description {
|
|
182
|
+
color: color-mix(in oklch, var(--st-cms-on-dark) 80%, transparent);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@media (max-width: 600px) {
|
|
186
|
+
:host {
|
|
187
|
+
padding: var(--st-steps-padding, var(--st-space-40) var(--st-space-20)) !important;
|
|
188
|
+
}
|
|
189
|
+
.card {
|
|
190
|
+
padding: var(--st-space-24);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
__decorateClass([
|
|
195
|
+
property({ type: String })
|
|
196
|
+
], Steps.prototype, "eyebrow", 2);
|
|
197
|
+
__decorateClass([
|
|
198
|
+
property({ type: String })
|
|
199
|
+
], Steps.prototype, "title", 2);
|
|
200
|
+
__decorateClass([
|
|
201
|
+
property({ type: String })
|
|
202
|
+
], Steps.prototype, "subtitle", 2);
|
|
203
|
+
__decorateClass([
|
|
204
|
+
property({ attribute: false })
|
|
205
|
+
], Steps.prototype, "items", 2);
|
|
206
|
+
__decorateClass([
|
|
207
|
+
property({ type: Boolean, reflect: true, attribute: "highlight-last" })
|
|
208
|
+
], Steps.prototype, "highlightLast", 2);
|
|
209
|
+
__decorateClass([
|
|
210
|
+
property({ type: Boolean, reflect: true })
|
|
211
|
+
], Steps.prototype, "inverted", 2);
|
|
212
|
+
Steps = __decorateClass([
|
|
213
|
+
customElement("st-steps")
|
|
214
|
+
], Steps);
|
|
215
|
+
|
|
216
|
+
export {
|
|
217
|
+
Steps
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=chunk-ZRC5BVDX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/steps/Steps.ts"],"sourcesContent":["import { LitElement, css, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\nimport { controlBaseStyles, fontFamilyStyles } from '../shared/styles';\nimport { sectionHeaderStyles, sectionSubtitleStyles, sectionTitleStyles } from '../shared/section-styles';\n\nexport interface StepItem {\n title: string;\n description?: string;\n}\n\n/**\n * A numbered process: a responsive grid of cards, each with an automatic \"01\", \"02\", ... number, a\n * title and a description. `highlightLast` makes the final card a dark navy call-out, the way\n * enterprise pages close a how-it-works sequence. `inverted` puts the whole block on the dark band.\n *\n * @cssprop --st-steps-padding - Overrides the block's padding.\n * @csspart header - The eyebrow/title/subtitle block.\n * @csspart card - Each step card (an `li`).\n * @csspart number - The step number.\n */\n@customElement('st-steps')\nexport class Steps extends LitElement {\n static styles = css`\n ${controlBaseStyles}\n ${fontFamilyStyles}\n\n :host {\n display: block;\n box-sizing: border-box;\n /* !important: a page-level reset (e.g. Tailwind Preflight) otherwise zeroes a shadow host's\n padding; override with --st-steps-padding instead. */\n padding: var(--st-steps-padding, var(--st-cms-section-padding) var(--st-space-24)) !important;\n background: var(--st-color-page);\n }\n\n :host([inverted]) {\n background: var(--st-cms-band-dark-bg);\n }\n\n .container {\n max-width: var(--st-cms-container-max);\n margin: 0 auto;\n }\n\n .header {\n ${sectionHeaderStyles}\n }\n\n .eyebrow {\n margin: 0 0 var(--st-space-12);\n font-size: var(--st-text-sm);\n font-weight: var(--st-font-weight-bold);\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--st-color-text-muted);\n }\n\n .title {\n ${sectionTitleStyles}\n color: inherit;\n letter-spacing: -0.02em;\n }\n\n .subtitle {\n ${sectionSubtitleStyles}\n max-width: 60ch;\n margin: 0 auto;\n }\n\n .grid {\n list-style: none;\n margin: 0;\n padding: 0;\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));\n gap: var(--st-space-24);\n }\n\n .card {\n display: flex;\n flex-direction: column;\n gap: var(--st-space-12);\n box-sizing: border-box;\n margin: 0;\n padding: var(--st-space-32);\n border: 1px solid var(--st-color-border);\n border-radius: var(--st-radius-lg);\n background: var(--st-color-surface);\n color: var(--st-color-text);\n }\n\n .number {\n font-size: var(--st-cms-text-3xl);\n font-weight: var(--st-font-weight-bold);\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--st-color-accent);\n font-variant-numeric: tabular-nums;\n }\n\n .card-title {\n margin: var(--st-space-8) 0 0;\n font-size: var(--st-text-xl);\n font-weight: var(--st-font-weight-semibold);\n line-height: 1.3;\n color: var(--st-color-text-strong);\n }\n\n .description {\n margin: 0;\n font-size: var(--st-text-md);\n line-height: 1.6;\n color: var(--st-color-text-muted);\n }\n\n /* inverted: a dark band with light cards; the number moves to the cyan highlight so it reads on navy. */\n :host([inverted]) .card {\n background: color-mix(in oklch, var(--st-cms-band-dark-color) 6%, transparent);\n border-color: color-mix(in oklch, var(--st-cms-band-dark-color) 16%, transparent);\n }\n\n :host([inverted]) .number {\n color: var(--st-cms-highlight);\n }\n\n :host([inverted]) .title,\n :host([inverted]) .card-title {\n color: var(--st-cms-band-dark-color);\n }\n\n :host([inverted]) .eyebrow,\n :host([inverted]) .subtitle,\n :host([inverted]) .description {\n color: color-mix(in oklch, var(--st-cms-band-dark-color) 75%, transparent);\n }\n\n /* highlightLast: the final card is the dark call-out. Declared after the inverted rules so it wins. */\n .card.highlight {\n background: var(--st-cms-navy);\n border-color: var(--st-cms-navy);\n color: var(--st-cms-on-dark);\n }\n\n .card.highlight .number {\n color: var(--st-cms-highlight);\n }\n\n .card.highlight .card-title {\n color: var(--st-cms-on-dark);\n }\n\n .card.highlight .description {\n color: color-mix(in oklch, var(--st-cms-on-dark) 80%, transparent);\n }\n\n @media (max-width: 600px) {\n :host {\n padding: var(--st-steps-padding, var(--st-space-40) var(--st-space-20)) !important;\n }\n .card {\n padding: var(--st-space-24);\n }\n }\n `;\n\n /** Small uppercase label above the title. */\n @property({ type: String })\n eyebrow = '';\n\n @property({ type: String })\n title = '';\n\n @property({ type: String })\n subtitle = '';\n\n /** The steps, in order. */\n @property({ attribute: false })\n items: StepItem[] = [];\n\n /** Renders the last step as a dark navy call-out card. */\n @property({ type: Boolean, reflect: true, attribute: 'highlight-last' })\n highlightLast = false;\n\n /** Puts the block on the dark band (`--st-cms-band-dark-bg`), which stays dark in the dark theme. */\n @property({ type: Boolean, reflect: true })\n inverted = false;\n\n render() {\n const heading = this.eyebrow || this.title || this.subtitle;\n const last = this.items.length - 1;\n return html`\n <div class=\"container\" part=\"container\">\n ${heading\n ? html`<div class=\"header\" part=\"header\">\n ${this.eyebrow ? html`<p class=\"eyebrow\" part=\"eyebrow\">${this.eyebrow}</p>` : nothing}\n ${this.title ? html`<h2 class=\"title\" part=\"title\">${this.title}</h2>` : nothing}\n ${this.subtitle ? html`<p class=\"subtitle\" part=\"subtitle\">${this.subtitle}</p>` : nothing}\n </div>`\n : nothing}\n <ol class=\"grid\" part=\"grid\" aria-label=${this.title || nothing}>\n ${this.items.map(\n (step, index) => html`\n <li class=\"card ${this.highlightLast && index === last ? 'highlight' : ''}\" part=\"card\">\n <span class=\"number\" part=\"number\" aria-hidden=\"true\">${String(index + 1).padStart(2, '0')}</span>\n <h3 class=\"card-title\" part=\"card-title\">${step.title}</h3>\n ${step.description ? html`<p class=\"description\" part=\"description\">${step.description}</p>` : nothing}\n </li>\n `\n )}\n </ol>\n </div>\n `;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,YAAY,KAAK,MAAM,eAAe;AAC/C,SAAS,eAAe,gBAAgB;AAqBjC,IAAM,QAAN,cAAoB,WAAW;AAAA,EAA/B;AAAA;AAkJL,mBAAU;AAGV,iBAAQ;AAGR,oBAAW;AAIX,iBAAoB,CAAC;AAIrB,yBAAgB;AAIhB,oBAAW;AAAA;AAAA,EAEX,SAAS;AACP,UAAM,UAAU,KAAK,WAAW,KAAK,SAAS,KAAK;AACnD,UAAM,OAAO,KAAK,MAAM,SAAS;AACjC,WAAO;AAAA;AAAA,UAED,UACE;AAAA,gBACI,KAAK,UAAU,yCAAyC,KAAK,OAAO,SAAS,OAAO;AAAA,gBACpF,KAAK,QAAQ,sCAAsC,KAAK,KAAK,UAAU,OAAO;AAAA,gBAC9E,KAAK,WAAW,2CAA2C,KAAK,QAAQ,SAAS,OAAO;AAAA,sBAE5F,OAAO;AAAA,kDAC+B,KAAK,SAAS,OAAO;AAAA,YAC3D,KAAK,MAAM;AAAA,MACX,CAAC,MAAM,UAAU;AAAA,gCACG,KAAK,iBAAiB,UAAU,OAAO,cAAc,EAAE;AAAA,wEACf,OAAO,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,2DAC/C,KAAK,KAAK;AAAA,kBACnD,KAAK,cAAc,iDAAiD,KAAK,WAAW,SAAS,OAAO;AAAA;AAAA;AAAA,IAG5G,CAAC;AAAA;AAAA;AAAA;AAAA,EAIT;AACF;AAhMa,MACJ,SAAS;AAAA,MACZ,iBAAiB;AAAA,MACjB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAqBd,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAanB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAMlB,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuG3B;AAAA,EADC,SAAS,EAAE,MAAM,OAAO,CAAC;AAAA,GAjJf,MAkJX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,OAAO,CAAC;AAAA,GApJf,MAqJX;AAGA;AAAA,EADC,SAAS,EAAE,MAAM,OAAO,CAAC;AAAA,GAvJf,MAwJX;AAIA;AAAA,EADC,SAAS,EAAE,WAAW,MAAM,CAAC;AAAA,GA3JnB,MA4JX;AAIA;AAAA,EADC,SAAS,EAAE,MAAM,SAAS,SAAS,MAAM,WAAW,iBAAiB,CAAC;AAAA,GA/J5D,MAgKX;AAIA;AAAA,EADC,SAAS,EAAE,MAAM,SAAS,SAAS,KAAK,CAAC;AAAA,GAnK/B,MAoKX;AApKW,QAAN;AAAA,EADN,cAAc,UAAU;AAAA,GACZ;","names":[]}
|
package/dist/cms-theme.css
CHANGED
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
stays dark there too. */
|
|
19
19
|
--st-cms-band-dark-bg: var(--st-color-text-strong);
|
|
20
20
|
--st-cms-band-dark-color: var(--st-color-on-solid);
|
|
21
|
+
|
|
22
|
+
/* Fixed brand colours for highlight surfaces (steps' last card, flow's accent node, bento "dark"
|
|
23
|
+
cards). They do not swap in the dark theme: navy and cyan read well on both page surfaces.
|
|
24
|
+
--st-cms-on-dark is the light text placed on them. */
|
|
25
|
+
--st-cms-navy: #011d41;
|
|
26
|
+
--st-cms-highlight: #49cce0;
|
|
27
|
+
--st-cms-on-dark: #ffffff;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
:root.dark {
|