@vamos-ui/react 0.3.0 → 0.4.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/dist/blocks/block-image.js +1 -1
- package/dist/blocks/block-link.js +1 -1
- package/dist/blocks/index.d.ts +2 -2
- package/dist/blocks/index.js +2 -2
- package/dist/blocks/site/_kit-1.js +1 -1
- package/dist/blocks/site/announcement-bar.js +1 -1
- package/dist/blocks/site/article-layout.d.ts +1 -0
- package/dist/blocks/site/article-layout.js +2 -1
- package/dist/blocks/site/blog-list.d.ts +1 -0
- package/dist/blocks/site/blog-list.js +3 -2
- package/dist/blocks/site/contact-dock.js +3 -1
- package/dist/blocks/site/contact-form.js +1 -1
- package/dist/blocks/site/cta.d.ts +2 -2
- package/dist/blocks/site/cta.js +6 -4
- package/dist/blocks/site/feature-grid.d.ts +1 -0
- package/dist/blocks/site/feature-grid.js +5 -4
- package/dist/blocks/site/footer.d.ts +1 -1
- package/dist/blocks/site/footer.js +8 -7
- package/dist/blocks/site/gallery-section.d.ts +6 -1
- package/dist/blocks/site/gallery-section.js +5 -4
- package/dist/blocks/site/hero.d.ts +3 -2
- package/dist/blocks/site/hero.js +7 -5
- package/dist/blocks/site/logo-strip.d.ts +1 -0
- package/dist/blocks/site/logo-strip.js +1 -1
- package/dist/blocks/site/map-hours.d.ts +2 -2
- package/dist/blocks/site/map-hours.js +4 -2
- package/dist/blocks/site/media-text.d.ts +4 -1
- package/dist/blocks/site/media-text.js +4 -3
- package/dist/blocks/site/menu-list.d.ts +7 -3
- package/dist/blocks/site/menu-list.js +34 -7
- package/dist/blocks/site/navbar.d.ts +1 -0
- package/dist/blocks/site/navbar.js +6 -5
- package/dist/blocks/site/not-found.js +2 -1
- package/dist/blocks/site/page-intro.d.ts +1 -0
- package/dist/blocks/site/page-intro.js +2 -1
- package/dist/blocks/site/process.d.ts +1 -0
- package/dist/blocks/site/process.js +2 -1
- package/dist/blocks/site/registry-1.d.ts +1 -1
- package/dist/blocks/site/registry-2.d.ts +5 -0
- package/dist/blocks/site/registry-2.js +2 -2
- package/dist/blocks/site/rich-text-section.js +2 -1
- package/dist/blocks/site/shared/heading-lines.d.ts +9 -0
- package/dist/blocks/site/shared/heading-lines.js +32 -0
- package/dist/blocks/site/shared/image-sizes.d.ts +25 -0
- package/dist/blocks/site/shared/image-sizes.js +37 -0
- package/dist/blocks/site/shared/island-keys.d.ts +1 -1
- package/dist/blocks/site/shared/island-keys.js +1 -1
- package/dist/blocks/site/shared/labels-2.d.ts +9 -1
- package/dist/blocks/site/shared/labels-2.js +24 -6
- package/dist/blocks/site/shared/open-now.d.ts +1 -1
- package/dist/blocks/site/shared/open-now.js +1 -1
- package/dist/blocks/site/shared/opening-hours.d.ts +18 -2
- package/dist/blocks/site/shared/opening-hours.js +36 -7
- package/dist/blocks/site/shared/tap.d.ts +9 -0
- package/dist/blocks/site/shared/tap.js +16 -0
- package/dist/blocks/site/team.d.ts +1 -1
- package/dist/blocks/site/team.js +4 -3
- package/dist/blocks/site/testimonials.d.ts +2 -2
- package/dist/blocks/site/testimonials.js +3 -3
- package/dist/charts/bar-chart.js +1 -1
- package/dist/charts/chart-shell.d.ts +9 -2
- package/dist/charts/chart-shell.js +10 -4
- package/dist/charts/chart.js +1 -1
- package/dist/charts/combo-chart.js +5 -7
- package/dist/charts/utils.d.ts +10 -0
- package/dist/charts/utils.js +46 -0
- package/dist/components/activity-timeline.js +5 -5
- package/dist/components/aspect-ratio.d.ts +26 -0
- package/dist/components/aspect-ratio.js +42 -0
- package/dist/components/carousel.d.ts +12 -3
- package/dist/components/carousel.js +8 -5
- package/dist/components/collapsible.d.ts +36 -0
- package/dist/components/collapsible.js +37 -0
- package/dist/components/image.d.ts +18 -2
- package/dist/components/image.js +12 -5
- package/dist/components/menubar.d.ts +96 -0
- package/dist/components/menubar.js +123 -0
- package/dist/components/placeholder-art.d.ts +51 -0
- package/dist/components/placeholder-art.js +82 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/lib/value-format.js +1 -1
- package/dist/locales/ar.d.ts +2087 -0
- package/dist/locales/ar.js +2088 -0
- package/dist/locales/de.d.ts +2087 -0
- package/dist/locales/de.js +2088 -0
- package/dist/locales/en.d.ts +1 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/es.d.ts +2087 -0
- package/dist/locales/es.js +2087 -0
- package/dist/locales/fr.d.ts +2087 -0
- package/dist/locales/fr.js +2088 -0
- package/dist/locales/tr.d.ts +1 -0
- package/dist/locales/tr.js +1 -0
- package/dist/media/media-library.js +1 -1
- package/dist/motion/text-reveal.d.ts +3 -1
- package/dist/motion/text-reveal.js +5 -3
- package/dist/render/block-renderer.js +1 -1
- package/dist/styles/blocks.css +18 -9
- package/dist/styles/editors.css +2 -1
- package/dist/styles/media.css +31 -5
- package/dist/styles.css +39 -9
- package/package.json +1 -1
|
@@ -41,7 +41,7 @@ export function BlockImage({ src, alt, width, height, decorative, aspect, fit =
|
|
|
41
41
|
const altText = decorative ? "" : (alt ?? "");
|
|
42
42
|
const Img = components?.Image;
|
|
43
43
|
const img = Img ? (_jsx(Img, { src: src, alt: altText, width: ratio ? undefined : width, height: ratio ? undefined : height, fill: ratio ? true : undefined, sizes: sizes, priority: priority, className: imgClassName })) : (_jsx("img", { src: src, alt: altText, width: width, height: height, sizes: sizes, loading: priority ? "eager" : "lazy", decoding: priority ? "sync" : "async", fetchPriority: priority ? "high" : undefined, className: imgClassName }));
|
|
44
|
-
const frame = (_jsx("span", { "data-ratio": ratio ? "" : undefined, className: cn("vb-image",
|
|
44
|
+
const frame = (_jsx("span", { "data-ratio": ratio ? "" : undefined, className: cn("vb-image", caption ? undefined : className), style: frameStyle, ...(caption ? null : fieldAttr), children: img }));
|
|
45
45
|
if (!caption)
|
|
46
46
|
return frame;
|
|
47
47
|
return (_jsxs("figure", { className: cn("vb-figure", className), ...fieldAttr, children: [frame, _jsx("figcaption", { className: "text-small text-fg-muted", children: caption })] }));
|
|
@@ -16,7 +16,7 @@ import { isExternalUrl, sanitizeHref } from "./schema/safety.js";
|
|
|
16
16
|
*/
|
|
17
17
|
export const BlockLink = React.forwardRef(function BlockLink({ href, newTab, appearance, size = "lg", components, labels, locale, field, editMode, className, children, rel, target, ...props }, ref) {
|
|
18
18
|
const safe = sanitizeHref(href);
|
|
19
|
-
const classes = cn(appearance === "link" ? "inline-flex items-center gap-1.5 font-medium text-fg underline decoration-line underline-offset-[0.3em] transition-colors hover:decoration-current focus-ring rounded-[2px]" : null, appearance && appearance !== "link" ? cn(buttonVariants({ variant: appearance, size }), appearance === "primary" && "vb-action-primary", appearance === "secondary" && "vb-action-secondary") : null, className);
|
|
19
|
+
const classes = cn(appearance === "link" ? "inline-flex min-h-6 pointer-coarse:min-h-11 items-center gap-1.5 font-medium text-fg underline decoration-line underline-offset-[0.3em] transition-colors hover:decoration-current focus-ring rounded-[2px]" : null, appearance && appearance !== "link" ? cn(buttonVariants({ variant: appearance, size }), appearance === "primary" && "vb-action-primary", appearance === "secondary" && "vb-action-secondary") : null, className);
|
|
20
20
|
const fieldAttr = editMode && field ? { "data-field": field } : undefined;
|
|
21
21
|
if (!safe) {
|
|
22
22
|
if (process.env.NODE_ENV !== "production")
|
package/dist/blocks/index.d.ts
CHANGED
|
@@ -35,14 +35,14 @@ export { BlogList, BlogListSkeleton, type BlogListProps, type BlogListVariant, t
|
|
|
35
35
|
export { ArticleLayout, shareUrl, type ArticleLayoutProps, type ArticleVariant, type ArticlePost, type ArticleAuthor, type ShareTarget } from "./site/article-layout.js";
|
|
36
36
|
export { ContactForm, type ContactFormProps, type ContactFormVariant, type ContactInfo } from "./site/contact-form.js";
|
|
37
37
|
export { MapHours, HoursTable, directionsUrl, mapEmbedUrl, localBusinessJsonLd, type MapHoursProps, type MapHoursVariant, type Branch, type DirectionsProvider, type BusinessType, type HoursDisplay } from "./site/map-hours.js";
|
|
38
|
-
export { MenuList, menuJsonLd, ALLERGENS, type MenuListProps, type MenuVariant, type MenuCategory, type MenuItem, type MenuPrice, type MenuTag, type Allergen } from "./site/menu-list.js";
|
|
38
|
+
export { MenuList, menuJsonLd, ALLERGENS, MENU_TAGS, type MenuListProps, type MenuVariant, type MenuCategory, type MenuItem, type MenuPrice, type MenuTag, type Allergen } from "./site/menu-list.js";
|
|
39
39
|
export { GallerySection, type GallerySectionProps, type GalleryVariant, type GalleryImage, type GallerySpan } from "./site/gallery-section.js";
|
|
40
40
|
export { ConsentBanner, ConsentDefaults, type ConsentBannerProps, type ConsentVariant } from "./site/consent-banner.js";
|
|
41
41
|
export { ContactDock, type ContactDockProps, type ContactDockVariant, type DockAction, type MessagingProvider } from "./site/contact-dock.js";
|
|
42
42
|
export { EmbedFacade, type EmbedFacadeProps } from "./site/embed-facade.js";
|
|
43
43
|
export { Footer, type FooterProps, type FooterVariant } from "./site/footer.js";
|
|
44
44
|
export { NotFound, type NotFoundProps, type NotFoundVariant, type NotFoundKind } from "./site/not-found.js";
|
|
45
|
-
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, type OpenStatus, type OpenState, type WeekRow, type DayHours, type ZonedMoment, type Weekday, type TimeRange } from "./site/shared/opening-hours.js";
|
|
45
|
+
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, timeVars, type OpenStatus, type OpenState, type WeekRow, type DayHours, type ZonedMoment, type Weekday, type TimeRange } from "./site/shared/opening-hours.js";
|
|
46
46
|
export { OpenNow, type OpenNowProps } from "./site/shared/open-now.js";
|
|
47
47
|
export { ConsentGate, type ConsentGateProps } from "./site/shared/consent-gate.js";
|
|
48
48
|
export { ConsentSettingsButton } from "./site/shared/consent-island.js";
|
package/dist/blocks/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { BlogList, BlogListSkeleton } from "./site/blog-list.js";
|
|
|
37
37
|
export { ArticleLayout, shareUrl } from "./site/article-layout.js";
|
|
38
38
|
export { ContactForm } from "./site/contact-form.js";
|
|
39
39
|
export { MapHours, HoursTable, directionsUrl, mapEmbedUrl, localBusinessJsonLd } from "./site/map-hours.js";
|
|
40
|
-
export { MenuList, menuJsonLd, ALLERGENS } from "./site/menu-list.js";
|
|
40
|
+
export { MenuList, menuJsonLd, ALLERGENS, MENU_TAGS } from "./site/menu-list.js";
|
|
41
41
|
export { GallerySection } from "./site/gallery-section.js";
|
|
42
42
|
export { ConsentBanner, ConsentDefaults } from "./site/consent-banner.js";
|
|
43
43
|
export { ContactDock } from "./site/contact-dock.js";
|
|
@@ -45,7 +45,7 @@ export { EmbedFacade } from "./site/embed-facade.js";
|
|
|
45
45
|
export { Footer } from "./site/footer.js";
|
|
46
46
|
export { NotFound } from "./site/not-found.js";
|
|
47
47
|
// Shared modules: opening hours, consent, form contract, brand marks, labels
|
|
48
|
-
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus } from "./site/shared/opening-hours.js";
|
|
48
|
+
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, timeVars } from "./site/shared/opening-hours.js";
|
|
49
49
|
export { OpenNow } from "./site/shared/open-now.js";
|
|
50
50
|
export { ConsentGate } from "./site/shared/consent-gate.js";
|
|
51
51
|
export { ConsentSettingsButton } from "./site/shared/consent-island.js";
|
|
@@ -126,7 +126,7 @@ export function CtaList({ actions, components, labels, locale, size = "lg", forc
|
|
|
126
126
|
style = "secondary";
|
|
127
127
|
primarySeen = true;
|
|
128
128
|
}
|
|
129
|
-
return (_jsxs(BlockLink, { href: a.link.href, newTab: a.link.newTab, appearance: style, size: size, components: components, labels: labels, locale: locale, editMode: editMode, field: `props.actions.${i}`, "data-track": a.link.kind, children: [a.link.label, style === "link" && arrow ? _jsx(ArrowEnd, {}) : null] }, `${a.link.href}-${i}`));
|
|
129
|
+
return (_jsxs(BlockLink, { href: a.link.href, newTab: a.link.newTab, appearance: style, size: size, components: components, labels: labels, locale: locale, editMode: editMode, field: `props.actions.${i}`, "data-track": a.link.kind, className: style === "link" ? "min-h-6 pointer-coarse:min-h-11" : undefined, children: [a.link.label, style === "link" && arrow ? _jsx(ArrowEnd, {}) : null] }, `${a.link.href}-${i}`));
|
|
130
130
|
}) }));
|
|
131
131
|
}
|
|
132
132
|
/** Logical "→" that mirrors in RTL. Decorative. */
|
|
@@ -33,6 +33,6 @@ export function AnnouncementBar({ variant = "inline", text, link, tone = "neutra
|
|
|
33
33
|
return null;
|
|
34
34
|
const cookie = storageKey ?? `vb_ann_${hash(textOf(text) + textOf(link?.label))}`;
|
|
35
35
|
const centered = variant === "centered";
|
|
36
|
-
const linkEl = link ? (_jsxs(BlockLink, { href: link.href, newTab: link.newTab, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.link", className: "group/ann inline-flex shrink-0 items-center gap-1.5 font-medium underline decoration-current/40 underline-offset-[0.28em] transition-[text-decoration-color] hover:decoration-current focus-ring rounded-[2px]", children: [link.label, _jsx(ArrowEnd, { className: "group-hover/ann:translate-x-0.5 rtl:group-hover/ann:-translate-x-0.5" })] })) : null;
|
|
36
|
+
const linkEl = link ? (_jsxs(BlockLink, { href: link.href, newTab: link.newTab, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.link", className: "group/ann inline-flex min-h-6 shrink-0 items-center gap-1.5 font-medium underline decoration-current/40 underline-offset-[0.28em] transition-[text-decoration-color] hover:decoration-current focus-ring rounded-[2px]", children: [link.label, _jsx(ArrowEnd, { className: "group-hover/ann:translate-x-0.5 rtl:group-hover/ann:-translate-x-0.5" })] })) : null;
|
|
37
37
|
return (_jsx("div", { role: "region", "aria-label": t("announcement"), id: anchor, "data-announcement": "", "data-variant": variant, "data-tone": tone, "data-block-id": editMode ? id : undefined, "data-hidden": editMode && outside ? "" : undefined, className: cn("vb-announcement relative text-small", toneClass[tone], className), children: _jsxs("div", { className: cn("vb-container flex min-h-9 items-center gap-3 py-1.5", centered && "justify-center text-center"), children: [_jsxs("p", { className: cn("flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-0.5", centered ? "justify-center" : "flex-1"), children: [centered ? (_jsx("span", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] after:ms-3 after:inline-block after:h-[0.7em] after:w-px after:bg-current after:align-[-0.05em] after:opacity-40 after:content-['']", ...field(editMode, "label"), children: label ?? t("notice") })) : null, _jsx("span", { className: "text-pretty", ...field(editMode, "text"), children: text }), linkEl] }), dismissible ? (_jsx(AnnouncementDismiss, { cookie: cookie, label: t("dismiss"), className: centered ? "absolute end-[max(var(--gutter),0.5rem)] top-1/2 -translate-y-1/2" : undefined })) : null] }) }));
|
|
38
38
|
}
|
|
@@ -18,6 +18,7 @@ export interface ArticlePost {
|
|
|
18
18
|
/** Standfirst under the title. */
|
|
19
19
|
dek?: React.ReactNode;
|
|
20
20
|
body: RichDoc;
|
|
21
|
+
/** Cover. `sizes`: wide `100vw`, contained `(min-width: 76rem) 76rem, 100vw`. Above the fold: `priority`. */
|
|
21
22
|
cover?: SiteImage2;
|
|
22
23
|
coverCaption?: React.ReactNode;
|
|
23
24
|
author?: ArticleAuthor;
|
|
@@ -14,6 +14,7 @@ import { BrandIcon } from "./shared/brand-icons.js";
|
|
|
14
14
|
import { CopyLinkButton } from "./shared/copy-link.js";
|
|
15
15
|
import { shareLabelKeys } from "./shared/island-keys.js";
|
|
16
16
|
import { BlogList } from "./blog-list.js";
|
|
17
|
+
import { headingLines } from "./shared/heading-lines.js";
|
|
17
18
|
/** Gives every h2/h3 a unique, locale-aware anchor and collects the h2s for the TOC. */
|
|
18
19
|
function withAnchors(doc, locale) {
|
|
19
20
|
const used = new Set();
|
|
@@ -50,7 +51,7 @@ export function ArticleLayout({ variant = "classic", post, toc: showToc = true,
|
|
|
50
51
|
const minutes = post.readingMinutes ?? readingMinutes(countWords(richToText(post.body)));
|
|
51
52
|
const ctx = { t, locale, components, labels };
|
|
52
53
|
const cover = variant !== "minimal" && post.cover?.src ? post.cover : undefined;
|
|
53
|
-
const header = (_jsxs("header", { className: cn("flex flex-col gap-6", variant === "wide-cover" && "pt-4"), children: [post.breadcrumb?.length ? _jsx(Breadcrumb, { trail: post.breadcrumb, current: post.title, ctx: ctx }) : null, variant === "minimal" ? (_jsx("p", { className: "font-mono text-eyebrow tabular uppercase tracking-[0.08em] text-fg-muted", children: _jsx("time", { dateTime: isoDay(post.publishedAt), children: formatDate2(isoDay(post.publishedAt), locale) }) })) : post.category ? (_jsx("p", { className: "font-mono text-eyebrow uppercase tracking-[0.08em]", children: _jsx(BlockLink, { href: post.category.href, components: components, labels: labels, locale: locale, className: "text-accent-ink hover:underline underline-offset-[0.25em] focus-ring rounded-[2px]", children: post.category.label }) })) : null, _jsx(BlockHeading, { id: headingId, level: headingLevel, size: variant === "minimal" ? "display-xl" : "display-l", text: post.title, editMode: editMode, field: "props.post.title", labels: labels, locale: locale, className: "max-w-[22ch]" }), post.dek ? _jsx("p", { className: "max-w-[58ch] text-lead text-fg-muted text-pretty", children: post.dek }) : null, _jsx(Byline, { post: post, minutes: minutes, ctx: ctx, showDate: variant !== "minimal" })] }));
|
|
54
|
+
const header = (_jsxs("header", { className: cn("flex flex-col gap-6", variant === "wide-cover" && "pt-4"), children: [post.breadcrumb?.length ? _jsx(Breadcrumb, { trail: post.breadcrumb, current: post.title, ctx: ctx }) : null, variant === "minimal" ? (_jsx("p", { className: "font-mono text-eyebrow tabular uppercase tracking-[0.08em] text-fg-muted", children: _jsx("time", { dateTime: isoDay(post.publishedAt), children: formatDate2(isoDay(post.publishedAt), locale) }) })) : post.category ? (_jsx("p", { className: "font-mono text-eyebrow uppercase tracking-[0.08em]", children: _jsx(BlockLink, { href: post.category.href, components: components, labels: labels, locale: locale, className: "text-accent-ink hover:underline underline-offset-[0.25em] focus-ring rounded-[2px]", children: post.category.label }) })) : null, _jsx(BlockHeading, { id: headingId, level: headingLevel, size: variant === "minimal" ? "display-xl" : "display-l", text: headingLines(post.title), editMode: editMode, field: "props.post.title", labels: labels, locale: locale, className: "max-w-[22ch]" }), post.dek ? _jsx("p", { className: "max-w-[58ch] text-lead text-fg-muted text-pretty", children: post.dek }) : null, _jsx(Byline, { post: post, minutes: minutes, ctx: ctx, showDate: variant !== "minimal" })] }));
|
|
54
55
|
const tocNav = tocVisible ? (_jsxs("nav", { "aria-label": t("onThisPage"), className: cn("vb-toc", variant === "wide-cover" ? "border-y border-line py-4" : "flex flex-col gap-3"), children: [_jsx("p", { className: cn("font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", variant === "wide-cover" && "sr-only"), children: t("onThisPage") }), _jsx("ol", { className: cn("text-small", variant === "wide-cover" ? "flex flex-wrap gap-x-6 gap-y-2" : "flex flex-col gap-2 border-s border-line"), children: toc.map((e) => (_jsx("li", { children: _jsx("a", { href: `#${e.id}`, className: cn("vb-toc-link block text-fg-muted transition-colors hover:text-fg focus-ring rounded-[2px]", variant !== "wide-cover" && "-ms-px border-s border-transparent ps-4 hover:border-fg"), children: e.text }) }, e.id))) })] })) : null;
|
|
55
56
|
const body = _jsx(RichText, { doc: doc, size: "lg", components: components, labels: labels, locale: locale, className: "vb-article-body", editMode: editMode, field: "props.post.body" });
|
|
56
57
|
return (_jsxs(_Fragment, { children: [draft ? (_jsxs("p", { role: "note", className: "vb-draft-strip sticky top-0 z-20 px-4 py-2 text-center text-small text-fg", children: [_jsx("span", { className: "me-2 font-mono text-eyebrow uppercase tracking-[0.08em]", children: t("preview") }), t("previewNotice")] })) : null, _jsxs(Section, { as: "article", theme: theme, anchor: anchor, headingId: headingId, blockId: id, blockType: "article", editMode: editMode, className: cn("vb-article", className), "data-variant": variant, children: [variant === "wide-cover" && cover ? (_jsx("div", { className: "vb-article-cover -mt-[calc(var(--section-y)*var(--vb-rhythm))] mb-12 @4xl:mb-16", children: _jsx(BlockImage, { ...cover, alt: cover.alt ?? "", decorative: !cover.alt, aspect: "21:9", priority: true, sizes: "100vw", components: components, caption: post.coverCaption ? _jsx("span", { className: "vb-container block", children: post.coverCaption }) : undefined }) })) : null, _jsxs(Container, { className: "flex flex-col gap-12 @4xl:gap-16", children: [_jsx("div", { className: cn(variant === "classic" ? "@4xl:grid @4xl:grid-cols-12 @4xl:gap-x-(--gutter)" : "mx-auto w-full max-w-(--prose)", variant === "wide-cover" && "max-w-[58rem]"), children: _jsx("div", { className: cn(variant === "classic" && "@4xl:col-span-9"), children: header }) }), variant === "classic" && cover ? (_jsx(BlockImage, { ...cover, alt: cover.alt ?? "", decorative: !cover.alt, aspect: "3:2", priority: true, sizes: "(min-width: 76rem) 76rem, 100vw", className: "vb-article-cover rounded-ui", components: components, caption: post.coverCaption })) : null, variant === "wide-cover" && tocNav ? _jsx("div", { className: "mx-auto w-full max-w-[58rem]", children: tocNav }) : null, _jsxs("div", { className: cn(variant === "classic" ? "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12" : "mx-auto w-full max-w-(--prose)"), children: [variant === "classic" && tocNav ? (_jsx("aside", { className: "@4xl:col-span-3 @4xl:col-start-10 @4xl:row-start-1", children: _jsx("div", { className: "@4xl:sticky @4xl:top-[calc(var(--topbar-h,0px)+2rem)]", children: tocNav }) })) : null, _jsxs("div", { className: cn("flex min-w-0 flex-col gap-12", variant === "classic" && "@4xl:col-span-8 @4xl:row-start-1 @6xl:col-span-7"), children: [body, _jsx(ArticleFooter, { post: post, share: share, url: url, ctx: ctx })] })] })] })] }), related?.length ? _jsx(BlogList, { variant: "compact", title: t("relatedPosts"), posts: related, postHeadingLevel: 3, theme: { rhythm: "tight" }, components: components, labels: labels, locale: locale }) : null, cta] }));
|
|
@@ -8,6 +8,7 @@ export interface PostSummary {
|
|
|
8
8
|
href: string;
|
|
9
9
|
title: string;
|
|
10
10
|
excerpt?: string;
|
|
11
|
+
/** 3:2 cover. `sizes`: featured `(min-width: 56rem) min(59vw, 45rem), 100vw`; grid `min(34vw, 26rem)` / `min(50vw, 38rem)` / 100vw. */
|
|
11
12
|
cover?: SiteImage2;
|
|
12
13
|
category?: {
|
|
13
14
|
label: string;
|
|
@@ -7,6 +7,7 @@ import { BlockImage } from "../block-image.js";
|
|
|
7
7
|
import { BlockLink } from "../block-link.js";
|
|
8
8
|
import { site2Text } from "./shared/labels-2.js";
|
|
9
9
|
import { fieldAttr, formatDate2, isoDay, useBlockIds2 } from "./shared/kit-2.js";
|
|
10
|
+
import { columnSizes } from "./shared/image-sizes.js";
|
|
10
11
|
/**
|
|
11
12
|
* Latest posts and category archives. Receives posts as data (`PostSummary`); fetching is the
|
|
12
13
|
* app's job (or the renderer's `resolvers.posts`). Each post is an `article` with one link on the
|
|
@@ -48,13 +49,13 @@ function Cover({ post, ctx, sizes, priority, className }) {
|
|
|
48
49
|
}
|
|
49
50
|
function Editorial({ posts, ctx }) {
|
|
50
51
|
const [first, ...rest] = posts;
|
|
51
|
-
return (_jsxs("div", { className: "flex flex-col gap-12", children: [_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-6 @4xl:grid-cols-12 @4xl:items-end", children: [ctx.covers && first.cover ? (_jsx("div", { className: "@4xl:col-span-7", children: _jsx(Cover, { post: first, ctx: ctx, sizes:
|
|
52
|
+
return (_jsxs("div", { className: "flex flex-col gap-12", children: [_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-6 @4xl:grid-cols-12 @4xl:items-end", children: [ctx.covers && first.cover ? (_jsx("div", { className: "@4xl:col-span-7", children: _jsx(Cover, { post: first, ctx: ctx, sizes: columnSizes(7 / 12) }) })) : null, _jsxs("div", { className: cn("flex flex-col gap-4", ctx.covers && first.cover ? "@4xl:col-span-5" : "@4xl:col-span-9"), children: [_jsx(Category, { post: first }), _jsx(PostTitle, { post: first, ctx: ctx, className: "font-display text-display-m" }), first.excerpt ? _jsx("p", { className: "max-w-[52ch] text-fg-muted text-pretty", children: first.excerpt }) : null, _jsx(Meta, { post: first, ctx: ctx })] })] }), rest.length ? (_jsx("ul", { className: "grid gap-x-(--gutter) @2xl:grid-cols-2", children: rest.map((p, i) => (_jsx("li", { className: "border-t border-line", children: _jsxs("article", { className: "vb-post group/post relative flex flex-col gap-2 py-6", children: [_jsx(PostDate, { post: p, ctx: ctx }), _jsx(PostTitle, { post: p, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), p.category ? _jsx("p", { className: "text-small text-fg-muted", children: p.category.label }) : null] }) }, p.href + i))) })) : null] }));
|
|
52
53
|
}
|
|
53
54
|
function ListPost({ post, ctx }) {
|
|
54
55
|
return (_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-2 py-6 @2xl:grid-cols-12 @2xl:items-baseline", children: [_jsx(PostDate, { post: post, ctx: ctx, className: "@2xl:col-span-2" }), _jsxs("div", { className: "flex flex-col gap-1.5 @2xl:col-span-8", children: [_jsx(PostTitle, { post: post, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), post.excerpt ? _jsx("p", { className: "line-clamp-1 text-small text-fg-muted", children: post.excerpt }) : null] }), _jsx("p", { className: "text-small text-fg-muted @2xl:col-span-2 @2xl:text-end", children: post.readingMinutes ? ctx.t("minRead", { count: post.readingMinutes }) : null })] }));
|
|
55
56
|
}
|
|
56
57
|
function GridPost({ post, ctx }) {
|
|
57
|
-
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-4", children: [ctx.covers ? post.cover ? _jsx(Cover, { post: post, ctx: ctx, sizes:
|
|
58
|
+
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-4", children: [ctx.covers ? post.cover ? _jsx(Cover, { post: post, ctx: ctx, sizes: `(min-width: 56rem) min(34vw, 26rem), ${columnSizes(1 / 2, { from: "42rem" })}` }) : _jsx("span", { "aria-hidden": true, className: "block aspect-[3/2] rounded-ui bg-surface-2" }) : null, _jsxs("div", { className: "flex flex-col gap-2.5", children: [_jsx(Category, { post: post }), _jsx(PostTitle, { post: post, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), _jsx(Meta, { post: post, ctx: ctx })] })] }));
|
|
58
59
|
}
|
|
59
60
|
function CompactPost({ post, ctx }) {
|
|
60
61
|
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-1 py-4", children: [_jsx(PostTitle, { post: post, ctx: ctx, className: "text-body font-medium" }), _jsx(PostDate, { post: post, ctx: ctx })] }));
|
|
@@ -6,6 +6,7 @@ import { telHref } from "./shared/kit-2.js";
|
|
|
6
6
|
import { BrandIcon, BRAND_NAMES, waLink } from "./shared/brand-icons.js";
|
|
7
7
|
import { DockHours } from "./shared/dock-hours.js";
|
|
8
8
|
import { DockAvoid } from "./shared/dock-avoid.js";
|
|
9
|
+
import { nameWithLabel } from "./shared/tap.js";
|
|
9
10
|
function actionSpec(a, p, t) {
|
|
10
11
|
const iconClass = "size-5";
|
|
11
12
|
switch (a) {
|
|
@@ -49,7 +50,8 @@ export function ContactDock(props) {
|
|
|
49
50
|
const defaultActions = ["call", "whatsapp", "directions", "booking", "email"];
|
|
50
51
|
const actions = (props.actions ?? defaultActions).map((a) => [a, actionSpec(a, props, t)]).filter((x) => !!x[1]).slice(0, 4);
|
|
51
52
|
const showFab = variant !== "bar" && href;
|
|
53
|
+
const fabLabel = label ?? t("messageUs");
|
|
52
54
|
const showBar = variant !== "fab" && actions.length >= 2;
|
|
53
55
|
const hoursIsland = hours && offHours !== "show" ? _jsx(DockHours, { hours: hours, mode: offHours, text: t("offHours"), now: now, className: "vb-dock-offhours" }) : null;
|
|
54
|
-
return (_jsxs("div", { "data-contact-dock": "", "data-variant": variant, "data-position": position, "data-contained": contained ? "" : undefined, "data-block-id": editMode ? id : undefined, className: cn("vb-dock", className), children: [showFab ? (_jsxs("div", { className: cn("vb-dock-fab", variant === "both" && "vb-dock-desktop", position === "start" && "flex-row-reverse"), children: [hoursIsland ? _jsx("span", { className: "vb-dock-note surface-popover max-w-[16rem] rounded-ui px-3 py-2 text-xs text-fg empty:hidden [&:has(>span:empty)]:hidden", children: hoursIsland }) : null, _jsx("span", { "aria-hidden": true, className: "vb-dock-label surface-popover rounded-full px-3.5 py-2 text-sm font-medium text-fg", children:
|
|
56
|
+
return (_jsxs("div", { "data-contact-dock": "", "data-variant": variant, "data-position": position, "data-contained": contained ? "" : undefined, "data-block-id": editMode ? id : undefined, className: cn("vb-dock", className), children: [showFab ? (_jsxs("div", { className: cn("vb-dock-fab", variant === "both" && "vb-dock-desktop", position === "start" && "flex-row-reverse"), children: [hoursIsland ? _jsx("span", { className: "vb-dock-note surface-popover max-w-[16rem] rounded-ui px-3 py-2 text-xs text-fg empty:hidden [&:has(>span:empty)]:hidden", children: hoursIsland }) : null, _jsx("span", { "aria-hidden": true, className: "vb-dock-label surface-popover rounded-full px-3.5 py-2 text-sm font-medium text-fg", children: fabLabel }), _jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", "data-track": provider, "aria-label": nameWithLabel(fabLabel, `${t("messageUsOn", { network })}, ${t("opensInNewTab").replace(/[()]/g, "")}`), className: "vb-dock-button inline-flex size-[52px] shrink-0 items-center justify-center rounded-full bg-fg text-scene transition-opacity hover:opacity-90 focus-ring", children: _jsx(BrandIcon, { name: provider, className: "size-6" }) })] })) : null, showBar ? (_jsxs("nav", { "aria-label": t("contactActions"), className: cn("vb-dock-bar surface-bar border-t border-line", variant === "both" && "vb-dock-mobile"), children: [hoursIsland && !showFab ? _jsx("p", { className: "px-4 pt-2 text-center text-xs text-fg-muted empty:hidden", children: hoursIsland }) : null, _jsx("ul", { className: "grid", style: { gridTemplateColumns: `repeat(${actions.length}, minmax(0, 1fr))` }, children: actions.map(([a, s]) => (_jsx("li", { children: _jsxs("a", { href: s.href, target: s.external ? "_blank" : undefined, rel: s.external ? "noopener noreferrer" : undefined, "data-track": a, "aria-label": nameWithLabel(s.label, s.aria), className: "flex h-14 flex-col items-center justify-center gap-1 text-[11px] font-medium text-fg transition-colors hover:bg-hover focus-ring", children: [s.icon, _jsx("span", { "aria-hidden": true, children: s.label })] }) }, a))) })] })) : null, !contained && (showFab || showBar) ? _jsx(DockAvoid, {}) : null] }));
|
|
55
57
|
}
|
|
@@ -40,6 +40,6 @@ export function ContactForm({ variant = "split", eyebrow, title, lead, fields =
|
|
|
40
40
|
}
|
|
41
41
|
function InfoList({ info, t, locale, editMode }) {
|
|
42
42
|
const hours = typeof info.hours === "object" ? summarizeHours(info.hours, locale) : undefined;
|
|
43
|
-
const linkClass = "text-fg underline decoration-line underline-offset-[0.25em] transition-colors hover:decoration-current focus-ring rounded-[2px]";
|
|
43
|
+
const linkClass = "inline-flex min-h-6 items-center text-fg underline decoration-line underline-offset-[0.25em] transition-colors hover:decoration-current focus-ring rounded-[2px]";
|
|
44
44
|
return (_jsxs("dl", { className: "flex flex-col border-t border-line", children: [info.phone ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("phone") }), _jsx("dd", { ...fieldAttr(editMode, "info.phone"), children: _jsx("a", { href: telHref(info.phone), className: "font-display text-display-m tabular text-fg transition-colors hover:text-accent-ink focus-ring rounded-[2px]", "data-track": "call", children: prettyPhone(info.phone) }) })] })) : null, info.email ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("email") }), _jsx("dd", { children: _jsx("a", { href: `mailto:${info.email}`, className: cn(linkClass, "text-lead"), "data-track": "email", children: info.email }) })] })) : null, info.address ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("address") }), _jsx("dd", { className: "not-italic text-fg", children: _jsx("address", { className: "not-italic", children: (typeof info.address === "string" ? info.address.split("\n") : addressLines(info.address, { locale })).map((l, i) => (_jsx("span", { className: "block", children: l }, i))) }) })] })) : null, info.hours ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("hours") }), _jsx("dd", { className: "text-fg", children: hours ? (_jsx("ul", { className: "flex flex-col gap-1", children: hours.map((h, i) => (_jsxs("li", { className: "flex justify-between gap-6 tabular", children: [_jsx("span", { children: h.days }), _jsx("span", { className: h.closed ? "text-fg-subtle" : undefined, children: h.closed ? t("closedDay") : h.text })] }, i))) })) : info.hours })] })) : null] }));
|
|
45
45
|
}
|
|
@@ -22,7 +22,7 @@ export interface CtaBookingConfig {
|
|
|
22
22
|
timeZone?: string;
|
|
23
23
|
/** "Hi, I'd like a table for {guests} on {date} at {time}." */
|
|
24
24
|
messageTemplate?: string;
|
|
25
|
-
/** embed: preview image shown before the calendar loads. */
|
|
25
|
+
/** embed: preview image shown before the calendar loads. `sizes`: `min(100vw, 76rem)`. */
|
|
26
26
|
preview?: SiteImage;
|
|
27
27
|
}
|
|
28
28
|
export interface CtaProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
@@ -39,7 +39,7 @@ export interface CtaProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
|
39
39
|
lead?: React.ReactNode;
|
|
40
40
|
/** ≤ 2; one primary. In `minimal` only the first is used, as a large text link. */
|
|
41
41
|
actions?: SiteCta[];
|
|
42
|
-
/** split: the photo. */
|
|
42
|
+
/** split: the photo. `sizes`: `(min-width: 56rem) min(42vw, 32rem), 100vw`. */
|
|
43
43
|
media?: SiteImage;
|
|
44
44
|
/** band/split: mono contact line ("0216 000 00 00 · WhatsApp"). */
|
|
45
45
|
contact?: {
|
package/dist/blocks/site/cta.js
CHANGED
|
@@ -11,6 +11,8 @@ import { EmbedFacade } from "./embed-facade.js";
|
|
|
11
11
|
import { formatSitePhone } from "./navbar.js";
|
|
12
12
|
import { ArrowEnd, CtaList, field, siteTemplate, siteText, useBlockIds } from "./_kit-1.js";
|
|
13
13
|
import { Reveal } from "../../motion/reveal.js";
|
|
14
|
+
import { headingLines } from "./shared/heading-lines.js";
|
|
15
|
+
import { columnSizes, SITE_SIZES } from "./shared/image-sizes.js";
|
|
14
16
|
/** `https://wa.me/<digits>?text=<encoded>` from an E.164 number. */
|
|
15
17
|
export function waLink(e164, text) {
|
|
16
18
|
const base = `https://wa.me/${e164.replace(/\D/g, "")}`;
|
|
@@ -32,7 +34,7 @@ export function Cta(props) {
|
|
|
32
34
|
return (_jsx(Section, { theme: sectionTheme, anchor: anchor, headingId: headingId, blockId: base, blockType: "cta", editMode: editMode, components: components, labels: labels, "data-variant": variant, className: cn("vb-cta", className), children: _jsx(Reveal, { disabled: editMode, children: variant === "split" ? _jsx(Split, { ...inner }) : variant === "minimal" ? _jsx(Minimal, { ...inner }) : variant === "newsletter" ? _jsx(Newsletter, { ...inner }) : variant === "booking" ? _jsx(Booking, { ...inner }) : _jsx(Band, { ...inner }) }) }));
|
|
33
35
|
}
|
|
34
36
|
function Heading({ p, size = "display-l", className }) {
|
|
35
|
-
return _jsx(BlockHeading, { id: p.headingId, level: p.headingLevel ?? 2, size: size, text: p.title, editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: className });
|
|
37
|
+
return _jsx(BlockHeading, { id: p.headingId, level: p.headingLevel ?? 2, size: size, text: headingLines(p.title), editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: className });
|
|
36
38
|
}
|
|
37
39
|
function Eyebrow({ p, className }) {
|
|
38
40
|
if (!p.eyebrow)
|
|
@@ -54,7 +56,7 @@ function ContactLine({ p, className }) {
|
|
|
54
56
|
if (!c?.phone && !c?.whatsapp)
|
|
55
57
|
return null;
|
|
56
58
|
const t = siteText(p.labels, p.locale);
|
|
57
|
-
const linkCls = "rounded-[2px] underline decoration-current/30 underline-offset-4 transition-[text-decoration-color] hover:decoration-current focus-ring";
|
|
59
|
+
const linkCls = "inline-flex min-h-6 items-center pointer-coarse:min-h-11 rounded-[2px] underline decoration-current/30 underline-offset-4 transition-[text-decoration-color] hover:decoration-current focus-ring";
|
|
58
60
|
return (_jsxs("p", { className: cn("flex flex-wrap items-center gap-x-3 gap-y-1 font-mono text-small tabular text-fg-muted", className), ...field(p.editMode, "contact"), children: [c.phone ? (_jsx("a", { href: `tel:${c.phone}`, "data-track": "call", className: linkCls, children: formatSitePhone(c.phone, p.locale) })) : null, c.phone && c.whatsapp ? _jsx("span", { "aria-hidden": true, children: "\u00B7" }) : null, c.whatsapp ? (_jsxs("a", { href: waLink(c.whatsapp, c.whatsappText), target: "_blank", rel: "noopener noreferrer", "data-track": "whatsapp", className: linkCls, children: ["WhatsApp", _jsxs("span", { className: "sr-only", children: [" ", t("opensWhatsApp")] })] })) : null] }));
|
|
59
61
|
}
|
|
60
62
|
const actionsOf = (p, extra) => _jsx(CtaList, { actions: p.actions, components: p.components, labels: p.labels, locale: p.locale, editMode: p.editMode, ...extra });
|
|
@@ -64,7 +66,7 @@ function Band(p) {
|
|
|
64
66
|
}
|
|
65
67
|
/* split ----------------------------------------------------------------- */
|
|
66
68
|
function Split(p) {
|
|
67
|
-
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-stretch", children: [_jsxs("div", { className: "flex flex-col justify-between gap-10 @4xl:col-span-6 @4xl:py-4", children: [_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx(Eyebrow, { p: p }), _jsx(Heading, { p: p }), _jsx(Lead, { p: p })] }), _jsxs("div", { className: "flex flex-col gap-5", children: [actionsOf(p), _jsx(ContactLine, { p: p }), _jsx(Fineprint, { p: p })] })] }), p.media ? (_jsx("div", { className: "@4xl:col-span-5 @4xl:col-start-8", children: _jsx(BlockImage, { ...p.media, aspect: "4:5", sizes:
|
|
69
|
+
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-stretch", children: [_jsxs("div", { className: "flex flex-col justify-between gap-10 @4xl:col-span-6 @4xl:py-4", children: [_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx(Eyebrow, { p: p }), _jsx(Heading, { p: p }), _jsx(Lead, { p: p })] }), _jsxs("div", { className: "flex flex-col gap-5", children: [actionsOf(p), _jsx(ContactLine, { p: p }), _jsx(Fineprint, { p: p })] })] }), p.media ? (_jsx("div", { className: "@4xl:col-span-5 @4xl:col-start-8", children: _jsx(BlockImage, { ...p.media, aspect: "4:5", sizes: columnSizes(5 / 12), className: "h-full rounded-ui", components: p.components, labels: p.labels, editMode: p.editMode, field: "props.media" }) })) : null] }));
|
|
68
70
|
}
|
|
69
71
|
/* minimal --------------------------------------------------------------- */
|
|
70
72
|
function Minimal(p) {
|
|
@@ -88,7 +90,7 @@ function Booking(p) {
|
|
|
88
90
|
const locale = p.locale ?? "en-US";
|
|
89
91
|
let control;
|
|
90
92
|
if (b.provider === "embed" && b.url) {
|
|
91
|
-
control = (_jsx("div", { className: "relative aspect-[16/10] overflow-hidden rounded-ui border border-line @4xl:aspect-[21/9]", children: _jsxs(EmbedFacade, { kind: "iframe", src: b.url, title: typeof p.title === "string" ? p.title.replace(/\*/g, "") : t("openCalendar"), buttonLabel: t("openCalendar"), children: [b.preview ? _jsx(BlockImage, { ...b.preview, decorative: true, aspect: "21:9", className: "absolute inset-0 h-full opacity-60", components: p.components }) : null, _jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[color-mix(in_oklch,var(--scene)_55%,transparent)] text-center", children: [_jsxs("span", { className: "inline-flex h-12 items-center gap-2.5 rounded-ui bg-fg px-6 text-[15px] font-semibold text-scene", children: [_jsx(CalendarDays, { "aria-hidden": true, strokeWidth: 1.5, className: "size-[18px]" }), t("openCalendar")] }), _jsx("span", { className: "text-small text-fg-muted", children: t("loadsCalendar", { provider: new URL(b.url).hostname.replace(/^www\./, "") }) })] })] }) }));
|
|
93
|
+
control = (_jsx("div", { className: "relative aspect-[16/10] overflow-hidden rounded-ui border border-line @4xl:aspect-[21/9]", children: _jsxs(EmbedFacade, { kind: "iframe", src: b.url, title: typeof p.title === "string" ? p.title.replace(/\*/g, "") : t("openCalendar"), buttonLabel: t("openCalendar"), children: [b.preview ? _jsx(BlockImage, { ...b.preview, decorative: true, aspect: "21:9", sizes: SITE_SIZES.container, className: "absolute inset-0 h-full opacity-60", components: p.components }) : null, _jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[color-mix(in_oklch,var(--scene)_55%,transparent)] text-center", children: [_jsxs("span", { className: "inline-flex h-12 items-center gap-2.5 rounded-ui bg-fg px-6 text-[15px] font-semibold text-scene", children: [_jsx(CalendarDays, { "aria-hidden": true, strokeWidth: 1.5, className: "size-[18px]" }), t("openCalendar")] }), _jsx("span", { className: "text-small text-fg-muted", children: t("loadsCalendar", { provider: new URL(b.url).hostname.replace(/^www\./, "") }) })] })] }) }));
|
|
92
94
|
}
|
|
93
95
|
else if (b.provider === "phone") {
|
|
94
96
|
control = (_jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-3", children: [b.phone ? (_jsx(BlockLink, { href: `tel:${b.phone}`, appearance: "primary", components: p.components, labels: p.labels, "data-track": "call", children: formatSitePhone(b.phone, locale) })) : null, actionsOf(p, { forceStyle: "link" })] }));
|
|
@@ -7,6 +7,7 @@ export interface FeatureItem {
|
|
|
7
7
|
text?: React.ReactNode;
|
|
8
8
|
/** Lucide name ("calendar-check") or a node. Small, before the title, never boxed. */
|
|
9
9
|
icon?: React.ReactNode;
|
|
10
|
+
/** `sizes`: tabs/sticky panel `min(59vw, 45rem)` or `min(50vw, 38rem)`, grid `min(34vw, 26rem)`, stacked (narrow) `min(100vw, 56rem)`. */
|
|
10
11
|
media?: SiteImage;
|
|
11
12
|
/** Makes the whole row clickable (one link, stretched with `::after`). */
|
|
12
13
|
link?: {
|
|
@@ -8,6 +8,7 @@ import { FeatureTabs } from "./feature-tabs.js";
|
|
|
8
8
|
import { SiteIcon } from "./_site-icon-1.js";
|
|
9
9
|
import { ArrowEnd, indexLabel, useBlockIds } from "./_kit-1.js";
|
|
10
10
|
import { Stagger } from "../../motion/reveal.js";
|
|
11
|
+
import { columnSizes, SITE_SIZES } from "./shared/image-sizes.js";
|
|
11
12
|
const plain = (n) => (typeof n === "string" ? n : undefined);
|
|
12
13
|
/**
|
|
13
14
|
* Services, treatments or product modules — without the card wall. Server component; the `tabs`
|
|
@@ -33,15 +34,15 @@ export function FeatureGrid({ variant = "list", eyebrow, title, lead, actions, i
|
|
|
33
34
|
value: `f${i}`,
|
|
34
35
|
title: it.title,
|
|
35
36
|
text: it.text,
|
|
36
|
-
panel: (_jsxs("div", { className: "flex flex-col gap-4", children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
37
|
-
})) }), _jsx("ol", { className: "flex flex-col @4xl:hidden", children: items.map((it, i) => (_jsxs("li", { className: "flex flex-col gap-3 border-t border-line py-6", children: [itemTitle(it, i), itemText(it, i), it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
37
|
+
panel: (_jsxs("div", { className: "flex flex-col gap-4", children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: columnSizes(7 / 12), className: "rounded-ui border border-line", components: components, labels: labels }) : null, it.link ? (_jsxs(BlockLink, { href: it.link.href, newTab: it.link.newTab, appearance: "link", ...linkProps, children: [it.link.label ?? it.title, _jsx(ArrowEnd, {})] })) : null] })),
|
|
38
|
+
})) }), _jsx("ol", { className: "flex flex-col @4xl:hidden", children: items.map((it, i) => (_jsxs("li", { className: "flex flex-col gap-3 border-t border-line py-6", children: [itemTitle(it, i), itemText(it, i), it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: SITE_SIZES.narrowOnly, className: "mt-2 rounded-ui border border-line", components: components, labels: labels }) : null] }, i))) })] }));
|
|
38
39
|
}
|
|
39
40
|
else if (v === "sticky-media") {
|
|
40
41
|
const names = items.map((_, i) => `--vbf-${base.replace(/[^a-zA-Z0-9_-]/g, "")}-${i}`);
|
|
41
|
-
body = (_jsxs("div", { className: "vb-sticky-features grid gap-x-(--gutter) @4xl:grid-cols-12", style: { timelineScope: names.join(", ") }, children: [_jsx("div", { className: "vb-sticky-media", "aria-hidden": true, children: _jsx("div", { className: "sticky top-[calc(var(--vb-nav-h,4.5rem)+2rem)]", children: _jsx("div", { className: "relative overflow-hidden rounded-ui", children: items.map((it, i) => it.media ? (_jsx(BlockImage, { ...it.media, decorative: true, aspect: "4:5", sizes:
|
|
42
|
+
body = (_jsxs("div", { className: "vb-sticky-features grid gap-x-(--gutter) @4xl:grid-cols-12", style: { timelineScope: names.join(", ") }, children: [_jsx("div", { className: "vb-sticky-media", "aria-hidden": true, children: _jsx("div", { className: "sticky top-[calc(var(--vb-nav-h,4.5rem)+2rem)]", children: _jsx("div", { className: "relative overflow-hidden rounded-ui", children: items.map((it, i) => it.media ? (_jsx(BlockImage, { ...it.media, decorative: true, aspect: "4:5", sizes: columnSizes(6 / 12), className: cn("vb-sticky-frame", i > 0 && "absolute! inset-0"), "data-pos": i === 0 ? "first" : i === items.length - 1 ? "last" : undefined, style: { "--vbf-timeline": names[i], zIndex: i }, components: components }, i)) : null) }) }) }), _jsx("ol", { className: "vb-sticky-list flex flex-col", children: items.map((it, i) => (_jsxs("li", { className: "vb-sticky-item flex min-h-[min(70svh,34rem)] flex-col justify-center gap-4 border-t border-line py-10", style: { viewTimelineName: names[i] }, children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: SITE_SIZES.narrowOnly, className: "vb-sticky-inline mb-2 rounded-ui", components: components, labels: labels }) : null, _jsx("span", { className: "font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }), itemTitle(it, i, "text-display-m font-display font-normal"), itemText(it, i, "max-w-[46ch]"), it.link ? (_jsxs(BlockLink, { href: it.link.href, newTab: it.link.newTab, appearance: "link", ...linkProps, className: "mt-2", children: [it.link.label ?? it.title, _jsx(ArrowEnd, {})] })) : null] }, i))) })] }));
|
|
42
43
|
}
|
|
43
44
|
else if (v === "grid") {
|
|
44
|
-
body = (_jsx("ul", { className: cn("grid gap-x-(--gutter) gap-y-12 @2xl:grid-cols-2", columns >= 3 && "@4xl:grid-cols-3", columns === 4 && "@6xl:grid-cols-4", columns === 2 && "@4xl:grid-cols-2"), children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative flex flex-col gap-3 border-t border-line pt-4", it.link && "vb-linked"), children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
45
|
+
body = (_jsx("ul", { className: cn("grid gap-x-(--gutter) gap-y-12 @2xl:grid-cols-2", columns >= 3 && "@4xl:grid-cols-3", columns === 4 && "@6xl:grid-cols-4", columns === 2 && "@4xl:grid-cols-2"), children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative flex flex-col gap-3 border-t border-line pt-4", it.link && "vb-linked"), children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: columnSizes(4 / 12), className: "mb-2 rounded-ui", components: components, labels: labels }) : null, _jsxs("div", { className: "flex items-start gap-3 pt-2", children: [showIndex ? _jsx("span", { className: "pt-1.5 font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }) : null, it.icon ? _jsx(SiteIcon, { icon: it.icon, className: "mt-1" }) : null, itemTitle(it, i)] }), itemText(it, i), more(it)] }, i))) }));
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
47
48
|
body = (_jsx("ol", { className: "grid gap-x-(--gutter) @4xl:grid-cols-2", children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative grid grid-cols-[3rem_1fr] gap-x-4 gap-y-3 border-t border-line py-7 @2xl:grid-cols-[4.5rem_1fr]", it.link && "vb-linked", !showIndex && "grid-cols-1!"), children: [showIndex ? _jsx("span", { className: "row-span-3 pt-1.5 font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }) : null, itemTitle(it, i), itemText(it, i, "max-w-[52ch]"), more(it)] }, i))) }));
|
|
@@ -13,7 +13,7 @@ export interface FooterProps extends Omit<BlockBaseProps, "variant" | "labels" |
|
|
|
13
13
|
*/
|
|
14
14
|
variant?: FooterVariant;
|
|
15
15
|
brand: {
|
|
16
|
-
name: string;
|
|
16
|
+
name: string; /** An image logo renders 2rem tall (`sizes="12rem"`). */
|
|
17
17
|
logo?: React.ReactNode | SiteImage2;
|
|
18
18
|
href?: string;
|
|
19
19
|
};
|
|
@@ -10,6 +10,7 @@ import { BrandIcon, BRAND_NAMES } from "./shared/brand-icons.js";
|
|
|
10
10
|
import { ConsentSettingsButton } from "./shared/consent-island.js";
|
|
11
11
|
import { ContactFormIsland } from "./shared/contact-form-island.js";
|
|
12
12
|
import { contactIslandLabelKeys } from "./shared/island-keys.js";
|
|
13
|
+
import { TAP } from "./shared/tap.js";
|
|
13
14
|
const isImage = (v) => !!v && typeof v === "object" && "src" in v && !React.isValidElement(v);
|
|
14
15
|
/**
|
|
15
16
|
* Site footer: navigation, contact, socials, legal links and the company details e-commerce law
|
|
@@ -27,14 +28,14 @@ export function Footer({ variant = "columns", brand, about, columns = [], contac
|
|
|
27
28
|
const y = String(year ?? new Date().getFullYear());
|
|
28
29
|
const ctx = { t, locale, components, labels, editMode };
|
|
29
30
|
const copyright = t("copyright", { year: y, name: company?.legalName ?? brand.name });
|
|
30
|
-
const legalLine = (_jsxs("div", { className: "flex flex-col gap-3 text-small text-fg-muted @4xl:flex-row @4xl:items-center @4xl:justify-between @4xl:gap-8", children: [_jsx("p", { children: copyright }), _jsx("nav", { "aria-label": t("legal"), children: _jsxs("ul", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: [legalLinks.map((l, i) => (_jsx("li", { children: _jsx(BlockLink, { href: l.href, newTab: l.newTab, components: components, labels: labels, locale: locale, className: "rounded-[2px] text-fg-muted underline-offset-[0.25em] transition-colors hover:text-fg hover:underline focus-ring", children: l.label }) }, i))), cookieSettings ? (_jsx("li", { children: _jsx(ConsentSettingsButton, { className: "text-fg-muted transition-colors hover:text-fg", children: t("cookieSettings") }) })) : null, credit ? (_jsx("li", { children: _jsx(BlockLink, { href: credit.href, newTab: credit.newTab, components: components, labels: labels, locale: locale, className: "rounded-[2px] text-fg-muted underline-offset-[0.25em] hover:text-fg hover:underline focus-ring", children: t("madeBy", { name: typeof credit.label === "string" ? credit.label : "" }) }) })) : null] }) })] }));
|
|
31
|
+
const legalLine = (_jsxs("div", { className: "flex flex-col gap-3 text-small text-fg-muted @4xl:flex-row @4xl:items-center @4xl:justify-between @4xl:gap-8", children: [_jsx("p", { children: copyright }), _jsx("nav", { "aria-label": t("legal"), children: _jsxs("ul", { className: "flex flex-wrap items-center gap-x-5 gap-y-2 pointer-coarse:gap-y-0", children: [legalLinks.map((l, i) => (_jsx("li", { children: _jsx(BlockLink, { href: l.href, newTab: l.newTab, components: components, labels: labels, locale: locale, className: cn(TAP, "rounded-[2px] text-fg-muted underline-offset-[0.25em] transition-colors hover:text-fg hover:underline focus-ring"), children: l.label }) }, i))), cookieSettings ? (_jsx("li", { children: _jsx(ConsentSettingsButton, { className: cn(TAP, "text-fg-muted transition-colors hover:text-fg"), children: t("cookieSettings") }) })) : null, credit ? (_jsx("li", { children: _jsx(BlockLink, { href: credit.href, newTab: credit.newTab, components: components, labels: labels, locale: locale, className: cn(TAP, "rounded-[2px] text-fg-muted underline-offset-[0.25em] hover:text-fg hover:underline focus-ring"), children: t("madeBy", { name: typeof credit.label === "string" ? credit.label : "" }) }) })) : null] }) })] }));
|
|
31
32
|
// The legal name already sits in the © line; the company row only earns its place with registry data.
|
|
32
33
|
const companyLine = company?.registry?.length ? (_jsxs("div", { className: "flex flex-col gap-1.5 @4xl:flex-row @4xl:items-baseline @4xl:gap-6", ...fieldAttr(editMode, "company"), children: [_jsx("p", { className: "text-small text-fg-muted", children: company.legalName }), company.registry?.length ? (_jsx("dl", { className: "flex flex-wrap gap-x-6 gap-y-1 font-mono text-eyebrow uppercase tracking-[0.06em] text-fg-muted", children: company.registry.map((r, i) => (_jsxs("div", { className: "flex gap-2", children: [_jsx("dt", { children: r.label }), _jsx("dd", { className: "tabular text-fg-muted", children: r.value })] }, i))) })) : null] })) : null;
|
|
33
34
|
if (variant === "minimal") {
|
|
34
35
|
return (_jsx(Section, { as: "footer", theme: { rhythm: "tight", ...theme }, anchor: anchor, blockId: id, blockType: "footer", editMode: editMode, className: cn("vb-footer", className), "data-variant": "minimal", "aria-label": t("footer"), children: _jsxs(Container, { className: "flex flex-col gap-6 @4xl:flex-row @4xl:items-center @4xl:justify-between", children: [_jsx(BrandMark, { brand: brand, ctx: ctx }), columns[0]?.links.length ? (_jsx("nav", { "aria-label": t("footerNav"), children: _jsx("ul", { className: "flex flex-wrap gap-x-6 gap-y-2 text-small", children: columns.flatMap((c) => c.links).slice(0, 5).map((l, i) => (_jsx("li", { children: _jsx(FooterLink, { link: l, ctx: ctx }) }, i))) }) })) : null, _jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-3", children: [_jsx(SocialList, { social: social, ctx: ctx }), _jsx("p", { className: "text-small text-fg-muted", children: copyright })] })] }) }));
|
|
35
36
|
}
|
|
36
|
-
const linkColumns = columns.slice(0, 4).map((c, i) => (_jsxs("nav", { "aria-labelledby": `${base}-col-${i}`, className: "flex flex-col gap-4", children: [_jsx("p", { id: `${base}-col-${i}`, className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: c.title }), _jsx("ul", { className: "flex flex-col gap-2.5", children: c.links.slice(0, 8).map((l, j) => (_jsx("li", { children: _jsx(FooterLink, { link: l, ctx: ctx }) }, j))) })] }, i)));
|
|
37
|
-
const contactBlock = contact ? (_jsxs("div", { className: "flex flex-col gap-2 text-small text-fg-muted", children: [contact.address ? (_jsx("address", { className: "not-italic", children: (typeof contact.address === "string" ? contact.address.split("\n") : addressLines(contact.address, { locale })).map((l, i) => (_jsx("span", { className: "block", children: l }, i))) })) : null, contact.phone ? (_jsx("a", { href: telHref(contact.phone), className: "self-start text-fg tabular underline decoration-line underline-offset-[0.25em] hover:decoration-current focus-ring rounded-[2px]", "data-track": "call", children: prettyPhone(contact.phone) })) : null, contact.email ? (_jsx("a", { href: `mailto:${contact.email}`, className: "self-start text-fg underline decoration-line underline-offset-[0.25em] hover:decoration-current focus-ring rounded-[2px]", "data-track": "email", children: contact.email })) : null] })) : null;
|
|
37
|
+
const linkColumns = columns.slice(0, 4).map((c, i) => (_jsxs("nav", { "aria-labelledby": `${base}-col-${i}`, className: "flex flex-col gap-4", children: [_jsx("p", { id: `${base}-col-${i}`, className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: c.title }), _jsx("ul", { className: "flex flex-col gap-2.5 pointer-coarse:gap-0", children: c.links.slice(0, 8).map((l, j) => (_jsx("li", { children: _jsx(FooterLink, { link: l, ctx: ctx }) }, j))) })] }, i)));
|
|
38
|
+
const contactBlock = contact ? (_jsxs("div", { className: "flex flex-col gap-2 text-small text-fg-muted", children: [contact.address ? (_jsx("address", { className: "not-italic", children: (typeof contact.address === "string" ? contact.address.split("\n") : addressLines(contact.address, { locale })).map((l, i) => (_jsx("span", { className: "block", children: l }, i))) })) : null, contact.phone ? (_jsx("a", { href: telHref(contact.phone), className: cn(TAP, "self-start text-fg tabular underline decoration-line underline-offset-[0.25em] hover:decoration-current focus-ring rounded-[2px]"), "data-track": "call", children: prettyPhone(contact.phone) })) : null, contact.email ? (_jsx("a", { href: `mailto:${contact.email}`, className: cn(TAP, "self-start text-fg underline decoration-line underline-offset-[0.25em] hover:decoration-current focus-ring rounded-[2px]"), "data-track": "email", children: contact.email })) : null] })) : null;
|
|
38
39
|
const newsletterBlock = newsletter ? (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("p", { className: "text-body font-medium text-fg", children: newsletter.title ?? t("newsletter") }), _jsx("p", { className: "text-small text-fg-muted", children: newsletter.hint ?? t("newsletterHint") }), _jsx(ContactFormIsland, { action: newsletter.action, fields: [{ name: "email", required: true, label: t("emailAddress") }], labels: { ...labelTemplates(labels, contactIslandLabelKeys), send: t("subscribe"), sent: t("subscribed") }, locale: locale, layout: "minimal", idBase: `${base}-nl`, className: "vb-footer-newsletter" })] })) : null;
|
|
39
40
|
if (variant === "wordmark") {
|
|
40
41
|
return (_jsxs(Section, { as: "footer", theme: theme, anchor: anchor, blockId: id, blockType: "footer", editMode: editMode, className: cn("vb-footer overflow-hidden pb-0!", className), "data-variant": "wordmark", "aria-label": t("footer"), children: [_jsxs(Container, { className: "flex flex-col gap-12", children: [_jsxs("div", { className: "grid gap-x-(--gutter) gap-y-10 @2xl:grid-cols-2 @4xl:grid-cols-12", children: [_jsxs("div", { className: "flex flex-col gap-5 @4xl:col-span-4", children: [about ? _jsx("p", { className: "max-w-[40ch] text-fg-muted text-pretty", children: about }) : null, contactBlock, _jsx(SocialList, { social: social, ctx: ctx })] }), _jsx("div", { className: "grid grid-cols-2 gap-x-(--gutter) gap-y-10 @2xl:col-span-1 @4xl:col-span-8 @4xl:grid-cols-4", children: linkColumns })] }), newsletterBlock ? _jsx("div", { className: "max-w-xl", children: newsletterBlock }) : null, _jsxs("div", { className: "flex flex-col gap-4 border-t border-line pt-6", children: [companyLine, legalLine] })] }), _jsx("p", { "aria-hidden": true, className: "vb-wordmark select-none whitespace-nowrap px-(--gutter) font-display text-display-2xl leading-[0.78] text-fg",
|
|
@@ -46,16 +47,16 @@ export function Footer({ variant = "columns", brand, about, columns = [], contac
|
|
|
46
47
|
return (_jsx(Section, { as: "footer", theme: theme, anchor: anchor, blockId: id, blockType: "footer", editMode: editMode, className: cn("vb-footer", className), "data-variant": "columns", "aria-label": t("footer"), children: _jsxs(Container, { className: "flex flex-col gap-14", children: [_jsxs("div", { className: "grid gap-x-(--gutter) gap-y-12 @4xl:grid-cols-12", children: [_jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-4", children: [_jsx(BrandMark, { brand: brand, ctx: ctx }), about ? (_jsx("p", { className: "max-w-[40ch] text-small text-fg-muted text-pretty", ...fieldAttr(editMode, "about"), children: about })) : null, contactBlock, _jsx(SocialList, { social: social, ctx: ctx })] }), _jsx("div", { className: cn("grid grid-cols-2 gap-x-(--gutter) gap-y-10 @4xl:col-span-7 @4xl:col-start-6", columns.length >= 3 && "@2xl:grid-cols-3", columns.length >= 4 && "@6xl:grid-cols-4"), children: linkColumns })] }), newsletterBlock ? _jsx("div", { className: "max-w-xl border-t border-line pt-10", children: newsletterBlock }) : null, _jsxs("div", { className: "flex flex-col gap-4 border-t border-line pt-6", children: [companyLine, legalLine] })] }) }));
|
|
47
48
|
}
|
|
48
49
|
function BrandMark({ brand, ctx }) {
|
|
49
|
-
const mark = isImage(brand.logo) ? (_jsx(BlockImage, { ...brand.logo, alt: brand.logo.alt ?? brand.name, fit: "contain", className: "h-8 w-auto bg-transparent", components: ctx.components })) : ((brand.logo ?? _jsx("span", { className: "font-display text-display-m leading-none text-fg", children: brand.name })));
|
|
50
|
+
const mark = isImage(brand.logo) ? (_jsx(BlockImage, { ...brand.logo, alt: brand.logo.alt ?? brand.name, fit: "contain", sizes: "12rem", className: "h-8 w-auto bg-transparent", components: ctx.components })) : ((brand.logo ?? _jsx("span", { className: "font-display text-display-m leading-none text-fg", children: brand.name })));
|
|
50
51
|
if (!brand.href)
|
|
51
52
|
return _jsx("div", { className: "self-start", children: mark });
|
|
52
|
-
return (_jsx(BlockLink, { href: brand.href, components: ctx.components, labels: ctx.labels, locale: ctx.locale, "aria-label": brand.name, className: "self-start rounded-[2px] focus-ring", children: mark }));
|
|
53
|
+
return (_jsx(BlockLink, { href: brand.href, components: ctx.components, labels: ctx.labels, locale: ctx.locale, "aria-label": brand.name, className: "inline-flex min-h-6 items-center self-start rounded-[2px] focus-ring pointer-coarse:min-h-11", children: mark }));
|
|
53
54
|
}
|
|
54
55
|
function FooterLink({ link, ctx }) {
|
|
55
|
-
return (_jsx(BlockLink, { href: link.href, newTab: link.newTab, components: ctx.components, labels: ctx.labels, locale: ctx.locale, className: "rounded-[2px] text-small text-fg underline-offset-[0.25em] transition-colors hover:text-accent-ink hover:underline focus-ring", children: link.label }));
|
|
56
|
+
return (_jsx(BlockLink, { href: link.href, newTab: link.newTab, components: ctx.components, labels: ctx.labels, locale: ctx.locale, className: cn(TAP, "rounded-[2px] text-small text-fg underline-offset-[0.25em] transition-colors hover:text-accent-ink hover:underline focus-ring"), children: link.label }));
|
|
56
57
|
}
|
|
57
58
|
function SocialList({ social, ctx }) {
|
|
58
59
|
if (!social?.length)
|
|
59
60
|
return null;
|
|
60
|
-
return (_jsx("ul", { className: "flex flex-wrap items-center gap-1", "aria-label": ctx.t("social"), children: social.slice(0, 6).map((s, i) => (_jsx("li", { children: _jsx("a", { href: s.href, target: "_blank", rel: "noopener noreferrer", "aria-label": `${BRAND_NAMES[s.network]} ${ctx.t("opensInNewTab")}`, className: "inline-flex size-10 items-center justify-center rounded-ui-sm text-fg-muted transition-colors hover:bg-hover hover:text-fg focus-ring", "data-track": `social:${s.network}`, children: _jsx(BrandIcon, { name: s.network, className: "size-[18px]" }) }) }, i))) }));
|
|
61
|
+
return (_jsx("ul", { className: "flex flex-wrap items-center gap-1 pointer-coarse:-ms-1", "aria-label": ctx.t("social"), children: social.slice(0, 6).map((s, i) => (_jsx("li", { children: _jsx("a", { href: s.href, target: "_blank", rel: "noopener noreferrer", "aria-label": `${BRAND_NAMES[s.network]} ${ctx.t("opensInNewTab")}`, className: "inline-flex size-10 items-center justify-center rounded-ui-sm text-fg-muted transition-colors hover:bg-hover hover:text-fg focus-ring pointer-coarse:size-11", "data-track": `social:${s.network}`, children: _jsx(BrandIcon, { name: s.network, className: "size-[18px]" }) }) }, i))) }));
|
|
61
62
|
}
|
|
@@ -22,7 +22,12 @@ export interface GallerySectionProps extends Omit<BlockBaseProps, "variant" | "l
|
|
|
22
22
|
eyebrow?: React.ReactNode;
|
|
23
23
|
title?: React.ReactNode;
|
|
24
24
|
lead?: React.ReactNode;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* 3–40 images, `alt` required. `sizes` per variant: mosaic `min(34vw, 26rem)` per 4-col cell
|
|
27
|
+
* (`min(67vw, 51rem)` for wide/big) above 42rem, 50vw/100vw below · grid `min(34vw, 26rem)` /
|
|
28
|
+
* 50vw · filmstrip from the cell height × ratio (≈ 12–24rem for 4:5) · split-caption
|
|
29
|
+
* `min(59vw, 45rem)` / 100vw.
|
|
30
|
+
*/
|
|
26
31
|
images: GalleryImage[];
|
|
27
32
|
/** Open images in a full-screen viewer. Default true. */
|
|
28
33
|
lightbox?: boolean;
|
|
@@ -8,6 +8,7 @@ import { site2Text } from "./shared/labels-2.js";
|
|
|
8
8
|
import { textOf, useBlockIds2 } from "./shared/kit-2.js";
|
|
9
9
|
import { GalleryLightbox } from "./shared/gallery-lightbox.js";
|
|
10
10
|
import { Stagger } from "../../motion/reveal.js";
|
|
11
|
+
import { columnSizes, filmstripSizes } from "./shared/image-sizes.js";
|
|
11
12
|
/**
|
|
12
13
|
* Venue, food and project photos — the section restaurants, spas and hotels live on. Server
|
|
13
14
|
* grid; the lightbox is the Media group's viewer (focus trap, Esc, arrows, "Image 3 of 12"),
|
|
@@ -25,19 +26,19 @@ export function GallerySection({ variant: variantProp = "mosaic", eyebrow, title
|
|
|
25
26
|
const header = title || eyebrow || lead ? _jsx(SectionHeader, { eyebrow: eyebrow, title: title, lead: lead, headingId: headingId, headingLevel: headingLevel, editMode: editMode, labels: labels, locale: locale }) : null;
|
|
26
27
|
let body;
|
|
27
28
|
if (variant === "filmstrip") {
|
|
28
|
-
body = (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("ul", { className: "vb-filmstrip scrollbar-none flex snap-x snap-mandatory gap-2 overflow-x-auto pb-1", tabIndex: 0, "aria-label": textOf(title).replace(/\*/g, "") || t("galleryImages", { count: images.length }), children: images.map((g, i) => (_jsx("li", { className: "shrink-0 snap-start", children: _jsx(Cell, { g: g, i: i, ctx: ctx, className: "h-[clamp(15rem,38cqi,30rem)]", style: { aspectRatio: g.image.width && g.image.height ? `${g.image.width} / ${g.image.height}` : "4 / 5" }, sizes:
|
|
29
|
+
body = (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("ul", { className: "vb-filmstrip scrollbar-none flex snap-x snap-mandatory gap-2 overflow-x-auto pb-1", tabIndex: 0, "aria-label": textOf(title).replace(/\*/g, "") || t("galleryImages", { count: images.length }), children: images.map((g, i) => (_jsx("li", { className: "shrink-0 snap-start", children: _jsx(Cell, { g: g, i: i, ctx: ctx, className: "h-[clamp(15rem,38cqi,30rem)]", style: { aspectRatio: g.image.width && g.image.height ? `${g.image.width} / ${g.image.height}` : "4 / 5" }, sizes: filmstripSizes(g.image.width && g.image.height ? g.image.width / g.image.height : 0.8), fill: true }) }, i))) }), _jsx("p", { className: "vb-container font-mono text-eyebrow tabular uppercase tracking-[0.08em] text-fg-muted", children: t("galleryImages", { count: images.length }) })] }));
|
|
29
30
|
}
|
|
30
31
|
else if (variant === "mosaic") {
|
|
31
|
-
body = (_jsx(Stagger, { asChild: true, effect: "clip", disabled: editMode, children: _jsx("ul", { className: "vb-mosaic grid grid-cols-2 gap-2 @2xl:grid-cols-12", children: images.map((g, i) => (_jsx("li", { "data-span": g.span ?? "1", className: "vb-mosaic-cell min-h-0", children: _jsx(Cell, { g: g, i: i, ctx: ctx, className: "size-full", sizes: "(
|
|
32
|
+
body = (_jsx(Stagger, { asChild: true, effect: "clip", disabled: editMode, children: _jsx("ul", { className: "vb-mosaic grid grid-cols-2 gap-2 @2xl:grid-cols-12", children: images.map((g, i) => (_jsx("li", { "data-span": g.span ?? "1", className: "vb-mosaic-cell min-h-0", children: _jsx(Cell, { g: g, i: i, ctx: ctx, className: "size-full", sizes: g.span === "wide" || g.span === "big" ? columnSizes(8 / 12, { from: "42rem" }) : columnSizes(4 / 12, { from: "42rem", narrow: "50vw" }), fill: true }) }, i))) }) }));
|
|
32
33
|
}
|
|
33
34
|
else if (variant === "split-caption") {
|
|
34
35
|
body = null;
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
38
|
const a = aspect === "mixed" ? undefined : aspect;
|
|
38
|
-
body = (_jsx(Stagger, { asChild: true, disabled: editMode, children: _jsx("ul", { className: "grid grid-cols-2 gap-x-3 gap-y-6 @4xl:grid-cols-3 @4xl:gap-x-4", children: images.map((g, i) => (_jsx("li", { children: _jsxs("figure", { className: "flex flex-col gap-2.5", children: [_jsx(Cell, { g: g, i: i, ctx: ctx, aspect: a ?? "3:2", sizes:
|
|
39
|
+
body = (_jsx(Stagger, { asChild: true, disabled: editMode, children: _jsx("ul", { className: "grid grid-cols-2 gap-x-3 gap-y-6 @4xl:grid-cols-3 @4xl:gap-x-4", children: images.map((g, i) => (_jsx("li", { children: _jsxs("figure", { className: "flex flex-col gap-2.5", children: [_jsx(Cell, { g: g, i: i, ctx: ctx, aspect: a ?? "3:2", sizes: columnSizes(1 / 3, { narrow: "50vw" }) }), g.caption ? _jsx("figcaption", { className: "text-small text-fg-muted", children: g.caption }) : null] }) }, i))) }) }));
|
|
39
40
|
}
|
|
40
|
-
const content = variant === "split-caption" ? (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12", children: [_jsx("div", { className: "@4xl:col-span-4", children: _jsx("div", { className: "flex flex-col gap-6 @4xl:sticky @4xl:top-[calc(var(--topbar-h,0px)+2rem)]", children: header }) }), _jsx("ol", { className: "flex flex-col gap-12 @4xl:col-span-7 @4xl:col-start-6", children: images.map((g, i) => (_jsx("li", { children: _jsxs("figure", { className: "flex flex-col gap-4", children: [_jsx(Cell, { g: g, i: i, ctx: ctx, sizes:
|
|
41
|
+
const content = variant === "split-caption" ? (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12", children: [_jsx("div", { className: "@4xl:col-span-4", children: _jsx("div", { className: "flex flex-col gap-6 @4xl:sticky @4xl:top-[calc(var(--topbar-h,0px)+2rem)]", children: header }) }), _jsx("ol", { className: "flex flex-col gap-12 @4xl:col-span-7 @4xl:col-start-6", children: images.map((g, i) => (_jsx("li", { children: _jsxs("figure", { className: "flex flex-col gap-4", children: [_jsx(Cell, { g: g, i: i, ctx: ctx, sizes: columnSizes(7 / 12) }), g.caption ? (_jsxs("figcaption", { className: "flex gap-4 text-small text-fg-muted text-pretty", children: [_jsx("span", { "aria-hidden": true, className: "font-mono text-eyebrow tabular text-fg-subtle", children: t("imageCounter", { index: i + 1, total: images.length }) }), _jsx("span", { children: g.caption })] })) : null] }) }, i))) })] })) : (_jsxs(_Fragment, { children: [header ? _jsx(Container, { children: header }) : null, variant === "filmstrip" ? _jsx("div", { className: "vb-filmstrip-wrap", children: body }) : _jsx(Container, { children: body })] }));
|
|
41
42
|
return (_jsx(Section, { theme: theme, anchor: anchor, headingId: title ? headingId : undefined, blockId: id, blockType: "gallery", editMode: editMode, className: cn("vb-gallery", className), "data-variant": variant, children: _jsx("div", { className: "flex flex-col gap-10 @4xl:gap-14", children: lightbox ? (_jsx(GalleryLightbox, { items: images.map((g) => ({ src: g.image.src, alt: g.image.alt, width: g.image.width, height: g.image.height, caption: textOf(g.caption) || undefined, focalPoint: g.image.focal })), children: content })) : (content) }) }));
|
|
42
43
|
}
|
|
43
44
|
function Cell({ g, i, ctx, className, style, sizes, aspect, fill }) {
|
|
@@ -13,11 +13,12 @@ export interface HeroProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
|
13
13
|
variant?: HeroVariant;
|
|
14
14
|
/** Small mono line above the title ("Agency · Maltepe, Istanbul"). */
|
|
15
15
|
eyebrow?: React.ReactNode;
|
|
16
|
-
/** The page `h1`. A string supports one `*emphasis
|
|
16
|
+
/** The page `h1`. A string supports one `*emphasis*`; `\n` sets a line break (each line is a block, joined by a real space so search engines and screen readers read "közde, muhabbet"). ≤ 9 words reads best. */
|
|
17
17
|
title: React.ReactNode;
|
|
18
18
|
lead?: React.ReactNode;
|
|
19
19
|
/** Up to two actions; one primary. */
|
|
20
20
|
actions?: SiteCta[];
|
|
21
|
+
/** `sizes`: editorial strip and full-bleed `100vw`; split-media portrait `(min-width: 56rem) 42vw, 100vw` (it bleeds to the edge). Above the fold: always `priority`. */
|
|
21
22
|
media?: SiteImage;
|
|
22
23
|
/** full-bleed: muted looping MP4 (`media` is the required poster). */
|
|
23
24
|
video?: {
|
|
@@ -27,7 +28,7 @@ export interface HeroProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
|
27
28
|
caption?: React.ReactNode;
|
|
28
29
|
/** Small true facts ("Founded" · "2014"), up to 4. */
|
|
29
30
|
meta?: SiteMeta[];
|
|
30
|
-
/** product: the product screenshot (no device frame). */
|
|
31
|
+
/** product: the product screenshot (no device frame). `sizes`: `min(100vw, 76rem)`. */
|
|
31
32
|
screenshot?: SiteImage;
|
|
32
33
|
/** product: a live composition instead of a screenshot (plain JSX only). */
|
|
33
34
|
visual?: React.ReactNode;
|
package/dist/blocks/site/hero.js
CHANGED
|
@@ -6,6 +6,8 @@ import { BlockImage } from "../block-image.js";
|
|
|
6
6
|
import { BlockLink } from "../block-link.js";
|
|
7
7
|
import { HeroVideo } from "./hero-video.js";
|
|
8
8
|
import { ArrowEnd, CtaList, field, siteText, useBlockIds } from "./_kit-1.js";
|
|
9
|
+
import { headingLines } from "./shared/heading-lines.js";
|
|
10
|
+
import { columnSizes, SITE_SIZES } from "./shared/image-sizes.js";
|
|
9
11
|
/**
|
|
10
12
|
* The first screen and the page `h1`, in five characters per sector — never the generic
|
|
11
13
|
* "centred title + two buttons + abstract visual". Server component; the background video is a
|
|
@@ -43,28 +45,28 @@ function Meta({ items, className }) {
|
|
|
43
45
|
return (_jsx("dl", { className: cn("flex flex-col gap-1.5 font-mono text-small text-fg-muted", className), children: items.slice(0, 4).map((m, i) => (_jsxs("div", { className: "flex gap-2", children: [_jsx("dt", { className: "text-fg-subtle after:content-['\u00B7'] after:ps-2", children: m.label }), _jsx("dd", { className: "text-fg", children: m.value })] }, i))) }));
|
|
44
46
|
}
|
|
45
47
|
function Title({ p, size, className }) {
|
|
46
|
-
return (_jsx(BlockHeading, { id: p.headingId, level: p.headingLevel, size: size, text: p.title, editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: cn("vb-hero-title", enter(p.editMode, 1, "title"), className) }));
|
|
48
|
+
return (_jsx(BlockHeading, { id: p.headingId, level: p.headingLevel, size: size, text: headingLines(p.title), editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: cn("vb-hero-title", enter(p.editMode, 1, "title"), className) }));
|
|
47
49
|
}
|
|
48
50
|
/* editorial ----------------------------------------------------------- */
|
|
49
51
|
function Editorial(p) {
|
|
50
52
|
const { eyebrow, lead, actions, meta, media, components, labels, locale, editMode } = p;
|
|
51
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Container, { className: "flex flex-col gap-8", children: [_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow }), _jsx(Title, { p: p, size: "display-xl", className: "max-w-[18ch]" }), _jsxs("div", { className: "grid gap-x-(--gutter) gap-y-8 pt-2 @4xl:grid-cols-12 @4xl:items-end", children: [_jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-6 @6xl:col-span-5", children: [_jsx(Lead, { editMode: editMode, className: enter(editMode, 2), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, max: 1, editMode: editMode, className: enter(editMode, 3) })] }), _jsx(Meta, { items: meta, className: cn("@4xl:col-span-4 @4xl:col-start-9 @4xl:items-end @4xl:text-end", enter(editMode, 4)) })] })] }), media ? (_jsx("div", { className: cn("vb-hero-strip mt-[calc(var(--section-y)*0.6)]", enter(editMode, 4, "media")), children: _jsx(BlockImage, { ...media, aspect: "21:9", priority: true, sizes:
|
|
53
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Container, { className: "flex flex-col gap-8", children: [_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow }), _jsx(Title, { p: p, size: "display-xl", className: "max-w-[18ch]" }), _jsxs("div", { className: "grid gap-x-(--gutter) gap-y-8 pt-2 @4xl:grid-cols-12 @4xl:items-end", children: [_jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-6 @6xl:col-span-5", children: [_jsx(Lead, { editMode: editMode, className: enter(editMode, 2), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, max: 1, editMode: editMode, className: enter(editMode, 3) })] }), _jsx(Meta, { items: meta, className: cn("@4xl:col-span-4 @4xl:col-start-9 @4xl:items-end @4xl:text-end", enter(editMode, 4)) })] })] }), media ? (_jsx("div", { className: cn("vb-hero-strip mt-[calc(var(--section-y)*0.6)]", enter(editMode, 4, "media")), children: _jsx(BlockImage, { ...media, aspect: "21:9", priority: true, sizes: SITE_SIZES.bleed, components: components, labels: labels, editMode: editMode, field: "props.media" }) })) : null] }));
|
|
52
54
|
}
|
|
53
55
|
/* split-media ---------------------------------------------------------- */
|
|
54
56
|
function SplitMedia(p) {
|
|
55
57
|
const { eyebrow, lead, actions, meta, media, caption, components, labels, locale, editMode } = p;
|
|
56
|
-
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-start", children: [_jsxs("div", { className: "order-2 flex flex-col gap-7 @4xl:order-1 @4xl:col-span-7 @4xl:pt-[calc(var(--section-y)*0.35)] @6xl:pe-[calc(var(--gutter)*2)]", children: [_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow }), _jsx(Title, { p: p, size: "display-xl" }), _jsx(Lead, { editMode: editMode, className: cn("max-w-[46ch]", enter(editMode, 2)), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, editMode: editMode, className: cn("pt-2", enter(editMode, 3)) }), _jsx(Meta, { items: meta, className: cn("mt-auto border-t border-line pt-5 @4xl:mt-10", enter(editMode, 4)) })] }), media ? (_jsxs("figure", { className: cn("vb-hero-portrait vb-bleed-end relative order-1 my-0 @4xl:order-2 @4xl:col-span-5", enter(editMode, 1, "media")), ...field(editMode, "media"), children: [_jsx(BlockImage, { ...media, aspect: "4:5", priority: true, sizes:
|
|
58
|
+
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-start", children: [_jsxs("div", { className: "order-2 flex flex-col gap-7 @4xl:order-1 @4xl:col-span-7 @4xl:pt-[calc(var(--section-y)*0.35)] @6xl:pe-[calc(var(--gutter)*2)]", children: [_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow }), _jsx(Title, { p: p, size: "display-xl" }), _jsx(Lead, { editMode: editMode, className: cn("max-w-[46ch]", enter(editMode, 2)), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, editMode: editMode, className: cn("pt-2", enter(editMode, 3)) }), _jsx(Meta, { items: meta, className: cn("mt-auto border-t border-line pt-5 @4xl:mt-10", enter(editMode, 4)) })] }), media ? (_jsxs("figure", { className: cn("vb-hero-portrait vb-bleed-end relative order-1 my-0 @4xl:order-2 @4xl:col-span-5", enter(editMode, 1, "media")), ...field(editMode, "media"), children: [_jsx(BlockImage, { ...media, aspect: "4:5", priority: true, sizes: columnSizes(5 / 12, { bleed: true }), className: "aspect-square! @4xl:aspect-[4/5]!", components: components, labels: labels }), caption ? (_jsx("figcaption", { className: "absolute bottom-0 start-0 bg-scene px-3 py-2 font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", ...field(editMode, "caption"), children: caption })) : null] })) : null] }));
|
|
57
59
|
}
|
|
58
60
|
/* full-bleed ----------------------------------------------------------- */
|
|
59
61
|
function FullBleed(p) {
|
|
60
62
|
const { eyebrow, lead, actions, media, video, aside, height = "auto", components, labels, locale, editMode } = p;
|
|
61
63
|
const t = siteText(labels, locale);
|
|
62
|
-
return (_jsxs("div", { className: cn("vb-hero-bleed", height === "screen" && "vb-hero-screen"), children: [_jsxs("div", { className: cn("vb-hero-media", enter(editMode, 0, "media")), "aria-hidden": video ? undefined : "true", children: [media ? (_jsx(BlockImage, { ...media, decorative: true, priority: true, sizes:
|
|
64
|
+
return (_jsxs("div", { className: cn("vb-hero-bleed", height === "screen" && "vb-hero-screen"), children: [_jsxs("div", { className: cn("vb-hero-media", enter(editMode, 0, "media")), "aria-hidden": video ? undefined : "true", children: [media ? (_jsx(BlockImage, { ...media, decorative: true, priority: true, sizes: SITE_SIZES.bleed, className: "absolute inset-0 h-full", components: components, labels: labels, editMode: editMode, field: "props.media" })) : null, video && media ? _jsx(HeroVideo, { src: video.src, poster: media.src, pauseLabel: t("pauseVideo"), playLabel: t("resumeVideo") }) : null] }), _jsxs(Container, { className: "relative z-[1] grid w-full content-end gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-end", children: [_jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-8 @6xl:col-span-7", children: [_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow }), _jsx(Title, { p: p, size: "display-xl" }), _jsx(Lead, { editMode: editMode, className: cn("max-w-[52ch] text-fg", enter(editMode, 2)), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, editMode: editMode, className: cn("pt-2", enter(editMode, 3)) })] }), aside ? (_jsxs("aside", { className: cn("vb-hero-aside surface flex flex-col gap-3 rounded-ui p-5 @4xl:col-span-4 @4xl:col-start-9 @6xl:col-span-3 @6xl:col-start-10", enter(editMode, 4)), ...field(editMode, "aside"), children: [_jsx("p", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: aside.title }), aside.body ? _jsx("p", { className: "text-body tabular text-fg", children: aside.body }) : null, aside.cta ? _jsx(CtaList, { actions: [aside.cta], components: components, labels: labels, locale: locale, size: "md", max: 1, className: "pt-1 [&>a]:w-full" }) : null] })) : null] })] }));
|
|
63
65
|
}
|
|
64
66
|
/* product -------------------------------------------------------------- */
|
|
65
67
|
function Product(p) {
|
|
66
68
|
const { eyebrow, lead, actions, release, screenshot, visual, meta, components, labels, locale, editMode } = p;
|
|
67
|
-
return (_jsxs(Container, { className: "flex flex-col", children: [_jsxs("div", { className: "flex flex-col gap-7", children: [release ? (_jsxs(BlockLink, { href: release.href, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.release", className: cn("group/rel inline-flex w-fit items-center gap-3 rounded-full border border-line py-1 ps-1 pe-3.5 text-small text-fg-muted transition-colors hover:border-field-border-hover hover:text-fg focus-ring", enter(editMode, 0)), children: [release.tag ? _jsx("span", { className: "rounded-full bg-accent-soft px-2.5 py-0.5 font-mono text-eyebrow tracking-[0.04em] text-accent-ink", children: release.tag }) : null, _jsx("span", { children: release.label }), _jsx(ArrowEnd, { className: "group-hover/rel:translate-x-0.5 rtl:group-hover/rel:-translate-x-0.5" })] })) : (_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow })), _jsxs("div", { className: "grid gap-x-(--gutter) gap-y-7 @4xl:grid-cols-12 @4xl:items-end", children: [_jsx(Title, { p: p, size: "display-xl", className: "@4xl:col-span-8" }), _jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-4", children: [_jsx(Lead, { editMode: editMode, className: enter(editMode, 2), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, size: "md", editMode: editMode, className: enter(editMode, 3) })] })] }), _jsx(Meta, { items: meta, className: cn("flex-row flex-wrap gap-x-6", enter(editMode, 4)) })] }), screenshot || visual ? (_jsx("div", { className: cn("vb-hero-shot mt-[calc(var(--section-y)*0.55)]", enter(editMode, 5, "media")), ...field(editMode, "screenshot"), children: visual ?? _jsx(BlockImage, { ...screenshot, priority: true, sizes:
|
|
69
|
+
return (_jsxs(Container, { className: "flex flex-col", children: [_jsxs("div", { className: "flex flex-col gap-7", children: [release ? (_jsxs(BlockLink, { href: release.href, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.release", className: cn("group/rel inline-flex w-fit items-center gap-3 rounded-full border border-line py-1 ps-1 pe-3.5 text-small text-fg-muted transition-colors hover:border-field-border-hover hover:text-fg focus-ring", enter(editMode, 0)), children: [release.tag ? _jsx("span", { className: "rounded-full bg-accent-soft px-2.5 py-0.5 font-mono text-eyebrow tracking-[0.04em] text-accent-ink", children: release.tag }) : null, _jsx("span", { children: release.label }), _jsx(ArrowEnd, { className: "group-hover/rel:translate-x-0.5 rtl:group-hover/rel:-translate-x-0.5" })] })) : (_jsx(Eyebrow, { editMode: editMode, className: enter(editMode, 0), children: eyebrow })), _jsxs("div", { className: "grid gap-x-(--gutter) gap-y-7 @4xl:grid-cols-12 @4xl:items-end", children: [_jsx(Title, { p: p, size: "display-xl", className: "@4xl:col-span-8" }), _jsxs("div", { className: "flex flex-col gap-6 @4xl:col-span-4", children: [_jsx(Lead, { editMode: editMode, className: enter(editMode, 2), children: lead }), _jsx(CtaList, { actions: actions, components: components, labels: labels, locale: locale, size: "md", editMode: editMode, className: enter(editMode, 3) })] })] }), _jsx(Meta, { items: meta, className: cn("flex-row flex-wrap gap-x-6", enter(editMode, 4)) })] }), screenshot || visual ? (_jsx("div", { className: cn("vb-hero-shot mt-[calc(var(--section-y)*0.55)]", enter(editMode, 5, "media")), ...field(editMode, "screenshot"), children: visual ?? _jsx(BlockImage, { ...screenshot, priority: true, sizes: SITE_SIZES.container, components: components, labels: labels }) })) : null] }));
|
|
68
70
|
}
|
|
69
71
|
/* statement ------------------------------------------------------------ */
|
|
70
72
|
function Statement(p) {
|