Skip to content

[Up for anyone to implement/port] NeonGradientCard Implementation #4

@shayanhabibi

Description

@shayanhabibi

Hey, I've made an implementation of the NeonGradientCard on a compile-to-javascript platform.

Unfortunately I don't have the knowledge to make an implementation, but anyone can feel free to make a pull. Hopefully these excerpts help.

JSX
import { Record } from "../fable_modules/fable-library-js.5.0.0-alpha.13/Types.js";
import { record_type, string_type } from "../fable_modules/fable-library-js.5.0.0-alpha.13/Reflection.js";
import { createSignal, splitProps, mergeProps } from "solid-js";
import { defaultOf } from "../fable_modules/fable-library-js.5.0.0-alpha.13/Util.js";
import { createElementBounds } from "@solid-primitives/bounds";
import { Lib_cn_Z35CD86D0 } from "../Utils.jsx";

export class NeonColors extends Record {
    constructor(First, Second) {
        super();
        this.First = First;
        this.Second = Second;
    }
}

export function NeonColors_$reflection() {
    return record_type("Partas.Solid.UIx.NeonColors", [], NeonColors, () => [["First", string_type], ["Second", string_type]]);
}

export function NeonGradientCard(props) {
    props = mergeProps({
        neonColors: new NeonColors("#ff00aa", "#00FFF1"),
        borderWidth: 2,
        borderRadius: 20,
    }, props);
    const [PARTAS_LOCAL, PARTAS_OTHERS] = splitProps(props, ["class", "borderWidth", "borderRadius", "neonColors", "cardClass", "children"]);
    const patternInput = createSignal(defaultOf());
    const containerBounds = createElementBounds(patternInput[0]);
    return <div class={Lib_cn_Z35CD86D0(["relative z-10 size-full rounded-(--border-radius)", PARTAS_LOCAL.class])}
        ref={patternInput[1]}
        style={{
            "--border-size": `${PARTAS_LOCAL.borderWidth}px`,
            "--border-radius": `${PARTAS_LOCAL.borderRadius}px`,
            "--neon-first-color": PARTAS_LOCAL.neonColors.First,
            "--neon-second-color": PARTAS_LOCAL.neonColors.Second,
            "--card-width": `${containerBounds.width}px`,
            "--card-height": `${containerBounds.height}px`,
            "--card-content-radius": `${PARTAS_LOCAL.borderRadius - PARTAS_LOCAL.borderWidth}px`,
            "--pseudo-element-background-image": `linearGradient(0deg, ${PARTAS_LOCAL.neonColors.First}, ${PARTAS_LOCAL.neonColors.Second})`,
            "--pseudo-element-width": `${containerBounds.width + (PARTAS_LOCAL.borderWidth * 2)}px`,
            "--pseudo-element-height": `${containerBounds.height + (PARTAS_LOCAL.borderWidth * 2)}px`,
            "--after-blur": `${containerBounds.width / 3}px`,
        }}
        {...PARTAS_OTHERS} bool:n$={false}>
        <div class={Lib_cn_Z35CD86D0(["relative size-full min-h-[inherit] rounded-(--card-content-radius) bg-gray-100 p-6", "before:absolute before:-left-(--border-size) before:-top-(--border-size) before:-z-10 before:block", "before:h-(--pseudo-element-height) before:w-(--pseudo-element-width) before:rounded-(--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-(--border-size) after:-top-(--border-size) after:-z-10 after:block", "after:h-(--pseudo-element-height) after:w-(--pseudo-element-width) after:rounded-(--border-radius) after:blur-(--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", PARTAS_LOCAL.cardClass])}>
            {PARTAS_LOCAL.children}
        </div>
    </div>;
}
F#
namespace Partas.Solid.UIx

open Browser.Types
open Partas.Solid
open Partas.Solid.Style
open Fable.Core
open Fable.Core.JsInterop
open Partas.Solid.Experimental
open Partas.Solid.Primitives.Bounds

type NeonColors = {
    First: string
    Second: string
}

type private Height = int
type private Width = int
type Dimensions = Width * Height

[<Erase>]
type NeonGradientCard() =
    inherit div()
    [<DefaultValue>]
    val mutable neonColors: NeonColors
    [<DefaultValue>]
    val mutable borderWidth: int
    [<DefaultValue>]
    val mutable borderRadius: int
    [<DefaultValue>]
    val mutable cardClass: string
    [<SolidTypeComponent>]
    member props.__ =
        props.neonColors <- {
            First = "#ff00aa"
            Second = "#00FFF1"
        }
        props.borderWidth <- 2
        props.borderRadius <- 20
        let containerRef,setContainerRef = createSignal<HTMLDivElement>(null)
        let containerBounds = createElementBounds(containerRef)
        div(class' = Lib.cn [|
            "relative z-10 size-full rounded-(--border-radius)"
            props.class'
        |])
            .ref(setContainerRef)
            .style'([
                "--border-size" ==> $"{props.borderWidth}px"
                "--border-radius" ==> $"{props.borderRadius}px"
                "--neon-first-color" ==> props.neonColors.First
                "--neon-second-color" ==> props.neonColors.Second
                "--card-width" ==> $"{containerBounds.width}px"
                "--card-height" ==> $"{containerBounds.height}px"
                "--card-content-radius" ==> $"{props.borderRadius - props.borderWidth}px"
                "--pseudo-element-background-image" ==> $"linearGradient(0deg, {props.neonColors.First}, {props.neonColors.Second})"
                "--pseudo-element-width" ==> $"{containerBounds.width + props.borderWidth * 2}px"
                "--pseudo-element-height" ==> $"{containerBounds.height + props.borderWidth * 2}px"
                "--after-blur" ==> $"{float containerBounds.width / 3.}px"
            ])
            .spread props {
            div(
            class' = Lib.cn [|
                "relative size-full min-h-[inherit] rounded-(--card-content-radius) bg-gray-100 p-6"
                "before:absolute before:-left-(--border-size) before:-top-(--border-size) before:-z-10 before:block"
                "before:h-(--pseudo-element-height) before:w-(--pseudo-element-width) before:rounded-(--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-(--border-size) after:-top-(--border-size) after:-z-10 after:block"
                "after:h-(--pseudo-element-height) after:w-(--pseudo-element-width) after:rounded-(--border-radius) after:blur-(--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"
                props.cardClass
            |]
            ) { props.children }
        }
        
CSS Tailwind
@theme inline {
  --animate-background-position-spin: background-position-spin 3000ms infinite
    alternate;
 
  @keyframes background-position-spin {
    0% {
      background-position: top center;
    }
    100% {
      background-position: bottom center;
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions