Comprehensive neumorphic components for all needs.
Adjust colors, shadows, and borders easily.
Optimized for all screen sizes.
Apply neumorphic styles efficiently with utilities.
Fast and lightweight components for smooth performance.
Toggle inner and outer shadows effortlessly.
Soft UI provides different components. These are as follows:
"use client";
import React, { useEffect, useState } from "react";
import clsx from "clsx";
type LEDLightProps = {
size?: number;
color?: string;
className?: string;
enabled?: boolean;
};
const LEDLight: React.FC<LEDLightProps> = ({
size = 100,
color = "red",
className = "",
enabled = true,
}) => {
const [ledColor, setLedColor] = useState("inherit");
const [shadowColor, setShadowColor] = useState("inherit");
useEffect(() => {
if (enabled) {
setLedColor(color);
setShadowColor(`${color}, ${color}66`);
} else {
setLedColor("inherit");
setShadowColor("inherit, inherit");
}
}, [color, enabled]);
return (
<div
className={clsx(
"relative flex items-center justify-center rounded-full bg-gray-200",
"shadow-[inset_3px_3px_6px_rgba(0,0,0,0.2),inset_-3px_-3px_6px_rgba(255,255,255,0.6)]",
className
)}
style={{ width: size, height: size }}
>
<div
className={clsx(
"absolute rounded-full",
"shadow-[0px_0px_10px_rgba(0,0,0,0.2)] transition-all duration-300"
)}
style={{
width: size / 1.55,
height: size / 1.55,
backgroundColor: ledColor,
boxShadow: `0 0 10px ${shadowColor}, 0 0 20px ${shadowColor}`,
filter: "blur(0.8px)",
transition: "background-color 0.5s ease, box-shadow 0.5s ease",
}}
/>
</div>
);
};
export default LEDLight;
"use client";
import LEDLight from "@/components/LEDLight";
import Switch from "@/components/Switch";
import { useState } from "react";
import FeatureContainer from "./FeatureContainer";
export default function LEDDemo() {
const [ledEnabled, setLedEnabled] = useState(true);
return (
<FeatureContainer title="LED Lights" id="LED">
<div className="flex gap-4 items-center">
<div className="flex items-center gap-4">
<LEDLight color="#ef4444" size={20} enabled={ledEnabled} />
<LEDLight color="#fcd34d" size={20} enabled={ledEnabled} />
<LEDLight color="#4ade80" size={20} enabled={ledEnabled} />
</div>
<div>
<Switch
size="large"
enabled={ledEnabled}
onToggle={() => setLedEnabled(!ledEnabled)}
/>
</div>
</div>
</FeatureContainer>
);
}
"use client";
import React from "react";
import clsx from "clsx";
type SwitchProps = {
enabled: boolean;
onToggle: (param: boolean) => void;
size?: "small" | "medium" | "large";
className?: string;
};
const Switch: React.FC<SwitchProps> = ({
enabled,
onToggle,
size = "medium",
className,
}) => {
const sizeStyles = {
small: {
switch: "w-10 h-5 rounded-lg",
circle: "w-3 h-3 rounded-md",
translate: "translate-x-6",
text: "text-xs",
},
medium: {
switch: "w-14 h-7 rounded-xl",
circle: "w-5 h-5 rounded-lg",
translate: "translate-x-8",
text: "text-sm",
},
large: {
switch: "w-20 h-10 rounded-2xl",
circle: "w-8 h-8 rounded-xl",
translate: "translate-x-11",
text: "text-base",
},
};
return (
<div
className={clsx(
"relative inline-flex items-center cursor-pointer transition-all duration-300",
className
)}
onClick={() => onToggle(!enabled)}
>
<div
className={clsx(
"flex items-center justify-between px-1 bg-gradient-to-r from-background-card to-background-card-secondary border border-muted/20 shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.4)]",
sizeStyles[size].switch
)}
>
<span
className={clsx(
sizeStyles[size].text,
"text-muted font-semibold transition-opacity duration-300 text-sm mx-2",
enabled ? "opacity-100" : "opacity-0",
size !== "large" && "hidden"
)}
>
on
</span>
<span
className={clsx(
sizeStyles[size].text,
"text-muted font-semibold transition-opacity duration-300 text-sm mx-2",
!enabled ? "opacity-100" : "opacity-0",
size !== "large" && "hidden"
)}
>
off
</span>
</div>
<div
className={clsx(
"flex items-center justify-center absolute top-1 bg-white shadow-md transition-transform duration-300",
sizeStyles[size].circle,
enabled ? sizeStyles[size].translate : "translate-x-1"
)}
>
<div className="h-[50%] rounded-full w-[3px] bg-background-card shadow-sm"></div>
</div>
</div>
);
};
export default Switch;
"use client";
import Switch from "@/components/Switch";
import { useState } from "react";
import FeatureContainer from "./FeatureContainer";
export default function SwitchDemo() {
const [switchAEnabled, setSwitchAEnabled] = useState(true);
const [switchBEnabled, setSwitchBEnabled] = useState(true);
const [switchCEnabled, setSwitchCEnabled] = useState(true);
return (
<FeatureContainer title="Switch Buttons" id="Switch">
<div>
<div className="flex items-center gap-4">
<Switch
enabled={switchAEnabled}
onToggle={(val) => setSwitchAEnabled(val)}
size="large"
/>
<Switch
enabled={switchBEnabled}
onToggle={(val) => setSwitchBEnabled(val)}
size="medium"
/>
<Switch
enabled={switchCEnabled}
onToggle={(val) => setSwitchCEnabled(val)}
size="small"
/>
</div>
</div>
</FeatureContainer>
);
}
import { lcdFont } from "@/lib/fonts";
import Card from "./Card";
import clsx from "clsx";
import { ReactNode } from "react";
type Elevation = "none" | "outside" | "inside" | "mix";
type LCDScreenProps = {
children: ReactNode;
className?: string;
elevation?: Elevation;
showGrid?: boolean;
};
export default function LCDScreen({
children,
className,
elevation = "inside",
showGrid = false,
}: LCDScreenProps) {
return (
<Card
variant="lcd"
className={clsx(
"relative text-4xl p-3 rounded-md text-center",
lcdFont.className,
className
)}
elevation={elevation}
>
{showGrid && (
<div
className="absolute w-full h-full inset-0"
style={{
backgroundImage: `
linear-gradient(to right, rgba(0,0,0,0.1) 1px, transparent 1px),
linear-gradient(to bottom, rgba(0,0,0,0.1) 1px, transparent 1px)
`,
backgroundSize: "10px 10px",
pointerEvents: "none",
}}
></div>
)}
<div>{children}</div>
</Card>
);
}
"use client";
import { useEffect, useRef, useState } from "react";
import FeatureContainer from "./FeatureContainer";
import LCDScreen from "@/components/LCDScreen";
export default function LCDScreenDemo() {
// CLOCK
const [hours, setHours] = useState<string>("00");
const [minutes, setMinutes] = useState<string>("00");
const [seconds, setSeconds] = useState<string>("00");
useEffect(() => {
const timerId = setInterval(() => {
const currentTime = new Date();
setHours(addLeadingZero(currentTime.getHours()));
setMinutes(addLeadingZero(currentTime.getMinutes()));
setSeconds(addLeadingZero(currentTime.getSeconds()));
}, 1000);
return () => clearInterval(timerId);
}, []);
const addLeadingZero = (num: number) =>
num < 10 ? `0${num}` : num.toString();
// Temperature
const [temperature, setTemperature] = useState(20);
useEffect(() => {
const temperatureId = setInterval(() => {
setTemperature((prevTemperature) => {
const newTemperature = prevTemperature + 1;
if (newTemperature >= 25) {
return 16;
}
return newTemperature;
});
}, 1000);
return () => clearInterval(temperatureId);
}, []);
// Wave
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const width = canvas.width;
const height = canvas.height;
const amplitude = 50; // Height of the wave
const frequency = 0.1; // Frequency of the wave
let offset = 0;
const drawWave = () => {
ctx.clearRect(0, 0, width, height); // Clear the canvas
ctx.beginPath();
ctx.moveTo(0, height / 2);
for (let x = 0; x < width; x++) {
const y = amplitude * Math.sin(frequency * x + offset) + height / 2;
ctx.lineTo(x, y);
}
ctx.strokeStyle = "#555555";
ctx.lineWidth = 2;
ctx.stroke();
};
const animate = () => {
drawWave();
offset -= 0.05; // Adjust this value to control the speed of the wave
requestAnimationFrame(animate);
};
animate(); // Start the animation
}, []);
// Inline styles for the canvas to handle animation
const canvasStyle: React.CSSProperties = {
display: "block",
animation: "moveCanvas 10s linear infinite",
backgroundColor: "transparent",
};
return (
<FeatureContainer title="LCD Screens" id="LCD">
<div className="space-y-3">
<div className="flex gap-4 items-center flex-wrap">
{/* CLOCK */}
<LCDScreen className="min-w-[160px]">
{`${hours}:${minutes}:${seconds}`}
</LCDScreen>
{/* TEMPERATURE */}
<LCDScreen className="min-w-[160px]">{`${temperature}°C`}</LCDScreen>
</div>
{/* WAVE */}
<div>
<LCDScreen showGrid={true}>
<canvas
ref={canvasRef}
width={200}
height={120}
style={canvasStyle}
/>
</LCDScreen>
</div>
</div>
</FeatureContainer>
);
}
import React from "react";
import clsx from "clsx";
type SliderProps = {
value: number;
min?: number;
max?: number;
step?: number;
onChange: (value: number) => void;
size?: "small" | "medium" | "large";
className?: string;
progressColor?: string;
};
const Slider: React.FC<SliderProps> = ({
value,
min = 0,
max = 100,
step = 1,
onChange,
size = "medium",
className,
progressColor = "red",
}) => {
const sizeStyles = {
small: {
trackHeight: "h-2",
thumbSize: "w-10 h-8",
thumbOffset: 20,
},
medium: {
trackHeight: "h-3",
thumbSize: "w-12 h-10",
thumbOffset: 20,
},
large: {
trackHeight: "h-4",
thumbSize: "w-14 h-12",
thumbOffset: 24,
},
};
const percentage = ((value - min) / (max - min)) * 100;
const thumbOffset =
percentage === 0
? 0
: percentage === 100
? sizeStyles[size].thumbOffset * 2
: sizeStyles[size].thumbOffset;
return (
<div
className={clsx(
"relative flex items-center bg-background-variant px-3 py-2 rounded-full",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
className
)}
>
<div
className={clsx(
"relative rounded-full bg-background-variant",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
sizeStyles[size].trackHeight,
"w-full"
)}
>
<div
className={clsx("absolute top-0 left-0 rounded-full")}
style={{
background: progressColor,
height: "100%",
width: `${percentage}%`,
}}
/>
</div>
<div
className={clsx(
"absolute top-1/2 transform -translate-y-1/2 rounded-lg border border-white bg-background-variant flex items-center justify-center",
"shadow-[2px_2px_8px_rgba(0,0,0,0.1),-2px_-2px_8px_rgba(255,255,255,0.7)]",
sizeStyles[size].thumbSize
)}
style={{ left: `calc(${percentage}% - ${thumbOffset}px)` }} // Adjust thumb positioning
>
<div className="h-[50%] rounded-full w-[3px] bg-background-card shadow-sm"></div>
</div>
<input
type="range"
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
className="absolute w-full h-full opacity-0 cursor-pointer"
/>
</div>
);
};
export default Slider;
"use client";
import { useState } from "react";
import FeatureContainer from "./FeatureContainer";
import Slider from "@/components/Slider";
export default function SliderDemo() {
const [value1, setValue1] = useState(20);
const [value2, setValue2] = useState(60);
const [value3, setValue3] = useState(15);
return (
<FeatureContainer title="Sliders" id="Slider">
<div className="mt-4 space-y-8">
<Slider
size="small"
progressColor="green"
value={value1}
onChange={(val) => setValue1(val)}
/>
<Slider
size="medium"
progressColor="red"
value={value2}
onChange={(val) => setValue2(val)}
/>
<Slider
size="large"
progressColor="blue"
value={value3}
onChange={(val) => setValue3(val)}
/>
</div>
</FeatureContainer>
);
}
import React from "react";
import clsx from "clsx";
type SizeTypes = "small" | "medium" | "large";
interface TextFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
fieldSize?: SizeTypes;
startIcon?: React.ReactNode;
endIcon?: React.ReactNode;
className?: string;
}
const TextField: React.FC<TextFieldProps> = ({
fieldSize = "medium",
startIcon,
endIcon,
className = "",
...inputProps
}) => {
const sizeStyles: Record<SizeTypes, string> = {
small: "text-sm py-1 px-2",
medium: "text-base py-2 px-3",
large: "text-lg py-3 px-4",
};
const sizeClass = sizeStyles[fieldSize] || sizeStyles.medium;
return (
<div
className={clsx(
"flex items-center rounded-lg bg-background-variant",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.1),inset_-2px_-2px_5px_rgba(255,255,255,0.7)]",
sizeClass,
className
)}
>
{startIcon && <div className="mr-2 text-muted">{startIcon}</div>}
<input
className={clsx(
"flex-1 bg-transparent outline-none",
startIcon && "pl-1",
endIcon && "pr-1"
)}
{...inputProps}
/>
{endIcon && <div className="ml-2 text-muted">{endIcon}</div>}
</div>
);
};
export default TextField;
import React from "react";
import clsx from "clsx";
type SizeTypes = "small" | "medium" | "large";
interface TextAreaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
size?: SizeTypes;
startIcon?: React.ReactNode;
endIcon?: React.ReactNode;
className?: string;
}
const TextArea: React.FC<TextAreaProps> = ({
size = "medium",
startIcon,
endIcon,
className = "",
...textareaProps
}) => {
const sizeStyles: Record<SizeTypes, string> = {
small: "text-sm py-1 px-2",
medium: "text-base py-2 px-3",
large: "text-lg py-3 px-4",
};
const sizeClass = sizeStyles[size] || sizeStyles.medium;
return (
<div
className={clsx(
"flex items-center rounded-lg bg-background-variant",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.1),inset_-2px_-2px_5px_rgba(255,255,255,0.7)]",
className
)}
>
{startIcon && <div className="mr-2">{startIcon}</div>}
<textarea
className={clsx("resize-none flex-1 bg-transparent outline-none", sizeClass)}
{...textareaProps}
/>
{endIcon && <div className="ml-2">{endIcon}</div>}
</div>
);
};
export default TextArea;
import React from "react";
import clsx from "clsx";
type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
label?: string;
size?: "small" | "medium" | "large";
className?: string;
};
const Checkbox: React.FC<CheckboxProps> = ({
checked,
onChange,
label,
size = "medium",
className = "",
disabled = false,
...rest
}) => {
const sizeStyles: Record<string, string> = {
small: "w-4 h-4",
medium: "w-6 h-6",
large: "w-8 h-8",
};
const checkboxSize = sizeStyles[size] || sizeStyles.medium;
return (
<label
className={clsx(
"flex items-center cursor-pointer space-x-2",
className
)}
>
<div
className={clsx(
"relative flex items-center justify-center rounded-sm",
"bg-background-variant shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
checkboxSize,
{
"bg-gray-300": disabled,
"bg-blue-500": checked && !disabled,
}
)}
>
<input
type="checkbox"
checked={checked}
onChange={(e) => onChange && onChange(e)}
disabled={disabled}
className="absolute opacity-0 cursor-pointer"
{...rest}
/>
<div
className={clsx(
"absolute rounded-sm transition-colors duration-300",
checked ? "bg-blue-500" : "bg-gray-300",
checkboxSize
)}
>
{checked && !disabled && (
<svg
className="w-full h-full text-white"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 12l4 4 8-8"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)}
</div>
</div>
{label && <span className={clsx("text-base", disabled && "text-gray-500")}>{label}</span>}
</label>
);
};
export default Checkbox;
"use client";
import React from "react";
import clsx from "clsx";
type SizeTypes = "small" | "medium" | "large";
type SelectOption = {
value: string | number;
label: string;
};
type SelectProps = React.SelectHTMLAttributes<HTMLSelectElement> & {
options: SelectOption[];
fieldSize?: SizeTypes;
className?: string;
};
// Explicitly typing the sizeStyles keys
const sizeStyles: Record<SizeTypes, string> = {
small: "px-2 py-1 text-sm",
medium: "px-3 py-2 text-base",
large: "px-4 py-3 text-lg",
};
const Select: React.FC<SelectProps> = ({
options,
fieldSize = "medium",
disabled = false,
className,
...rest
}) => {
return (
<div
className={clsx(
"relative inline-block rounded-full",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
className
)}
>
<select
{...rest}
disabled={disabled}
className={clsx(
"appearance-none rounded-full focus:outline-none w-full",
sizeStyles[fieldSize], // Use the correct size styles
"bg-background-variant text-gray-700 cursor-pointer",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]", // Inner shadow
disabled ? "bg-gray-300 cursor-not-allowed text-gray-500" : ""
)}
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
{/* Arrow Icon */}
<div
className={clsx(
"absolute top-1/2 right-3 transform -translate-y-1/2",
"text-gray-500",
"pointer-events-none"
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-4 h-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M6 9l6 6 6-6" />
</svg>
</div>
</div>
);
};
export default Select;
"use client";
import { useState } from "react";
import FeatureContainer from "./FeatureContainer";
import TextField from "@/components/TextField";
import { BiLock, BiUser } from "react-icons/bi";
import TextArea from "@/components/TextArea";
import Checkbox from "@/components/CheckBox";
import DatePicker from "@/components/DatePicker";
import TimePicker from "@/components/TimePicker";
import Select from "@/components/Select";
export default function FormFieldsDemo() {
const [value1, setValue1] = useState("");
const [value2, setValue2] = useState("");
const [value3, setValue3] = useState("");
const [checked, setChecked] = useState(false);
const [selectedValue1, setSelectedValue1] = useState("option1");
const [selectedValue2, setSelectedValue2] = useState("option1");
const [selectedValue3, setSelectedValue3] = useState("option1");
const options = [
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
];
return (
<FeatureContainer title="Form Fields" id="Form">
<div className="space-y-4">
<TextField
startIcon={<BiUser />}
value={value1}
onChange={(e) => setValue1(e.target.value)}
placeholder="Username"
/>
<TextField
startIcon={<BiLock />}
value={value2}
onChange={(e) => setValue2(e.target.value)}
placeholder="Password"
type="password"
/>
<TextArea
value={value3}
onChange={(e) => setValue3(e.target.value)}
placeholder="Remarks"
rows={6}
/>
<Checkbox
label="I agree with terms and conditions"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>
<div>
<Select
value={selectedValue1}
onChange={(e) => setSelectedValue1(e.target.value)}
options={options}
fieldSize="small"
className="w-64"
/>
</div>
<div>
<Select
value={selectedValue2}
onChange={(e) => setSelectedValue2(e.target.value)}
options={options}
fieldSize="medium"
className="w-64"
/>
</div>
<div>
<Select
value={selectedValue3}
onChange={(e) => setSelectedValue3(e.target.value)}
options={options}
fieldSize="large"
className="w-64"
/>
</div>
</div>
</FeatureContainer>
);
}
import React from "react";
import clsx from "clsx";
type DatePickerProps = {
value: Date;
onChange: (value: Date) => void;
label?: string;
disabled?: boolean;
className?: string;
};
const formatDate = (date: Date): string => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const parseDate = (dateString: string): Date => {
const [year, month, day] = dateString.split("-").map(Number);
return new Date(year, month - 1, day);
};
const DatePicker: React.FC<DatePickerProps> = ({
value,
onChange,
label,
disabled = false,
className,
}) => {
return (
<div className={clsx("inline-flex flex-col space-y-2", className)}>
{label && (
<label
className={clsx(
"text-sm font-medium",
disabled ? "text-gray-500" : "text-gray-700"
)}
>
{label}
</label>
)}
<div
className={clsx(
"relative flex items-center px-4 py-2 rounded-lg",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.1),inset_-2px_-2px_5px_rgba(255,255,255,0.7)]",
disabled
? "bg-gray-100 cursor-not-allowed border-gray-300"
: "bg-background-variant"
)}
>
<input
type="date"
value={formatDate(value)}
onChange={(e) => onChange(parseDate(e.target.value))}
disabled={disabled}
className={clsx(
"w-full bg-transparent outline-none text-gray-700",
disabled ? "text-gray-500" : "text-gray-700",
"appearance-none focus:outline-none"
)}
/>
<div className="absolute inset-0 rounded-lg bg-transparent pointer-events-none" />
</div>
</div>
);
};
export default DatePicker;
import React from "react";
import clsx from "clsx";
type TimePickerProps = {
value: Date;
onChange: (value: Date) => void;
label?: string;
disabled?: boolean;
className?: string;
};
const formatTime = (date: Date): string => {
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${hours}:${minutes}`;
};
const parseTime = (timeString: string, currentDate: Date): Date => {
const [hours, minutes] = timeString.split(":").map(Number);
const newDate = new Date(currentDate);
newDate.setHours(hours);
newDate.setMinutes(minutes);
return newDate;
};
const TimePicker: React.FC<TimePickerProps> = ({
value,
onChange,
label,
disabled = false,
className,
}) => {
return (
<div className={clsx("inline-flex flex-col space-y-2", className)}>
{label && (
<label
className={clsx(
"text-sm font-medium",
disabled ? "text-gray-500" : "text-gray-700"
)}
>
{label}
</label>
)}
<div
className={clsx(
"relative flex items-center px-4 py-2 rounded-lg",
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.1),inset_-2px_-2px_5px_rgba(255,255,255,0.7)]",
disabled
? "bg-gray-100 cursor-not-allowed border-gray-300"
: "bg-background-variant"
)}
>
<input
type="time"
value={formatTime(value)}
onChange={(e) => onChange(parseTime(e.target.value, value))}
disabled={disabled}
className={clsx(
"w-full bg-transparent outline-none text-gray-700",
disabled ? "text-gray-500" : "text-gray-700",
"appearance-none focus:outline-none"
)}
/>
<div className="absolute inset-0 rounded-lg bg-transparent pointer-events-none" />
</div>
</div>
);
};
export default TimePicker;
"use client";
import { useState } from "react";
import FeatureContainer from "./FeatureContainer";
import DatePicker from "@/components/DatePicker";
import TimePicker from "@/components/TimePicker";
export default function DateTimePickerDemo() {
const [date, setDate] = useState(new Date());
const [time, setTime] = useState(new Date());
return (
<FeatureContainer title="Date and Time Pickers" id="DateTime">
<div className="space-y-4">
<div>
<DatePicker value={date} onChange={(val) => setDate(val)} />
</div>
<div>
<TimePicker value={time} onChange={(val) => setTime(val)} />
</div>
</div>
</FeatureContainer>
);
}
// components/Card.tsx
import React, { ReactNode } from "react";
import clsx from "clsx";
type Elevation = "none" | "outside" | "inside" | "mix";
type Variant = "default" | "air" | "lcd";
type CardProps = {
children: ReactNode;
className?: string;
elevation?: Elevation;
variant?: Variant;
rounded?: boolean;
};
const Card: React.FC<CardProps> = ({
children,
className = "",
elevation = "none",
variant = "default",
rounded = true,
}) => {
const elevationClasses = {
none: "",
outside:
"shadow-[2px_2px_8px_rgba(0,0,0,0.15),-2px_-2px_8px_rgba(255,255,255,0.7)]",
inside:
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
mix: "shadow-[0_4px_6px_rgba(0,0,0,0.1),0_1px_3px_rgba(0,0,0,0.1),inset_1px_1px_3px_rgba(255,255,255,0.7),inset_-1px_-1px_3px_rgba(0,0,0,0.1)]",
};
const variantClasses = {
default:
"bg-gradient-to-r from-background-card to-background-card-secondary border border-muted/20",
air: "bg-gradient-to-r from-background-card-secondary to-background-card border border-muted/20",
lcd: "bg-background-card-lcd border border-muted-dark",
};
return (
<div
className={clsx(
"p-6",
elevationClasses[elevation],
variantClasses[variant],
rounded ? "rounded-2xl" : "",
"inline-block",
className
)}
>
{children}
</div>
);
};
export default Card;
"use client";
import FeatureContainer from "./FeatureContainer";
import Card from "@/components/Card";
export default function CardsDemo() {
return (
<FeatureContainer title="Cards" id="Card">
<div className="gap-4 text-sm font-medium flex flex-wrap text-center">
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="default"
elevation="none"
>
Default
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="default"
elevation="inside"
>
Default (Inside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="default"
elevation="outside"
>
Default (Outside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="default"
elevation="mix"
>
Default (Mix)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="air"
elevation="none"
>
Air
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="default"
elevation="inside"
>
Air (Inside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="air"
elevation="outside"
>
Air (Outside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="air"
elevation="mix"
>
Air (Mix)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="lcd"
elevation="none"
>
LCD
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="lcd"
elevation="inside"
>
LCD (Inside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="lcd"
elevation="outside"
>
LCD (Outside)
</Card>
</div>
<div>
<Card
className="w-28 h-28 flex items-center justify-center"
variant="lcd"
elevation="mix"
>
LCD (Mix)
</Card>
</div>
</div>
</FeatureContainer>
);
}
import React, { ReactNode } from "react";
import clsx from "clsx";
type Elevation = "none" | "outside" | "inside" | "mix";
type ChipProps = {
children: ReactNode;
className?: string;
elevation?: Elevation;
rounded?: boolean;
};
const Chip: React.FC<ChipProps> = ({
children,
className = "",
elevation = "none",
rounded = true,
}) => {
const elevationClasses = {
none: "",
outside:
"shadow-[2px_2px_8px_rgba(0,0,0,0.15),-2px_-2px_8px_rgba(255,255,255,0.7)]",
inside:
"shadow-[inset_2px_2px_5px_rgba(0,0,0,0.15),inset_-2px_-2px_5px_rgba(255,255,255,0.6)]",
mix: "shadow-[0_4px_6px_rgba(0,0,0,0.1),0_1px_3px_rgba(0,0,0,0.1),inset_1px_1px_3px_rgba(255,255,255,0.7),inset_-1px_-1px_3px_rgba(0,0,0,0.1)]",
};
return (
<div
className={clsx(
"px-6 py-2",
elevationClasses[elevation],
"bg-gradient-to-r from-background-card to-background-card-secondary border border-muted/20",
rounded ? "rounded-full" : "rounded-sm",
"inline-block",
className
)}
>
{children}
</div>
);
};
export default Chip;
import Chip from "@/components/Chip";
import FeatureContainer from "./FeatureContainer";
export default function ChipsDemo() {
return (
<FeatureContainer title="Chips" id="Chip">
<div className="space-y-4">
<div className="gap-4 font-medium flex flex-wrap text-center text-sm text-muted">
<Chip>Default</Chip>
<Chip elevation="inside">Inside</Chip>
<Chip elevation="outside">Outside</Chip>
<Chip elevation="mix">Mix</Chip>
</div>
<div className="gap-4 font-medium flex flex-wrap text-center text-sm text-muted">
<Chip rounded={false}>Default</Chip>
<Chip rounded={false} elevation="inside">
Inside
</Chip>
<Chip rounded={false} elevation="outside">
Outside
</Chip>
<Chip rounded={false} elevation="mix">
Mix
</Chip>
</div>
</div>
</FeatureContainer>
);
}
"use client";
import React from "react";
type IconProps = {
icon: React.ReactElement;
size?: number;
className?: string;
onClick?: () => void;
};
const Icon: React.FC<IconProps> = ({
icon,
size = 24,
className = "",
onClick,
}) => {
const IconElement = React.cloneElement(icon, {
size,
className: `inline-block ${className}`,
style: {
filter:
"drop-shadow(1px 1px 2px rgba(255, 255, 255, 0.7)) drop-shadow(-1px -1px 2px rgba(0, 0, 0, 0.2))",
},
});
return (
<div
className="inline-flex items-center justify-center"
style={{ width: size, height: size }}
onClick={() => onClick?.()}
>
{IconElement}
</div>
);
};
export default Icon;
import Icon from "@/components/Icon";
import FeatureContainer from "./FeatureContainer";
import { BiBell, BiChat, BiUser } from "react-icons/bi";
export default function IconsDemo() {
return (
<FeatureContainer title="Icons" id="Icon">
<div className="space-y-4">
<div className="gap-4 font-medium flex flex-wrap text-muted">
<Icon icon={<BiBell />} size={70} />
<Icon icon={<BiUser />} size={70} />
<Icon icon={<BiChat />} size={70} />
</div>
<div className="gap-4 font-medium flex flex-wrap text-muted">
<Icon icon={<BiBell />} size={50} />
<Icon icon={<BiUser />} size={50} />
<Icon icon={<BiChat />} size={50} />
</div>
<div className="gap-4 font-medium flex flex-wrap text-muted">
<Icon icon={<BiBell />} size={30} />
<Icon icon={<BiUser />} size={30} />
<Icon icon={<BiChat />} size={30} />
</div>
</div>
</FeatureContainer>
);
}
Body 1
Body 2
Caption
import React, { ReactNode } from "react";
import clsx from "clsx";
type TypographyProps = {
variant?: "h1" | "h2" | "h3" | "body1" | "body2" | "caption";
children: ReactNode;
className?: string;
noShadow?: boolean;
};
const Typography: React.FC<TypographyProps> = ({
variant = "body1",
children,
className = "",
noShadow = false,
}) => {
const baseStyles = {
h1: "text-2xl sm:text-5xl font-medium text-text",
h2: "text-xl sm:text-3xl font-medium text-text",
h3: "text-lg sm:text-2xl font-medium text-text",
body1: "text-base font-normal text-text",
body2: "text-sm font-light text-muted",
caption: "text-xs font-light text-gray-500 text-muted",
};
const shadowClass = noShadow ? "" : "text-shadow";
const Component =
variant === "h1" || variant === "h2" || variant === "h3" ? variant : "p";
return (
<Component className={clsx(baseStyles[variant], shadowClass, className)}>
{children}
</Component>
);
};
export default Typography;
import FeatureContainer from "./FeatureContainer";
import Typography from "@/components/Typography";
export default function TypographyDemo() {
return (
<FeatureContainer title="Typography" id="Typography">
<div className="space-y-4">
<Typography variant="h1">Heading 1</Typography>
<Typography variant="h2">Heading 2</Typography>
<Typography variant="h3">Heading 3</Typography>
<Typography variant="body1">Body 1</Typography>
<Typography variant="body2">Body 2</Typography>
<Typography variant="caption">Caption</Typography>
</div>
</FeatureContainer>
);
}
import React from "react";
import clsx from "clsx";
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
label: string;
size?: "small" | "medium" | "large";
loading?: boolean;
disabled?: boolean;
className?: string;
};
// Size styles
const sizeStyles = {
small: "px-4 py-2 text-sm",
medium: "px-6 py-3 text-base",
large: "px-8 py-4 text-lg",
};
const Button: React.FC<ButtonProps> = ({
label,
size = "medium",
loading = false,
disabled = false,
className,
...rest
}) => {
return (
<button
{...rest}
disabled={disabled || loading}
className={clsx(
"rounded-md focus:outline-none transition-all duration-300",
sizeStyles[size],
"relative inline-flex justify-center items-center",
"shadow-[2px_2px_8px_rgba(0,0,0,0.1),-2px_-2px_8px_rgba(255,255,255,0.7)]", // Neumorphism shadow
disabled || loading
? "bg-gray-300 text-gray-500 cursor-not-allowed"
: "bg-background-variant text-gray-700 hover:shadow-md",
className
)}
>
{loading ? (
<div className="w-5 h-5 border-4 border-t-transparent border-gray-400 rounded-full animate-spin"></div> // Loading spinner
) : (
<span>{label}</span>
)}
</button>
);
};
export default Button;
"use client";
import Button from "@/components/Button";
import FeatureContainer from "./FeatureContainer";
import { useState } from "react";
export default function ButtonDemo() {
const [loading, setLoading] = useState(false);
const handleClick = () => {
setLoading(true);
setTimeout(() => setLoading(false), 2000); // Simulate a loading state for 2 seconds
};
return (
<FeatureContainer title="Buttons" id="Button">
<div className="space-y-4">
<div>
<Button
label="Submit"
size="medium"
loading={loading}
onClick={handleClick}
className="w-32"
/>
</div>
<div>
<Button label="Cancel" size="small" className="w-24" />
</div>
<div>
<Button label="Large Button" size="large" className="w-fit" />
</div>
</div>
</FeatureContainer>
);
}