Skip to content

feat: added glow component #654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ export const docsConfig: DocsConfig = {
items: [],
label: "",
},
{
title: "Glow",
href: `/docs/components/glow`,
items: [],
label: "New",
},
],
},
{
Expand Down
77 changes: 77 additions & 0 deletions content/docs/components/glow.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Glow Component
date: 2025-04-20
description: A beautiful glow effect that appears on hover interactions
author: Syeda Hoorain Ali
published: true
---

<ComponentPreview name="glow-demo" />

## Installation

<Tabs defaultValue="cli">

<TabsList>
<TabsTrigger value="cli">CLI</TabsTrigger>
<TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">

```bash
npx shadcn@latest add "https://magicui.design/r/glow"
```

</TabsContent>

<TabsContent value="manual">

<Steps>

<Step>Copy and paste the following code into your project.</Step>

<ComponentSource name="glow" />

<Step>Update the import paths to match your project setup.</Step>

<Step>Update `global.css`</Step>

Add the following lines to your `global.css` file:

```css title="app/global.css" {5-7}
@tailwind base;
@tailwind components;
@tailwind utilities;

@theme inline {
--radius-inherit: inherit;
}
```

</Steps>

</TabsContent>

</Tabs>

## Props

### GlowArea

| Prop | Type | Default | Description |
| ----------- | ----------- | ------- | --------------------------------------------- |
| `className` | `string` | `-` | The class name to be applied to the component |
| `children` | `ReactNode` | `-` | Children elements |
| `size` | `number` | `50` | The size of the glow area |

### Glow

| Prop | Type | Default | Description |
| ----------- | ----------- | -------- | --------------------------------------------- |
| `className` | `string` | `-` | The class name to be applied to the component |
| `children` | `ReactNode` | `-` | Children elements |
| `color` | `string` | `purple` | The color of the glow |

## Credits

- Credit to [@Syeda Hoorain Ali](https://github.com/syeda-hoorain-ali/)
22 changes: 22 additions & 0 deletions registry/example/glow-demo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Card } from "@/components/ui/card";
import { GlowArea, Glow } from "@/registry/magicui/glow";

export default function GlowDemo() {
return (
<GlowArea
size={200}
className="w-full h-full min-h-72 grid grid-cols-2 gap-4"
>
<Glow
color="#db2777"
className="border border-muted rounded-lg flex items-center justify-center shadow-lg"
>
<span className="text-2xl font-semi-bold">Hover</span>
</Glow>

<Glow className="border border-muted rounded-lg flex items-center justify-center shadow-lg">
<span className="text-2xl font-semi-bold">Me</span>
</Glow>
</GlowArea>
);
}
128 changes: 128 additions & 0 deletions registry/magicui/glow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"use client";

import {
ComponentPropsWithoutRef,
CSSProperties,
MouseEvent,
useEffect,
useRef,
} from "react";

import { cn } from "@/lib/utils";

interface GlowAreaProps extends ComponentPropsWithoutRef<"div"> {
/**
* @default 50
* @type number
* @description
* The size of the glow area
* */
size?: number;
}

export const GlowArea: React.FC<GlowAreaProps> = (props) => {
const { className, size = 50, ...rest } = props;
const element = useRef<HTMLDivElement | null>(null);
const latestCoords = useRef<{ x: number; y: number } | null>(null);
const frameId = useRef<number | null>(null);

const animateGlow = () => {
if (latestCoords.current && element.current) {
element.current.style.setProperty(
"--glow-x",
`${latestCoords.current.x}px`,
);
element.current.style.setProperty(
"--glow-y",
`${latestCoords.current.y}px`,
);

frameId.current = null;
}
};

const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => {
const bounds = e.currentTarget.getBoundingClientRect();
latestCoords.current = {
x: e.clientX - bounds.left,
y: e.clientY - bounds.top,
};
console.log(latestCoords.current);

if (!frameId.current) {
frameId.current = requestAnimationFrame(() => animateGlow());
}
};

const handleMouseLeave = (e: MouseEvent<HTMLDivElement>) => {
e.currentTarget.style.removeProperty("--glow-x");
e.currentTarget.style.removeProperty("--glow-y");
};

return (
<div
className={cn("relative", className)}
ref={element}
onMouseLeave={handleMouseLeave}
onMouseMove={handleMouseMove}
style={
{
"--glow-size": `${size}px`,
} as CSSProperties
}
{...rest}
/>
);
};

GlowArea.displayName = "GlowArea";

interface GlowProps extends ComponentPropsWithoutRef<"div"> {
/**
* @default "purple"
* @type string
* @description
* The color of the glow
* */
color?: string;
}

export const Glow: React.FC<GlowProps> = (props) => {
const { className, color = "purple", children, ...rest } = props;

const element = useRef<HTMLDivElement | null>(null);

useEffect(() => {
element.current?.style.setProperty(
"--glow-top",
`${element.current.offsetTop}px`,
);
element.current?.style.setProperty(
"--glow-left",
`${element.current.offsetLeft}px`,
);
}, []);

return (
<div ref={element} className={cn(className, "relative")}>
<div
style={{
backgroundImage: `radial-gradient(
var(--glow-size) var(--glow-size) at
calc(var(--glow-x, -99999px) - var(--glow-left, 0px))
calc(var(--glow-y, -99999px) - var(--glow-top, 0px)),
${color} 0%,
transparent 100%
)`,
}}
className={cn(
className,
"absolute pointer-events-none inset-0 transition-all mix-blend-multiply dark:mix-blend-lighten after:content-[''] after:absolute after:bg-background/80 after:inset-[0.1rem] after:rounded-[inherit]",
)}
></div>
{children}
</div>
);
};

Glow.displayName = "Glow";
15 changes: 15 additions & 0 deletions registry/registry-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1710,4 +1710,19 @@ export const examples: Registry["items"] = [
},
],
},
{
name: "glow-demo",
type: "registry:example",
title: "Glow Demo",
description:
"Example showing a beautiful glow effect that appears when hovering over elements.",
registryDependencies: ["https://magicui.design/r/glow"],
files: [
{
path: "registry/example/glow-demo.tsx",
type: "registry:example",
// target: "components/glow-demo.tsx",
},
],
},
];
14 changes: 14 additions & 0 deletions registry/registry-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1174,4 +1174,18 @@ export const ui: Registry["items"] = [
},
],
},
{
name: "glow",
type: "registry:ui",
title: "Glow",
description:
"A beautiful glow effect that appears when hovering over elements",
files: [
{
path: "registry/magicui/glow.tsx",
type: "registry:ui",
target: "components/magicui/glow.tsx",
},
],
},
];
4 changes: 4 additions & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -521,3 +521,7 @@
.steps > h3 {
@apply mt-8 mb-4 text-base font-semibold;
}

@theme inline {
--radius-inherit: inherit;
}