-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui/docs/overview): add neon gradient card to hero
- Loading branch information
1 parent
b4d45ac
commit 04ffef9
Showing
6 changed files
with
466 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { Meta, StoryObj } from '@storybook/react' | ||
import { NeonGradientCard } from './neon-gradient-card' | ||
|
||
const defaultChildren = ( | ||
<div className="w-full h-auto overflow-hidden rounded-[calc(19px-3px)] bg-transparent"> | ||
<img | ||
className="block w-full h-full object-cover m-0 p-0" | ||
src="https://github.com/user-attachments/assets/1a0a5cb7-95fd-49ce-a8b5-4742cccb1cc8" | ||
alt="2025 Docs Site Cover" | ||
/> | ||
</div> | ||
) | ||
|
||
const meta: Meta<typeof NeonGradientCard> = { | ||
title: '📚 Docs Site/Neon Gradient Card', | ||
component: NeonGradientCard, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
args: { | ||
children: defaultChildren, | ||
borderSize: 5, | ||
borderRadius: 20, | ||
neonColors: { | ||
firstColor: '#ff00aa', | ||
secondColor: '#00FFF1', | ||
}, | ||
className: '', | ||
}, | ||
argTypes: { | ||
'children': { | ||
control: false, // Disable control for this prop | ||
}, | ||
'borderSize': { | ||
control: { type: 'number', min: 1, max: 20 }, | ||
}, | ||
'borderRadius': { | ||
control: { type: 'number', min: 0, max: 50 }, | ||
}, | ||
'neonColors': { | ||
control: 'object', | ||
}, | ||
'neonColors.firstColor': { | ||
control: 'color', | ||
}, | ||
'neonColors.secondColor': { | ||
control: 'color', | ||
}, | ||
'className': { | ||
control: 'text', | ||
}, | ||
}, | ||
} | ||
|
||
export default meta | ||
|
||
type Story = StoryObj<typeof NeonGradientCard> | ||
|
||
export const Default: Story = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
'use client' | ||
|
||
import type { | ||
CSSProperties, | ||
ReactElement, | ||
ReactNode, | ||
} from 'react' | ||
import { cn } from '@cuhacking/shared/utils/cn' | ||
|
||
import { | ||
useEffect, | ||
useRef, | ||
useState, | ||
} from 'react' | ||
|
||
interface NeonColorsProps { | ||
firstColor: string | ||
secondColor: string | ||
} | ||
|
||
interface NeonGradientCardProps { | ||
/** | ||
* @default <div/> | ||
* @type ReactElement | ||
* @description | ||
* The component to be rendered as the card | ||
*/ | ||
as?: ReactElement | ||
/** | ||
* @default "" | ||
* @type string | ||
* @description | ||
* The className of the card | ||
*/ | ||
className?: string | ||
|
||
/** | ||
* @default "" | ||
* @type ReactNode | ||
* @description | ||
* The children of the card | ||
*/ | ||
children?: ReactNode | ||
|
||
/** | ||
* @default 5 | ||
* @type number | ||
* @description | ||
* The size of the border in pixels | ||
*/ | ||
borderSize?: number | ||
|
||
/** | ||
* @default 20 | ||
* @type number | ||
* @description | ||
* The size of the radius in pixels | ||
*/ | ||
borderRadius?: number | ||
|
||
/** | ||
* @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }" | ||
* @type NeonColorsProps | ||
* @description | ||
* The colors of the neon gradient | ||
*/ | ||
neonColors?: NeonColorsProps | ||
|
||
[key: string]: any | ||
} | ||
|
||
// Define the default neon colors outside the component to avoid passing an object expression directly as a default prop | ||
const defaultNeonColors: NeonColorsProps = { | ||
firstColor: '#ff00aa', | ||
secondColor: '#00FFF1', | ||
} | ||
|
||
const NeonGradientCard: React.FC<NeonGradientCardProps> = ({ | ||
className, | ||
children, | ||
borderSize = 2, | ||
borderRadius = 20, | ||
neonColors = defaultNeonColors, | ||
...props | ||
}) => { | ||
const containerRef = useRef<HTMLDivElement>(null) | ||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) | ||
|
||
useEffect(() => { | ||
const updateDimensions = () => { | ||
if (containerRef.current) { | ||
const { offsetWidth, offsetHeight } = containerRef.current | ||
setDimensions({ width: offsetWidth, height: offsetHeight }) | ||
} | ||
} | ||
|
||
updateDimensions() | ||
window.addEventListener('resize', updateDimensions) | ||
|
||
return () => { | ||
window.removeEventListener('resize', updateDimensions) | ||
} | ||
}, []) | ||
|
||
useEffect(() => { | ||
if (containerRef.current) { | ||
const { offsetWidth, offsetHeight } = containerRef.current | ||
setDimensions({ width: offsetWidth, height: offsetHeight }) | ||
} | ||
}, [children]) | ||
|
||
return ( | ||
<div | ||
ref={containerRef} | ||
style={ | ||
{ | ||
'--border-size': `${borderSize}px`, | ||
'--border-radius': `${borderRadius}px`, | ||
'--neon-first-color': neonColors.firstColor, | ||
'--neon-second-color': neonColors.secondColor, | ||
'--card-width': `${dimensions.width}px`, | ||
'--card-height': `${dimensions.height}px`, | ||
'--card-content-radius': `${borderRadius - borderSize}px`, | ||
'--pseudo-element-background-image': `linear-gradient(0deg, ${neonColors.firstColor}, ${neonColors.secondColor})`, | ||
'--pseudo-element-width': `${dimensions.width + borderSize * 2}px`, | ||
'--pseudo-element-height': `${dimensions.height + borderSize * 2}px`, | ||
'--after-blur': `${dimensions.width / 3}px`, | ||
} as CSSProperties | ||
} | ||
className={cn( | ||
'relative z-10 size-full rounded-[var(--border-radius)]', | ||
className, | ||
)} | ||
{...props} | ||
> | ||
<div | ||
className={cn( | ||
'relative size-full min-h-[100%] rounded-[var(--card-content-radius)] bg-gray-100 ', | ||
'before:absolute before:-left-[var(--border-size)] before:-top-[var(--border-size)] before:-z-10 before:block', | ||
'before:h-[var(--pseudo-element-height)] before:w-[var(--pseudo-element-width)] before:rounded-[var(--border-radius)] before:content-[\'\']', | ||
'before:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] before:bg-[length:100%_200%]', | ||
'before:animate-background-position-spin', | ||
'after:absolute after:-left-[var(--border-size)] after:-top-[var(--border-size)] after:-z-10 after:block', | ||
'after:h-[var(--pseudo-element-height)] after:w-[var(--pseudo-element-width)] after:rounded-[var(--border-radius)] after:blur-[var(--after-blur)] after:content-[\'\']', | ||
'after:bg-[linear-gradient(0deg,var(--neon-first-color),var(--neon-second-color))] after:bg-[length:100%_200%] after:opacity-80', | ||
'after:animate-background-position-spin', | ||
'dark:bg-neutral-900', | ||
)} | ||
> | ||
{children} | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
export { NeonGradientCard } |
60 changes: 60 additions & 0 deletions
60
libs/external/magic-ui/components/ui/neon-gradient-card.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { Meta, StoryObj } from '@storybook/react' | ||
import { NeonGradientCard } from './neon-gradient-card' | ||
|
||
const defaultChildren = ( | ||
<div className="w-full h-auto overflow-hidden rounded-[calc(19px-3px)] bg-transparent"> | ||
<img | ||
className="block w-full h-full object-cover m-0 p-0" | ||
src="https://github.com/user-attachments/assets/1a0a5cb7-95fd-49ce-a8b5-4742cccb1cc8" | ||
alt="2025 Docs Site Cover" | ||
/> | ||
</div> | ||
) | ||
|
||
const meta: Meta<typeof NeonGradientCard> = { | ||
title: '🪄 Magic UI/Neon Gradient Card', | ||
component: NeonGradientCard, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
args: { | ||
children: defaultChildren, | ||
borderSize: 5, | ||
borderRadius: 20, | ||
neonColors: { | ||
firstColor: '#ff00aa', | ||
secondColor: '#00FFF1', | ||
}, | ||
className: '', | ||
}, | ||
argTypes: { | ||
'children': { | ||
control: false, // Disable control for this prop | ||
}, | ||
'borderSize': { | ||
control: { type: 'number', min: 1, max: 20 }, | ||
}, | ||
'borderRadius': { | ||
control: { type: 'number', min: 0, max: 50 }, | ||
}, | ||
'neonColors': { | ||
control: 'object', | ||
}, | ||
'neonColors.firstColor': { | ||
control: 'color', | ||
}, | ||
'neonColors.secondColor': { | ||
control: 'color', | ||
}, | ||
'className': { | ||
control: 'text', | ||
}, | ||
}, | ||
} | ||
|
||
export default meta | ||
|
||
type Story = StoryObj<typeof NeonGradientCard> | ||
|
||
export const Default: Story = {} |
Oops, something went wrong.