save
This commit is contained in:
@@ -1,13 +1,6 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
import buildingAvif from "@/assets/gebos/building.avif"
|
||||
import buildingWebp from "@/assets/gebos/building.webp"
|
||||
import consumptionRing from "@/assets/gebos/consumption-ring.svg"
|
||||
import smokeAlarmAvif from "@/assets/gebos/smoke-alarm.avif"
|
||||
import smokeAlarmWebp from "@/assets/gebos/smoke-alarm.webp"
|
||||
import tealBars from "@/assets/gebos/teal-bars.svg"
|
||||
import uviBadge from "@/assetAs/gebos/uvi-badge.svg"
|
||||
import { AssetRender, renders } from ""
|
||||
import { AssetRender, renders } from "@/components/gebos/site-assets"
|
||||
|
||||
/**
|
||||
* Homepage hero composition from the GebOS visual asset kit:
|
||||
@@ -26,41 +19,15 @@ function HeroArt({ className }: { className?: string }) {
|
||||
aria-label="GebOS Produktvisualisierung"
|
||||
role="img"
|
||||
>
|
||||
<img
|
||||
src={tealBars}
|
||||
alt=""
|
||||
className="absolute top-[12%] -right-[5%] z-0 w-[46%]"
|
||||
/>
|
||||
<AssetRender
|
||||
render={renders.barChart}
|
||||
alt=""
|
||||
className="absolute top-[52%] left-[10%] w-[16%] scale-200"
|
||||
className="absolute bottom-[0%] right-[10%] w-[16%] scale-200 z-10"
|
||||
/>
|
||||
<picture>
|
||||
<source srcSet={buildingAvif} type="image/avif" />
|
||||
<img
|
||||
src={buildingWebp}
|
||||
alt="Modernes Mehrfamilienhaus"
|
||||
className="absolute top-[2%] left-[2%] z-1 h-[90%] w-[96%] object-contain mix-blend-multiply"
|
||||
/>
|
||||
</picture>
|
||||
<picture>
|
||||
<source srcSet={smokeAlarmAvif} type="image/avif" />
|
||||
<img
|
||||
src={smokeAlarmWebp}
|
||||
alt="Rauchwarnmelder"
|
||||
className="absolute top-[4%] -left-[2%] z-3 w-[22%] rounded-full mix-blend-multiply"
|
||||
/>
|
||||
</picture>
|
||||
<img
|
||||
src={uviBadge}
|
||||
alt=""
|
||||
className="absolute bottom-[7%] left-[2%] z-4 w-[15%]"
|
||||
/>
|
||||
<img
|
||||
src={consumptionRing}
|
||||
alt=""
|
||||
className="absolute -bottom-[2%] -right-[2%] z-5 w-[46%]"
|
||||
<AssetRender
|
||||
render={renders.buildingAlpha}
|
||||
className="absolute top-[2%] left-[2%] z-1 h-[90%] w-[96%]"
|
||||
imgClassName="h-full w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -34,13 +34,25 @@ import glassBadge2Avif from "@/assets/gebos/glass-badge-2.avif"
|
||||
import glassBadge2Webp from "@/assets/gebos/glass-badge-2.webp"
|
||||
import glassBadge3Avif from "@/assets/gebos/glass-badge-3.avif"
|
||||
import glassBadge3Webp from "@/assets/gebos/glass-badge-3.webp"
|
||||
import glassBadge4Avif from "@/assets/gebos/glass-badge-4.avif"
|
||||
import glassBadge4Webp from "@/assets/gebos/glass-badge-4.webp"
|
||||
import glassBadge5Avif from "@/assets/gebos/glass-badge-5.avif"
|
||||
import glassBadge5Webp from "@/assets/gebos/glass-badge-5.webp"
|
||||
import glassBadge6Avif from "@/assets/gebos/glass-badge-6.avif"
|
||||
import glassBadge6Webp from "@/assets/gebos/glass-badge-6.webp"
|
||||
import glassCubes from "@/assets/gebos/glass-cubes.svg"
|
||||
import glassCubesStackAvif from "@/assets/gebos/glass-cubes-stack.avif"
|
||||
import glassCubesStackWebp from "@/assets/gebos/glass-cubes-stack.webp"
|
||||
import glassDonutShadowAvif from "@/assets/gebos/glass-donut-shadow.avif"
|
||||
import glassDonutShadowWebp from "@/assets/gebos/glass-donut-shadow.webp"
|
||||
import glassDonutAvif from "@/assets/gebos/glass-donut.avif"
|
||||
import glassDonutWebp from "@/assets/gebos/glass-donut.webp"
|
||||
import heatMeterAvif from "@/assets/gebos/heat-meter.avif"
|
||||
import heatMeterWebp from "@/assets/gebos/heat-meter.webp"
|
||||
import houseSmallIsoAvif from "@/assets/gebos/house-small-isometric.avif"
|
||||
import houseSmallIsoWebp from "@/assets/gebos/house-small-isometric.webp"
|
||||
import housesSmallIsoAvif from "@/assets/gebos/houses-small-isometric.avif"
|
||||
import housesSmallIsoWebp from "@/assets/gebos/houses-small-isometric.webp"
|
||||
import iconBilling from "@/assets/gebos/icon-billing.svg"
|
||||
import iconPortal from "@/assets/gebos/icon-portal.svg"
|
||||
import iconRadio from "@/assets/gebos/icon-radio.svg"
|
||||
@@ -61,8 +73,12 @@ import shippingBoxClosedAvif from "@/assets/gebos/shipping-box-closed.avif"
|
||||
import shippingBoxClosedWebp from "@/assets/gebos/shipping-box-closed.webp"
|
||||
import shippingBoxMetersAvif from "@/assets/gebos/shipping-box-meters.avif"
|
||||
import shippingBoxMetersWebp from "@/assets/gebos/shipping-box-meters.webp"
|
||||
import smokeAlarmAlphaAvif from "@/assets/gebos/smoke-alarm-alpha.avif"
|
||||
import smokeAlarmAlphaWebp from "@/assets/gebos/smoke-alarm-alpha.webp"
|
||||
import smokeAlarmAvif from "@/assets/gebos/smoke-alarm.avif"
|
||||
import smokeAlarmWebp from "@/assets/gebos/smoke-alarm.webp"
|
||||
import sprinterAvif from "@/assets/gebos/sprinter.avif"
|
||||
import sprinterWebp from "@/assets/gebos/sprinter.webp"
|
||||
import tealBars from "@/assets/gebos/teal-bars.svg"
|
||||
import technicianPipeAvif from "@/assets/gebos/technician-pipe.avif"
|
||||
import technicianPipeWebp from "@/assets/gebos/technician-pipe.webp"
|
||||
@@ -165,6 +181,13 @@ const renders = {
|
||||
alt: "Modernes Mehrfamilienhaus",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** same subject as `smokeAlarm`, but with genuine alpha – safe on any surface */
|
||||
smokeAlarmAlpha: {
|
||||
avif: smokeAlarmAlphaAvif,
|
||||
webp: smokeAlarmAlphaWebp,
|
||||
alt: "Rauchwarnmelder",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** isometric corner view of the same building type, genuine alpha */
|
||||
buildingIso: {
|
||||
avif: buildingIsoAvif,
|
||||
@@ -179,6 +202,13 @@ const renders = {
|
||||
alt: "Einfamilienhaus, isometrisch",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** cluster of small apartment blocks, isometric, genuine alpha */
|
||||
housesSmallIso: {
|
||||
avif: housesSmallIsoAvif,
|
||||
webp: housesSmallIsoWebp,
|
||||
alt: "Mehrere Mehrfamilienhäuser, isometrisch",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** full hero scene: building, gateway, consumption bars & ring on a glass platform */
|
||||
fullHeroSection: {
|
||||
avif: fullHeroSectionAvif,
|
||||
@@ -211,6 +241,18 @@ const renders = {
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
glassDonut: {
|
||||
avif: glassDonutAvif,
|
||||
webp: glassDonutWebp,
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
glassDonutShadow: {
|
||||
avif: glassDonutShadowAvif,
|
||||
webp: glassDonutShadowWebp,
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** glossy numbered badge "1" – process-step numeral, genuine alpha */
|
||||
glassBadge1: {
|
||||
avif: glassBadge1Avif,
|
||||
@@ -232,6 +274,27 @@ const renders = {
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** glossy numbered badge "4" – process-step numeral, genuine alpha */
|
||||
glassBadge4: {
|
||||
avif: glassBadge4Avif,
|
||||
webp: glassBadge4Webp,
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** glossy numbered badge "5" – process-step numeral, genuine alpha */
|
||||
glassBadge5: {
|
||||
avif: glassBadge5Avif,
|
||||
webp: glassBadge5Webp,
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
/** glossy numbered badge "6" – process-step numeral, genuine alpha */
|
||||
glassBadge6: {
|
||||
avif: glassBadge6Avif,
|
||||
webp: glassBadge6Webp,
|
||||
alt: "",
|
||||
matte: "alpha",
|
||||
},
|
||||
cubesRound: {
|
||||
avif: cubesRoundAvif,
|
||||
webp: cubesRoundWebp,
|
||||
@@ -262,6 +325,12 @@ const renders = {
|
||||
alt: "Fußbodenheizungsrohre",
|
||||
matte: "alpha",
|
||||
},
|
||||
sprinter: {
|
||||
avif: sprinterAvif,
|
||||
webp: sprinterWebp,
|
||||
alt: "Servicefahrzeug",
|
||||
matte: "alpha",
|
||||
},
|
||||
workerTablet: {
|
||||
avif: workerTabletAvif,
|
||||
webp: workerTabletWebp,
|
||||
|
||||
@@ -31,14 +31,16 @@ type NavItem = {
|
||||
|
||||
/** Source of truth for nav order – desktop pill, dropdown and mobile sheet all read it. */
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ to: "/", label: "Start" },
|
||||
{ to: "/loesungen", label: "Lösungen" },
|
||||
{ to: "/so-funktionierts", label: "So funktioniert’s" },
|
||||
{ to: "/fuer-wen", label: "Für wen?", menu: AUDIENCES },
|
||||
{ to: "/konfigurator", label: "Konfigurator" },
|
||||
{ to: "/unternehmen", label: "Unternehmen" },
|
||||
{ to: "/kontakt", label: "Kontakt" },
|
||||
]
|
||||
|
||||
/** "/" is a prefix of every route, so only the root may match exactly. */
|
||||
const isExact = (to: string) => to === "/"
|
||||
|
||||
/**
|
||||
* Only colour changes between states: the highlight itself is one shared pill
|
||||
* that slides, so a weight change here would reflow the row it has to land on.
|
||||
@@ -147,9 +149,9 @@ function AudienceMenu({ item, active }: { item: NavItem; active: boolean }) {
|
||||
|
||||
function DesktopNav() {
|
||||
const { pathname } = useLocation()
|
||||
/** mirrors NavLink's default (non-`end`) matching, but for the whole row at once */
|
||||
/** mirrors each NavLink's own matching, but for the whole row at once */
|
||||
const activeKey = NAV_ITEMS.find((item) =>
|
||||
matchPath({ path: item.to, end: false }, pathname)
|
||||
matchPath({ path: item.to, end: isExact(item.to) }, pathname)
|
||||
)?.to
|
||||
const { navRef, pill } = useActivePill(activeKey)
|
||||
|
||||
@@ -173,6 +175,7 @@ function DesktopNav() {
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
end={isExact(item.to)}
|
||||
data-nav={item.to}
|
||||
className={navItemClass(item.to === activeKey)}
|
||||
>
|
||||
|
||||
@@ -6,9 +6,9 @@ import { cn } from "@/lib/utils"
|
||||
|
||||
/**
|
||||
* On/off switch. The lit track is the same teal glass as a primary button, at
|
||||
* pill scale (`inset-shadow-glass-pill`); off it is a recessed navy pane. The
|
||||
* thumb travels on the site's spring easing and carries the checkbox tick, so
|
||||
* the state reads without relying on colour alone.
|
||||
* pill scale (`inset-shadow-glass-pill`); underneath sits a recessed navy pane.
|
||||
* The thumb travels on the site's spring easing and carries the checkbox tick,
|
||||
* so the state reads without relying on colour alone.
|
||||
*/
|
||||
function Switch({
|
||||
className,
|
||||
@@ -18,19 +18,28 @@ function Switch({
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
"group peer inline-flex h-7 w-12 shrink-0 cursor-pointer items-center rounded-full p-0.5 transition-colors outline-none",
|
||||
"group peer relative inline-flex h-7 w-12 shrink-0 cursor-pointer items-center rounded-full p-0.5 outline-none",
|
||||
"inset-shadow-xs bg-navy/20 transition-colors duration-300 motion-reduce:transition-none",
|
||||
"data-[state=unchecked]:hover:bg-navy/30 data-[state=checked]:bg-brand-600",
|
||||
"focus-visible:ring-[3px] focus-visible:ring-ring/40",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"data-[state=unchecked]:inset-shadow-xs data-[state=unchecked]:bg-navy/20 data-[state=unchecked]:hover:bg-navy/30",
|
||||
"data-[state=checked]:glass-teal data-[state=checked]:inset-shadow-glass-pill data-[state=checked]:shadow-glass data-[state=checked]:bg-brand-600 data-[state=checked]:hover:glass-lit",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{/*
|
||||
* The teal glass rides its own layer: gradients cannot interpolate from
|
||||
* `none`, so switching it on the track itself would snap. Fading the
|
||||
* layer carries the gradient, rim and cast bloom across together.
|
||||
*/}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="glass-teal inset-shadow-glass-pill shadow-glass pointer-events-none absolute inset-0 rounded-full opacity-0 transition-opacity duration-300 group-hover:glass-lit group-data-[state=checked]:opacity-100 motion-reduce:transition-none"
|
||||
/>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={cn(
|
||||
"pointer-events-none flex size-6 items-center justify-center rounded-full bg-card ring-1 ring-navy/10",
|
||||
"pointer-events-none relative flex size-6 items-center justify-center rounded-full bg-card ring-1 ring-navy/10",
|
||||
"shadow-[0_1px_2px_rgb(19_41_60/0.2)]",
|
||||
"transition-transform duration-300 ease-[cubic-bezier(0.34,1.16,0.42,1)] motion-reduce:transition-none",
|
||||
"data-[state=unchecked]:translate-x-0 data-[state=checked]:translate-x-5"
|
||||
|
||||
Reference in New Issue
Block a user