דילוג לתוכן הראשי
Page heads

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).

טיפולי גוף

עיסוי רקמות עמוק

שעה של לחץ איטי ועמוק, למי שהכתפיים והגב התחתון לא משתחררים. מתחילים בשיחה קצרה ומסיימים במגבת חמה.

The real component. The gap above the trail is --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).
  • bgColorstringdefault 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).
  • breadcrumbBreadcrumbItemData[]
    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).
  • 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.
  • breadcrumbHomeIconbooleandefault true
    The house icon for home (UkBreadcrumb's default). The demos pass false and 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.
  • eyebrowstring
    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).
  • childrenReactNode
    The head's own lines: a badge, the <h1>, a lead <p>, a meta row.
  • classNamestring
    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 is var(--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's max-w-[56ch] still measures 512px.
  • The <h1> and the <p> stay plain (CLAUDE.md 1.3). A brand changes their size through --h1-size and the subtext tokens in its scope.

Live in

#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).

מגדל המשרדים

שתים עשרה קומות של זכוכית ופלדה, מהשרטוט הראשון ועד מסירת המפתח.

The real component, 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

  • imageSrcstring
    The 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.
  • imagePositionstringdefault center
    Its object-position, e.g. "center 30%" to keep the faces in a photo of people.
  • videoSrcstring
    A background video (BgVideo, below) instead of the photo. With both set, the video wins.
  • videoControlsbooleandefault true
    The video's play / pause button.
  • overlayOpacitynumberdefault 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"default "md"
    Min height: 50vh, 70vh, 90vh, 100svh. At every width, unless sizeMobile is set.
  • sizeMobilesame as sizedefault size
    The min height under 768px.
  • minHeightstring
    Any CSS length. Overrides both sizes.
  • boxedbooleandefault 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.
  • coverbooleandefault 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).
  • scrollArrowbooleandefault 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). No text-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, or imagePosition="center 30%". A full-width 50vh band is a very wide frame and crops the heads off.

Live in

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.

שירותים

קידום אתרים בגוגל

עמודים שנמצאים כשמחפשים אתכם בגוגל, ופניות שמגיעות מהם לוואטסאפ.

The real component. Its home crumb is the studio's / with the house icon, as on every studio page.

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

  • titlestringrequired
    The h1. *asterisks* in it colour that part (highlightTitle).
  • titleSpanstring
    A substring of title to colour with uk-title-span, so the title stays a plain string for crumbs and SEO.
  • badgestring
    A uk-title-badge 16px above the h1.
  • subtitlestring
    A 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"
    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.
  • childrenReactNode
    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 clean with breadcrumbHomeHref.
  • To change its padding, change .uk-section.uk-section-page-header in styles/uk-components.css, the one place. The size prop 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

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-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.

ראש הדף
Under the navbar: the token, then .uk-breadcrumb-after.
הסקשן הראשון
Under a picture: the row's 16 / 16, then the next section's top.

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 rhythm flags both directions, CRUMB and CRUMBTOP.
  • 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.

התמונות מתחלפות. השכבה הכהה והתוכן נשארים במקום.

The real component, 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[]required
    The photos, in order. The first loads eager (the LCP frame): lead with the strongest.
  • speednumberdefault 5000
    The crossfade interval, in ms.
  • overlaystring | falsedefault "rgb(0 0 0 / 0.6)"
    A CSS background (solid or gradient) between the photos and the content. false for none.
  • heightstringdefault "100svh"
    The frame's fixed height.
  • mobileAspectRatiostring
    Under 768px an aspect-ratio (e.g. "4/5") replaces height, so a tall band is not a giant crop on a phone.
  • coverHerobooleandefault true
    The cover-hero scroll effect: the photos sink and fade, the overlay and the content stay.
  • contentClassNamestring
    Added to the content layer, a centred column by default (flex flex-col items-center justify-center text-center px-4).
  • interactivebooleandefault false
    Lets drag and swipe reach the photos. For a band with no content on top.
  • dotsbooleandefault false
    Dots pagination, bottom centre (clickable with interactive).
  • classNamestring
    On the frame.
  • childrenReactNode
    The content layer.

Rules

  • Text colour is yours: the global p is 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 in children moves them against the sink. See Cover Hero.

Live in

#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).

The real component, variant="flow", its default colours.
The real component, 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..1default 1
    flow only. Lower is paler and hazier, a mist instead of veins.
  • classNamestringdefault ""
    On the <canvas>.

Rules

  • It fills its positioned parent (position: absolute; inset: 0; z-index: 0, no pointer events): the parent needs relative, the content above it relative 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

#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

  • srcstringrequired
    The video file.
  • posterstringdefault ""
    An image while the video loads.
  • controlsbooleandefault true
    A round play / pause button, 20px from the bottom and from the inline end.

Rules

  • The video layer is z-index: 0 with no pointer events: the content needs relative and a z-index. The button sits at z-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

#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.

מטיילת בדרכה לאקרופוליס
The real component on the Athens demo's picture; the cards' look is this page's class. Two cards on a phone.

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

  • srcstringrequired
    The cut-out picture: one image with alpha (scripts/cutout.cjs).
  • altstringrequired
    Its 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]).
  • childrenReactNode
    The cards.

Props: UkStageCard

  • at{ left, right, top, bottom }required
    Where on the picture, in % of its box. Physical sides: the picture is not mirrored in RTL.
  • delaynumberdefault 0
    The float's phase, in seconds. Negative starts mid-cycle, so the cards drift out of sync.
  • decorativebooleandefault true
    aria-hidden: a card repeats a fact from the copy. false when it says something new.
  • classNamestringdefault ""
    The card's look (background, radius, shadow, padding): the demo's class.
  • childrenReactNoderequired
    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-hidden by 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

תבנית UIKit

כותרת גיבור באורך בינוני שיושבת כאן

זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.

Header 02

תבנית UIKit

כותרת גיבור באורך בינוני שיושבת כאן

זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.

Modern

Header 03

תבנית UIKit

כותרת גיבור באורך בינוני שיושבת כאן

זוהי פסקת משנה באורך מייצג. היא מסבירה את ההצעה בכמה משפטים, נותנת הקשר וטון, ומאפשרת לראות איך הטקסט יושב מתחת לכותרת לפני שמכניסים תוכן אמיתי. את כל המלל אפשר להחליף ממקום אחד בקובץ ה-data.