Pin
Overview
The Pin component makes absolute positioning declarative.
It lets you "pin" elements like close buttons, badges, or floating actions to a corner or edge of their nearest relative container.
Preview
- Preview
- Code
Pinned button inside a card
import Pin from './components/ui';
function Example() {
return (
<div className="relative p-10 border rounded-lg">
<Pin to="top-right" offset="normal" zIndex="high">
<button>✕</button>
</Pin>
<p>Card Content</p>
</div>
);
}
Installation
- npm
- yarn
- pnpm
- manual
npx @mindfiredigital/ignix-ui add pin
yarn @mindfiredigital/ignix-ui add pin
pnpm @mindfiredigital/ignix-ui add pin
import React from "react";
import * as Slot from "@radix-ui/react-slot";
import clsx from "clsx";
type PinPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
type PinOffset = "none" | "small" | "normal" | "large" | "xl";
type PinMobile = "relative" | "absolute";
type PinZIndex = "low" | "normal" | "high" | number;
export interface PinProps extends React.HTMLAttributes<HTMLElement> {
to?: PinPosition;
offset?: PinOffset;
mobile?: PinMobile;
zIndex?: PinZIndex;
as?: React.ElementType;
children?: React.ReactNode;
className?: string;
}
export function Pin({
to = "top-right",
offset = "normal",
mobile,
zIndex = "normal",
as: Component = Slot.Slot,
children,
className,
...rest
}: PinProps): React.ReactElement {
// Position mapping
const positionMap: Record<PinPosition, string> = {
"top-left": "top-0 left-0",
"top-right": "top-0 right-0",
"bottom-left": "bottom-0 left-0",
"bottom-right": "bottom-0 right-0",
};
// Offset mapping (spacing tokens)
const offsetMap: Record<PinOffset, string> = {
none: "m-0",
small: "m-1",
normal: "m-2",
large: "m-4",
xl: "m-6",
};
// z-index mapping
const zIndexMap: Record<Exclude<PinZIndex, number>, string> = {
low: "z-10",
normal: "z-20",
high: "z-50",
};
const positionClasses = positionMap[to];
const offsetClasses = offsetMap[offset];
const zIndexClasses =
typeof zIndex === "number" ? `z-[${zIndex}]` : zIndexMap[zIndex];
// Mobile override
const mobileClasses =
mobile === "relative"
? "sm:relative sm:static"
: mobile === "absolute"
? "sm:absolute"
: "";
const classes = clsx(
"absolute",
positionClasses,
offsetClasses,
zIndexClasses,
mobileClasses,
className
);
return (
<Component className={classes} {...rest}>
{children}
</Component>
);
}
export default Pin;
Usage
import Pin from './components/ui';
function BasicPin() {
return (
<div className="relative">
<Pin to="bottom-right" offset="large">
<button className="bg-blue-600 text-white p-2 rounded-full">+</button>
</Pin>
</div>
);
}
Variants
- Preview
- Code
Pinned button inside a card
<div className="relative p-10 w-40 sm:w-96 m-auto border rounded-lg min-h-[200px]">
<Pin to="top-right" offset="normal" zIndex="high">
<button className="bg-red-600 text-white w-6 h-6 rounded-full shadow">✕</button>
</Pin>
<p>Card Content</p>
</div>
Props
Prop | Type | Default | Description |
---|---|---|---|
to | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-right" | Position of the pinned element. |
offset | "none" | "small" | "normal" | "large" | "xl" | "normal" | Spacing offset from edges. |
zIndex | "low" | "normal" | "high" | number | "normal" | Z-index control (preset or custom number). |
mobile | "relative" | "absolute" | undefined | Override positioning behavior on mobile. |
as | React.ElementType | "div" | Custom wrapper element (e.g., span , button ). |
children | ReactNode | — | Element(s) to pin. |
⚠️ Note: Parent container must have position: relative
for <Pin>
to work properly.