lampEffect
A Backgrounds block/component from Aceternity UI — clean-room reimplemented for Domphy (see methodology). Call lampEffect() with no arguments for a working demo, or edit the code below live.
Implementation notes
Each cone half is built from clip-path: polygon(...) (a triangle rotated outward from vertical, mirrored left/right) filled with a plain linear-gradient(to bottom, bright, transparent), rather than a literal CSS conic-gradient() as the research note describes - a deliberate substitution for simpler, more predictable 'twin wedge fanning from a point' geometry; visually it reads as the same twin-cone light shape. A mask-image linear-gradient fades the whole cone's bottom edge into the section background as specified. All light-emitting pieces (both cone halves, the bright bar, two glow blobs at 8rem/16rem and 15rem/30rem per the research note) play a one-time motion()-driven width/opacity entrance, staggered by small per-element delays, over ~0.8s ease-in-out, matching the spec's timing.
Status: ported · Reference: Aceternity UI original
// Aceternity UI "Lamp Effect" — clean-room reimplementation from the public
// behavior/visual spec only (no upstream source viewed or copied). A
// section-header background depicting a stage-lamp light cone: two mirrored
// gradient wedges meeting above the heading, plus a thin bright bar and two
// soft blurred glow blobs at the light source, all widening/brightening
// together in a single one-time entrance on mount.
//
// Each cone half is a `clip-path: polygon(...)` triangle (apex at top-center
// of its own box, base spanning its full width) rotated a few degrees
// outward from vertical and mirrored left/right — a plain
// `linear-gradient(to bottom, bright, transparent)` fill, not a literal CSS
// `conic-gradient()` — which reads as the same "twin fan of light meeting at
// a point" shape with simpler, more predictable geometry (see this
// component's `fidelityNotes`). A `mask-image` linear-gradient on the
// wrapper fades the whole cone's bottom edge into the section background
// instead of cutting it off sharply, matching the spec.
//
// Every light-emitting piece (both cone halves, the bright bar, the two glow
// blobs) plays its own one-time `motion()` entrance — narrower/dimmer to
// full width/opacity — over well under a second, staggered by a small delay
// per element, exactly once on mount; nothing continues animating
// afterward and there is no hover/pointer interaction.
import type { DomphyElement, StyleObject } from "@domphy/core";
import { heading, motion } from "@domphy/ui";
import { type ThemeColor, themeColor, themeSpacing } from "@domphy/theme";
export interface LampEffectProps {
/** Heading/content shown tucked under the light source. Defaults to a small demo heading. */
children?: DomphyElement | DomphyElement[];
/** Theme color family for the cone/glow. Defaults to `"info"` (cyan/blue). */
glowColor?: ThemeColor;
style?: StyleObject;
}
const ENTRANCE_DURATION_MS = 800;
function defaultLampContent(): DomphyElement[] {
return [{ h1: "Build faster, ship brighter", $: [heading({ color: "neutral" })] } as DomphyElement];
}
/**
* A stage-lamp light cone — two mirrored gradient wedges meeting above the
* heading, plus a bright bar and soft glow blobs at the light source — that
* widens and brightens once on mount, then holds static. Non-interactive.
* Call with no arguments for a working demo.
*/
function lampEffect(props: LampEffectProps = {}): DomphyElement<"div"> {
const glowColor = props.glowColor ?? "info";
const contentChildren = props.children
? Array.isArray(props.children)
? props.children
: [props.children]
: defaultLampContent();
const coneEntranceTransition = { duration: ENTRANCE_DURATION_MS, delay: 0, easing: "ease-in-out" };
// `_doctorDisable` is a doctor-only annotation not present in core's strict
// `PartialElement` type — build through an untyped literal, then assert, so
// the excess-property check doesn't fire (mirrors meteors.ts).
function coneHalf(side: "left" | "right") {
const outwardAngle = side === "left" ? -14 : 14;
const apexAnchor = side === "left" ? "top right" : "top left";
const anchorOffset = side === "left" ? "translateX(-100%)" : "translateX(0%)";
return {
div: null,
ariaHidden: "true",
// Decorative light wedge with no text of its own — exempt from the
// missing-color contract (mirrors meteors.ts's dot spans elsewhere).
_doctorDisable: "missing-color",
style: {
position: "absolute",
top: 0,
left: "50%",
// Matches `motion()`'s `animate` end value — environments without
// WAAPI support (`el.animate`) render at this resting/final width
// immediately instead of collapsing to 0 (mirrors `blurFade.ts`'s
// "persistent style already IS the settled appearance" convention).
width: themeSpacing(120),
height: themeSpacing(112),
transformOrigin: apexAnchor,
transform: `${anchorOffset} rotate(${outwardAngle}deg)`,
clipPath: "polygon(50% 0%, 0% 100%, 100% 100%)",
backgroundImage: (listener) =>
`linear-gradient(to bottom, ${themeColor(listener, "shift-13", glowColor)}, transparent)`,
} as StyleObject,
$: [
motion({
initial: { width: themeSpacing(60), opacity: 0.5 },
animate: { width: themeSpacing(120), opacity: 1 },
transition: coneEntranceTransition,
}),
],
} as DomphyElement<"div">;
}
const glowBar = {
div: null,
ariaHidden: "true",
// Decorative light-source bar with no text of its own — exempt from the
// missing-color contract. Also exempt from tone-background-inherit: the
// bar's brightness is intentionally a fixed accent, not a surface that
// should track the ambient dataTone context (same reasoning as
// meteors.ts's dots elsewhere in this package).
_doctorDisable: ["missing-color", "tone-background-inherit"],
style: {
position: "absolute",
top: themeSpacing(28),
left: "50%",
transform: "translateX(-50%)",
// Matches `motion()`'s `animate` end value — see the matching note on
// `coneHalf()` above.
width: themeSpacing(64),
height: themeSpacing(0.5),
borderRadius: "999px",
backgroundColor: (listener) => themeColor(listener, "shift-16", glowColor),
boxShadow: (listener) => `0 0 ${themeSpacing(6)} ${themeColor(listener, "shift-13", glowColor)}`,
} as StyleObject,
$: [
motion({
initial: { width: themeSpacing(32), opacity: 0.5 },
animate: { width: themeSpacing(64), opacity: 1 },
transition: { ...coneEntranceTransition, delay: 100 },
}),
],
} as DomphyElement<"div">;
function glowBlob(key: string, restingUnits: number, expandedUnits: number, blurPx: number, delay: number): DomphyElement {
return {
div: null,
_key: key,
ariaHidden: "true",
// Decorative glow blob with no text of its own — exempt from the
// missing-color contract. Also exempt from tone-background-inherit:
// the blob's brightness is intentionally a fixed accent, not a
// surface that should track the ambient dataTone context (same
// reasoning as meteors.ts's dots elsewhere in this package).
_doctorDisable: ["missing-color", "tone-background-inherit"],
style: {
position: "absolute",
top: themeSpacing(28),
left: "50%",
transform: "translate(-50%, -50%)",
// Matches `motion()`'s `animate` end value — see the matching note on
// `coneHalf()` above.
width: themeSpacing(expandedUnits),
height: themeSpacing(expandedUnits),
borderRadius: "50%",
filter: `blur(${blurPx}px)`,
backgroundColor: (listener) => themeColor(listener, "shift-11", glowColor),
} as StyleObject,
$: [
motion({
initial: { width: themeSpacing(restingUnits), height: themeSpacing(restingUnits), opacity: 0.5 },
animate: { width: themeSpacing(expandedUnits), height: themeSpacing(expandedUnits), opacity: 1 },
transition: { ...coneEntranceTransition, delay },
}),
],
} as DomphyElement;
}
const coneWrapper = {
div: [
coneHalf("left"),
coneHalf("right"),
glowBlob("lamp-glow-outer", 60, 120, 96, 200),
glowBlob("lamp-glow-inner", 32, 64, 48, 150),
glowBar,
],
ariaHidden: "true",
_doctorDisable: "missing-color",
style: {
position: "absolute",
inset: 0,
overflow: "hidden",
// Fades the whole cone's bottom edge into the section background
// instead of cutting it off sharply.
maskImage: "linear-gradient(to bottom, black, transparent)",
WebkitMaskImage: "linear-gradient(to bottom, black, transparent)",
} as StyleObject,
} as DomphyElement<"div">;
return {
div: [
coneWrapper,
{
div: contentChildren,
style: {
position: "relative",
zIndex: 1,
textAlign: "center",
// A moderate top gap (well short of the cone's own `themeSpacing(112)`
// height) tucks the heading up near the glow bar/blobs rather than
// fully below the faded tail of the cone.
marginTop: themeSpacing(24),
paddingInline: themeSpacing(6),
} as StyleObject,
} as DomphyElement,
],
dataTone: "shift-17",
style: {
position: "relative",
overflow: "hidden",
borderRadius: themeSpacing(4),
minHeight: themeSpacing(140),
paddingBottom: themeSpacing(16),
backgroundColor: (listener) => themeColor(listener, "inherit"),
color: (listener) => themeColor(listener, "shift-9"),
display: "flex",
flexDirection: "column",
alignItems: "center",
...(props.style ?? {}),
} as StyleObject,
};
}
export { lampEffect };