Heads & heroes
The head every inner page opens with, and the pieces a homepage hero is built from. Each block shows the real component, its props from the code and the demos it is live in.
#uk-inside-header-clean
UkInsideHeader variant="clean"
<Component />The white head every inner page starts with: the breadcrumb right under the navbar, then the head's own lines (a badge, the h1, a lead) as children. No picture and no height of its own: it is as tall as its text, and it ends 40px (--uk-section-padding-small) above what follows.
When to use
Every inner page of a demo that opens on text: about, a list of services, a service, contact. Not on the studio's own pages (they use PageHeader), and not when the page opens on a photo (the classic).
--uk-breadcrumb-top, read from this page's real navbar, not from the grey strip: the studio bar draws its bottom border, so here it is 16px on a phone too.What it does to the lead
A <p> that is a direct child of the head takes the subtext tokens of uk-inside-header.css: max-width: --uk-inside-header-subtext-max-width (32rem, about 50 Hebrew characters a line), font-size 1rem, and 1.325rem from 768px (--uk-inside-header-subtext-size-mobile / -desktop). In clean it sits at the start (margin-inline: 0), where the classic centres it. So the <p> stays plain.
Code
import UkInsideHeader from '@/components/uikit/UkInsideHeader'
<UkInsideHeader
variant="clean"
bgColor="transparent"
breadcrumb={[{ label: 'טיפולים', href: `${ROOT}/treatments` }, { label: title }]}
breadcrumbHomeLabel="בית"
breadcrumbHomeHref={ROOT}
breadcrumbHomeIcon={false}
>
<span className="uk-title-badge">{eyebrow}</span>
<div className="uk-spacer-v-16" />
<h1>{title}</h1>
<div className="uk-spacer-v-16" />
<p>{description}</p>
</UkInsideHeader>A demo writes it once, in its own <Brand>PageHeader (app/demos/spa_salon_31_05/_components/AureaPageHeader.tsx), only to pass its home link (breadcrumbHomeHref={ROOT}, "בית" as a word) and the order of its lines. The gaps, the measure and the sizes stay the component's. A demo with several homepages takes root as a prop instead of a constant (AgencyPageHeader, ConfPageHeader, InteriorPageHeader).
Props
variant"classic" | "clean"default"classic""clean"is the white head. Anything else is the picture header (next block).bgColorstringdefaultvar(--uk-inside-header-bg)The head's background. The default token isvar(--foreground), the classic's dark fallback, so a clean head passes"transparent"(or the page's colour).breadcrumbBreadcrumbItemData[]The trail, home not included ({ label, href? }). With it the container takes.uk-breadcrumb-topand a.uk-breadcrumb-aftersits under the trail. Without it: 32px above the lines, 40px from 768px (pt-8 md:pt-10).breadcrumbSiblingsBreadcrumbItemData[]Pages beside the last crumb: the last crumb becomes a dropdown of them.breadcrumbHomeLabelstringdefault"ראשי"The home crumb's text (UkBreadcrumb's default), shown when the icon is off.breadcrumbHomeHrefstringdefault"/"Where home points (UkBreadcrumb's default). A demo passes its root, so "בית" opens the demo and not the studio.breadcrumbHomeIconbooleandefaulttrueThe house icon for home (UkBreadcrumb's default). The demos passfalseand show the word.containerSizestringdefault""A modifier for the head's own container, e.g."uk-container-large". It must match the page below, or the trail sits off the content's edge.eyebrowstringA line above the children, as<p data-uk-inside-header-eyebrow>: muted on clean, light over the picture on classic. Every classic demo passes it; the clean demos pass auk-title-badgeas a child instead (the blog passeseyebrow).childrenReactNodeThe head's own lines: a badge, the<h1>, a lead<p>, a meta row.classNamestringExtra classes on the<header>.
| Prop | Type | Default | What |
|---|---|---|---|
variant | "classic" | "clean" | "classic" | "clean" is the white head. Anything else is the picture header (next block). |
bgColor | string | var(--uk-inside-header-bg) | The head's background. The default token is var(--foreground), the classic's dark fallback, so a clean head passes "transparent" (or the page's colour). |
breadcrumb | BreadcrumbItemData[] | - | The trail, home not included ({ label, href? }). With it the container takes .uk-breadcrumb-top and a .uk-breadcrumb-after sits under the trail. Without it: 32px above the lines, 40px from 768px (pt-8 md:pt-10). |
breadcrumbSiblings | BreadcrumbItemData[] | - | Pages beside the last crumb: the last crumb becomes a dropdown of them. |
breadcrumbHomeLabel | string | "ראשי" | The home crumb's text (UkBreadcrumb's default), shown when the icon is off. |
breadcrumbHomeHref | string | "/" | Where home points (UkBreadcrumb's default). A demo passes its root, so "בית" opens the demo and not the studio. |
breadcrumbHomeIcon | boolean | true | The house icon for home (UkBreadcrumb's default). The demos pass false and show the word. |
containerSize | string | "" | A modifier for the head's own container, e.g. "uk-container-large". It must match the page below, or the trail sits off the content's edge. |
eyebrow | string | - | A line above the children, as <p data-uk-inside-header-eyebrow>: muted on clean, light over the picture on classic. Every classic demo passes it; the clean demos pass a uk-title-badge as a child instead (the blog passes eyebrow). |
children | ReactNode | - | The head's own lines: a badge, the <h1>, a lead <p>, a meta row. |
className | string | - | Extra classes on the <header>. |
Not read by clean: size, sizeMobile, minHeight.
Rules
- Pass
bgColor="transparent"(or the page's colour): without it the head paints--uk-inside-header-bg, which isvar(--foreground). - Retune the lead in the demo's scope, through the token:
[data-uk-inside-header] { --uk-inside-header-subtext-max-width: 56ch }(the blog, maslul.css). A class on the<p>loses to the component's rule: the coach'smax-w-[56ch]still measures 512px. - The
<h1>and the<p>stay plain (CLAUDE.md 1.3). A brand changes their size through--h1-sizeand the subtext tokens in its scope.
Live in
- Spa ·
AureaPageHeader - Agency ·
AgencyPageHeader(its case page is the classiccover) - Blog ·
MaslulPageHeader, measure 56ch - Clean energy ·
VerdisPageHeader - Coach ·
CoachPageHeader - Conference ·
ConfPageHeader - Interior studio ·
InteriorPageHeader - Surf ·
WaveLinePageHeader, its trail 8px under the bar - Wedding dresses ·
AtelierPageHeader
#uk-inside-header-classic
UkInsideHeader variant="classic"
<Component />The picture header, and the default variant: a photo or a video edge to edge under a black overlay, the head centred in white over it. The breadcrumb is not on the picture: it gets its own row under it, in the page's container, 16px above and below.
When to use
A page that opens on a photo: a hotel's rooms, a clinic, a restaurant's menu. On an inner page keep it short (size="sm", 50vh): the content is below it. Not for a text page (clean is lighter), and not as a homepage hero (Heroes).
מגדל המשרדים
שתים עשרה קומות של זכוכית ופלדה, מהשרטוט הראשון ועד מסירת המפתח.
size="sm". Under the picture, the breadcrumb's row.Under a floating navbar
Under a sticky bar (in the flow) the section's own padding centres the text. When the bar floats over the picture, the page wraps the header in [data-navbar-transparent] and the header adds the bar's height on top (padding-top: var(--uk-navbar-h)), so the text centres in the visible part (the barber).
Code
<UkInsideHeader
imageSrc={hero.src}
size="sm"
eyebrow={eyebrow}
breadcrumb={crumbs}
breadcrumbHomeLabel="בית"
breadcrumbHomeHref={ROOT}
breadcrumbHomeIcon={false}
containerSize="uk-container-large"
>
<h1>{title}</h1>
<div className="uk-spacer-v-16" />
<p>{description}</p>
</UkInsideHeader>Props
imageSrcstringThe background photo,object-fit: cover,loading="eager"(it is the LCP).imageAltstringdefault""Its alt. The photo is a background (aria-hidden), so empty is the norm.imagePositionstringdefaultcenterIts object-position, e.g."center 30%"to keep the faces in a photo of people.videoSrcstringA background video (BgVideo, below) instead of the photo. With both set, the video wins.videoControlsbooleandefaulttrueThe video's play / pause button.overlayOpacitynumberdefault0.65The black layer over the picture, drawn only with a photo or a video. Withcoverit is mixed from--uk-inside-header-cover-bg, not from black.size"sm" | "md" | "lg" | "full"default"md"Min height: 50vh, 70vh, 90vh, 100svh. At every width, unlesssizeMobileis set.sizeMobilesame as sizedefaultsizeThe min height under 768px.minHeightstringAny CSS length. Overrides both sizes.boxedbooleandefaultfalseA rounded card inside the page's container instead of edge to edge, 16px under the navbar (--uk-inside-header-boxed-top). It crops less of the people in a photo. Ignored withcover.coverbooleandefaultfalseFull screen under the navbar (calc(100svh - var(--uk-navbar-h))): on scroll the content sinks and the next section rises over it. The curtain is--uk-inside-header-cover-bg(default#111).scrollArrowbooleandefaultfalseThe "more below" chevrons (UkScrollToMainArrow) at the foot; a click scrolls past the header.
| Prop | Type | Default | What |
|---|---|---|---|
imageSrc | string | - | The background photo, object-fit: cover, loading="eager" (it is the LCP). |
imageAlt | string | "" | Its alt. The photo is a background (aria-hidden), so empty is the norm. |
imagePosition | string | center | Its object-position, e.g. "center 30%" to keep the faces in a photo of people. |
videoSrc | string | - | A background video (BgVideo, below) instead of the photo. With both set, the video wins. |
videoControls | boolean | true | The video's play / pause button. |
overlayOpacity | number | 0.65 | The black layer over the picture, drawn only with a photo or a video. With cover it is mixed from --uk-inside-header-cover-bg, not from black. |
size | "sm" | "md" | "lg" | "full" | "md" | Min height: 50vh, 70vh, 90vh, 100svh. At every width, unless sizeMobile is set. |
sizeMobile | same as size | size | The min height under 768px. |
minHeight | string | - | Any CSS length. Overrides both sizes. |
boxed | boolean | false | A rounded card inside the page's container instead of edge to edge, 16px under the navbar (--uk-inside-header-boxed-top). It crops less of the people in a photo. Ignored with cover. |
cover | boolean | false | Full screen under the navbar (calc(100svh - var(--uk-navbar-h))): on scroll the content sinks and the next section rises over it. The curtain is --uk-inside-header-cover-bg (default #111). |
scrollArrow | boolean | false | The "more below" chevrons (UkScrollToMainArrow) at the foot; a click scrolls past the header. |
Plus variant, bgColor (with no picture, the band's colour; default var(--uk-inside-header-bg) = var(--foreground)), the breadcrumb props, containerSize, eyebrow, children and className, as in the clean table.
Rules
- The
<h1>and the<p>stay plain: the classic's CSS colours them for the overlay (--uk-inside-header-title-color-on-media,--uk-inside-header-subtext-color-on-media). Notext-white. - Nothing between the trail's row and the next section: the row has 16px under it and the section after it starts at 16px (8 under 640px). A spacer there doubles the gap.
- A photo of people:
boxed, orimagePosition="center 30%". A full-width 50vh band is a very wide frame and crops the heads off.
Live in
- Barber ·
sm, under a transparent navbar - Cosmetics ·
sm - Dentist ·
sm - Lawyer ·
sm - Meridian ·
sm - Real estate ·
sm - Lumière ·
lg,mdon a phone - Gym ·
boxed - Photographer ·
boxed - Photographer, an album ·
cover - Catania ·
cover+scrollArrow - Catania, the menu ·
scrollArrow - Agency, a case page ·
cover
#page-header
PageHeader
<Component />The studio's inner-page head: the breadcrumb, an optional badge, the h1 (part of it coloured with titleSpan) and a subtitle, all centred. One uk-section uk-section-page-header: the token above the trail (plus --uk-topbar-h), 60px under the head, 40px under 768px. On the studio its h1 is 3.5rem ([data-studio] .uk-section-page-header in app/globals.css).
When to use
The studio's own pages. Not in a demo: it has no prop for the home link, so its trail always starts at the studio's /. A demo uses the clean head.
קידום אתרים בגוגל
עמודים שנמצאים כשמחפשים אתכם בגוגל, ופניות שמגיעות מהם לוואטסאפ.
Code
import PageHeader from '@/components/uikit/PageHeader/PageHeader'
<PageHeader
breadcrumb={[{ label: 'שירותים', href: '/services' }, { label: 'קידום אתרים' }]}
badge="שירותים"
title="קידום אתרים בגוגל"
titleSpan="בגוגל"
subtitle="עמודים שנמצאים כשמחפשים אתכם בגוגל, ופניות שמגיעות מהם לוואטסאפ."
/>No index.ts and not in the uikit barrel: import it from its file.
Props
titlestringrequiredThe h1.*asterisks*in it colour that part (highlightTitle).titleSpanstringA substring oftitleto colour withuk-title-span, so the title stays a plain string for crumbs and SEO.badgestringAuk-title-badge16px above the h1.subtitlestringA grey line 8px under the h1,max-w-xl, centred.breadcrumbBreadcrumbItemData[]The trail, home not included. Home is always the studio's/with the house icon: there is no prop for it.breadcrumbSiblingsBreadcrumbItemData[]The last crumb becomes a dropdown of these.size"small" | "medium" | "large"Addsuk-section-<size>, but the head's own padding (.uk-section.uk-section-page-header, two classes) outranks it: today it changes nothing, and no page passes it.childrenReactNodeAnything under the head (buttons, a search), 24px under it.
| Prop | Type | Default | What |
|---|---|---|---|
title | string | required | The h1. *asterisks* in it colour that part (highlightTitle). |
titleSpan | string | - | A substring of title to colour with uk-title-span, so the title stays a plain string for crumbs and SEO. |
badge | string | - | A uk-title-badge 16px above the h1. |
subtitle | string | - | A grey line 8px under the h1, max-w-xl, centred. |
breadcrumb | BreadcrumbItemData[] | - | The trail, home not included. Home is always the studio's / with the house icon: there is no prop for it. |
breadcrumbSiblings | BreadcrumbItemData[] | - | The last crumb becomes a dropdown of these. |
size | "small" | "medium" | "large" | - | Adds uk-section-<size>, but the head's own padding (.uk-section.uk-section-page-header, two classes) outranks it: today it changes nothing, and no page passes it. |
children | ReactNode | - | Anything under the head (buttons, a search), 24px under it. |
Rules
- Only on the studio. In a demo the trail starts at the demo's root: UkInsideHeader
cleanwithbreadcrumbHomeHref. - To change its padding, change
.uk-section.uk-section-page-headerin styles/uk-components.css, the one place. Thesizeprop does not reach it (see the table). - Buttons or a search under the head go in
children(24px under it), not in a section after it.
Live in
- About
- Contact
- Services · and each service page, e.g.
/services/seo - Works
- Webflow · and
/webflow/features - Build a website · and every
/build-website/<industry> - The guide · and every chapter
- Site type page · every type page (
TypePageTemplate) - Accessibility
- Privacy
Every head above puts the trail at the same distances, and none of them types a spacer for it: two tokens on :root (--uk-breadcrumb-top, --uk-breadcrumb-after), their classes and the picture header's own row carry the gaps, in the breadcrumb's CSS and the header's. Change a gap there and every demo follows. The trail itself: UkBreadcrumb, on UIKit Components.
- navbar (in the flow) → trail: 10 / 16 (16 / 16 under a bar that draws its bottom border)
--uk-breadcrumb-topon:root, read by.uk-breadcrumb-top(UkInsideHeader clean, a head you build) and by.uk-section-page-header(PageHeader, plus--uk-topbar-h). Phone: under 768px. - trail → the head (badge / h1): 32 / 40
--uk-breadcrumb-afteron:root(32 under 768px), read by.uk-breadcrumb-after, an empty div (UkInsideHeader clean, PageHeader, a head you build). - picture header → trail: 16 / 16The trail's own row,
[data-uk-inside-header-breadcrumb]: 16px above and below. - trail row → the first section: 24 / 32The row's 16 + the next
.uk-sectionstarting at 8 / 16 (phone: under 640px). In front of apt-0section,.uk-breadcrumb-spacer-bottom(8 / 16) does it.
| From → to | Phone / desktop (px) | Carried by |
|---|---|---|
| navbar (in the flow) → trail | 10 / 16 (16 / 16 under a bar that draws its bottom border) | --uk-breadcrumb-top on :root, read by .uk-breadcrumb-top (UkInsideHeader clean, a head you build) and by .uk-section-page-header (PageHeader, plus --uk-topbar-h). Phone: under 768px. |
| trail → the head (badge / h1) | 32 / 40 | --uk-breadcrumb-after on :root (32 under 768px), read by .uk-breadcrumb-after, an empty div (UkInsideHeader clean, PageHeader, a head you build). |
| picture header → trail | 16 / 16 | The trail's own row, [data-uk-inside-header-breadcrumb]: 16px above and below. |
| trail row → the first section | 24 / 32 | The row's 16 + the next .uk-section starting at 8 / 16 (phone: under 640px). In front of a pt-0 section, .uk-breadcrumb-spacer-bottom (8 / 16) does it. |
Why 10 on a phone: the navbar's own bottom padding reads as part of the gap. A bar that draws its bottom border (UkNavbar bottomBorder) ends at its line, and 10 from a line reads glued, so there the phone takes 16 too: :root:has(nav[uk_navbar].hasBottomBorder:not(.isFloating)), by itself.
Live: the pink bands are the real paddings of the real classes, painted. Resize the window to see the phone values.
A head you build yourself
<header>
{/* the trail's own row: no uk-section around it */}
<div className="uk-container uk-breadcrumb-top"> {/* navbar → trail: the token */}
<UkBreadcrumb items={crumbs} />
<div className="uk-breadcrumb-after" /> {/* trail → head: 32 / 40, the token */}
<h1>{title}</h1>
</div>
</header>A demo retunes the token in its own scope, never in the arsenal. One does today: the surf demo, .uk-breadcrumb-top { --uk-breadcrumb-top: 8px; } in app/demos/surf_sport_01_06/_styles/waveline.css, 8px at every width.
Rules
- The trail never sits inside a
uk-section: its padding (40 / 100) pushes it away from the navbar and the head, and it reads as part of whatever block it landed in. It is a row of the page. - Count every box between the trail and its neighbour: two paddings meet there without anyone typing a big number (the row's 16 plus a section's top).
npm run rhythmflags both directions,CRUMBandCRUMBTOP. - The rule in full:
.claude/skills/vertical-rhythm/SKILL.md, rule 3b (a file in the repo, not a web page).
#heroes
Heroes
The pieces in components/uikit a homepage hero is built from. Each is one layer, not a finished hero: the copy, the buttons and the layout around them stay the demo's. Finished layouts to copy are at the bottom, in Hero examples.
#uk-cover-slideshow
UkCoverSlideshow
<Component />A full-bleed background of crossfading photos (NetflixSlideShow underneath) in three layers: the photos, an overlay, your content on top. It knows nothing about the content. For a homepage whose story is several photos (a photographer, a pub); not for one photo, and not on an inner page.
height="420px", mobileAspectRatio="4/5", coverHero={false}.Code
import UkCoverSlideshow from '@/components/uikit/UkCoverSlideshow'
<UkCoverSlideshow images={slides} height="100svh" mobileAspectRatio="4/5">
<div className="uk-container">
<h1>{title}</h1>
</div>
</UkCoverSlideshow>Props
imagesstring[]requiredThe photos, in order. The first loads eager (the LCP frame): lead with the strongest.speednumberdefault5000The crossfade interval, in ms.overlaystring | falsedefault"rgb(0 0 0 / 0.6)"A CSSbackground(solid or gradient) between the photos and the content.falsefor none.heightstringdefault"100svh"The frame's fixed height.mobileAspectRatiostringUnder 768px an aspect-ratio (e.g."4/5") replacesheight, so a tall band is not a giant crop on a phone.coverHerobooleandefaulttrueThe cover-hero scroll effect: the photos sink and fade, the overlay and the content stay.contentClassNamestringAdded to the content layer, a centred column by default (flex flex-col items-center justify-center text-center px-4).interactivebooleandefaultfalseLets drag and swipe reach the photos. For a band with no content on top.dotsbooleandefaultfalseDots pagination, bottom centre (clickable withinteractive).classNamestringOn the frame.childrenReactNodeThe content layer.
| Prop | Type | Default | What |
|---|---|---|---|
images | string[] | required | The photos, in order. The first loads eager (the LCP frame): lead with the strongest. |
speed | number | 5000 | The crossfade interval, in ms. |
overlay | string | false | "rgb(0 0 0 / 0.6)" | A CSS background (solid or gradient) between the photos and the content. false for none. |
height | string | "100svh" | The frame's fixed height. |
mobileAspectRatio | string | - | Under 768px an aspect-ratio (e.g. "4/5") replaces height, so a tall band is not a giant crop on a phone. |
coverHero | boolean | true | The cover-hero scroll effect: the photos sink and fade, the overlay and the content stay. |
contentClassName | string | - | Added to the content layer, a centred column by default (flex flex-col items-center justify-center text-center px-4). |
interactive | boolean | false | Lets drag and swipe reach the photos. For a band with no content on top. |
dots | boolean | false | Dots pagination, bottom centre (clickable with interactive). |
className | string | - | On the frame. |
children | ReactNode | - | The content layer. |
Rules
- Text colour is yours: the global
pis grey (--muted-foreground). Set the tokens on the content class for a dark overlay (this preview does), not a colour class on each line. - Depth on scroll (with
coverHero):data-movement="-60"on elements inchildrenmoves them against the sink. See Cover Hero.
Live in
- Photographer, home ·
100svh, a crop per slide - Pub, home ·
92svh,4/5on a phone
#uk-shader-backdrop
UkShaderBackdrop
<Component />A moving WebGL background in plain WebGL (no three.js, about 3KB). For a text hero on a light ground that needs life without a photo. Not behind a photo. Its default colours are light (pass colors for a brand ground).
variant="flow", its default colours.variant="dots", its default colours.Code
import UkShaderBackdrop from '@/components/uikit/UkShaderBackdrop'
<section className="uk-section relative overflow-hidden">
<UkShaderBackdrop variant="flow" intensity={0.8} />
<div className="uk-container relative z-10">…</div>
</section>Props
variant"flow" | "dots"default"flow"flow: a soft liquid gradient, light in its default colours. dots: a floor of points in perspective rippling toward the horizon, in the lower part of the box.colorsstring[] (hex)flow takes the background + 3 colours (default#ffffff #cfe0ff #b4f0f7 #dcd6ff), dots the first 2 (default#2563eb #22c3dd).intensitynumber, 0..1default1flow only. Lower is paler and hazier, a mist instead of veins.classNamestringdefault""On the<canvas>.
| Prop | Type | Default | What |
|---|---|---|---|
variant | "flow" | "dots" | "flow" | flow: a soft liquid gradient, light in its default colours. dots: a floor of points in perspective rippling toward the horizon, in the lower part of the box. |
colors | string[] (hex) | - | flow takes the background + 3 colours (default #ffffff #cfe0ff #b4f0f7 #dcd6ff), dots the first 2 (default #2563eb #22c3dd). |
intensity | number, 0..1 | 1 | flow only. Lower is paler and hazier, a mist instead of veins. |
className | string | "" | On the <canvas>. |
Rules
- It fills its positioned parent (
position: absolute; inset: 0; z-index: 0, no pointer events): the parent needsrelative, the content above itrelative z-10. - It stops off screen and in a hidden tab, draws one still frame under
prefers-reduced-motion, and draws nothing without WebGL: the section's own background shows, so give it one. - Not in the uikit barrel: import it from its folder, like every component.
Live in
- Studio home ·
flow, softened colours - Agency, home ·
flow,intensity={0.8} - AI conference, home ·
flow,intensity={0.7} - Hero lab
#bg-video
BgVideo
<Component />A full-cover background video, muted, looped, autoplaying and playsInline, with an optional play / pause button. Drop it inside a position: relative box. For a homepage whose story moves (the surf school, the hotel); UkInsideHeader uses it for videoSrc. No live preview here: a hero video is megabytes.
Code
import BgVideo from '@/components/uikit/BgVideo'
<section className="relative min-h-svh overflow-hidden">
<BgVideo src="/demos/<brand>/hero.mp4" controls={false} />
<div className="relative z-10">…</div>
</section>Props
srcstringrequiredThe video file.posterstringdefault""An image while the video loads.controlsbooleandefaulttrueA round play / pause button, 20px from the bottom and from the inline end.
| Prop | Type | Default | What |
|---|---|---|---|
src | string | required | The video file. |
poster | string | "" | An image while the video loads. |
controls | boolean | true | A round play / pause button, 20px from the bottom and from the inline end. |
Rules
- The video layer is
z-index: 0with no pointer events: the content needsrelativeand a z-index. The button sits atz-index: 20. - The overlay is yours: BgVideo draws none (UkInsideHeader adds its own, the hotel its
lm-hero-overlay). - Playwright's own Chromium has no H.264: an .mp4 never plays there and a screenshot shows a black box (scripts/_pw.cjs).
Live in
- Surf, home · no button, no poster
- Lumière, home · no button, its own overlay
#uk-stage
UkStage
<Component />A cut-out portrait on a flat block, with HTML cards floating over it. The component owns the mechanism (the box, the picture, card slots placed in % of the picture, a card font size that shrinks with the screen, the slow float); the card's look is the demo's class. For a hero built around one person: a dentist, a guide, a coach.

Code
import UkStage, { UkStageCard } from '@/components/uikit/UkStage'
<UkStage src={img.src} alt="…" fetchPriority="high" className="mx-auto max-w-[460px]">
<UkStageCard at={{ left: '6%', top: '32%' }} delay={-1.5} className="my-stage__card">
<strong>18 שנות ניסיון</strong>
</UkStageCard>
</UkStage>Props: UkStage
srcstringrequiredThe cut-out picture: one image with alpha (scripts/cutout.cjs).altstringrequiredIts alt text.fetchPriority"high" | "low" | "auto""high"in a hero: it is the LCP candidate.classNamestringdefault""On the box: size and place it here (mx-auto max-w-[460px]).childrenReactNodeThe cards.
| Prop | Type | Default | What |
|---|---|---|---|
src | string | required | The cut-out picture: one image with alpha (scripts/cutout.cjs). |
alt | string | required | Its alt text. |
fetchPriority | "high" | "low" | "auto" | - | "high" in a hero: it is the LCP candidate. |
className | string | "" | On the box: size and place it here (mx-auto max-w-[460px]). |
children | ReactNode | - | The cards. |
Props: UkStageCard
at{ left, right, top, bottom }requiredWhere on the picture, in % of its box. Physical sides: the picture is not mirrored in RTL.delaynumberdefault0The float's phase, in seconds. Negative starts mid-cycle, so the cards drift out of sync.decorativebooleandefaulttruearia-hidden: a card repeats a fact from the copy.falsewhen it says something new.classNamestringdefault""The card's look (background, radius, shadow, padding): the demo's class.childrenReactNoderequiredThe card's content.
| Prop | Type | Default | What |
|---|---|---|---|
at | { left, right, top, bottom } | required | Where on the picture, in % of its box. Physical sides: the picture is not mirrored in RTL. |
delay | number | 0 | The float's phase, in seconds. Negative starts mid-cycle, so the cards drift out of sync. |
decorative | boolean | true | aria-hidden: a card repeats a fact from the copy. false when it says something new. |
className | string | "" | The card's look (background, radius, shadow, padding): the demo's class. |
children | ReactNode | required | The card's content. |
Tokens: --uk-stage-card-fs (default clamp(0.72rem, 0.55rem + 0.55vw, 0.95rem)), --uk-stage-float-y (-7px), --uk-stage-float-duration (7s). The float stops under prefers-reduced-motion.
Rules
- The picture is one image with alpha (
scripts/cutout.cjs), so it sits on any section background. - Positions are physical (
left/right): the picture is not mirrored in RTL, so the empty part of the block is where the image has it. - A card repeats a fact from the copy, so it is
aria-hiddenby default;decorative={false}when it says something new.
#hero-examples
Hero examples
Finished hero layouts: markup and scoped CSS in one block, the copy from headers-data.ts, so the layouts compare and not the text. Copy a block, swap the content.
Header 01
כותרת גיבור באורך בינוני שיושבת כאן
זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.
Header 02
כותרת גיבור באורך בינוני שיושבת כאן
זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.
ModernHeader 03
כותרת גיבור באורך בינוני שיושבת כאן
זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.
