diff --git a/app/page.tsx b/app/page.tsx
index 277852d..da2f0ca 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,9 +1,14 @@
+import { Typography } from "@/components/elements"
+
const Home: React.FC = () => {
return (
-
- Start prompting
-
+
)
}
diff --git a/components/elements/Button.config.tsx b/components/elements/Button.config.tsx
new file mode 100644
index 0000000..6c15289
--- /dev/null
+++ b/components/elements/Button.config.tsx
@@ -0,0 +1,38 @@
+import type { ElementConfig } from "./types"
+
+export const ButtonConfig: ElementConfig = {
+ Button: {
+ label: "Button",
+ fields: {
+ children: {
+ type: "text",
+ label: "Label",
+ },
+ variant: {
+ type: "select",
+ label: "Variant",
+ options: [
+ { label: "Default", value: "default" },
+ { label: "Destructive", value: "destructive" },
+ { label: "Outline", value: "outline" },
+ { label: "Secondary", value: "secondary" },
+ { label: "Ghost", value: "ghost" },
+ { label: "Link", value: "link" },
+ ],
+ },
+ size: {
+ type: "select",
+ label: "Size",
+ options: [
+ { label: "Default", value: "default" },
+ { label: "Extra Small", value: "xs" },
+ { label: "Small", value: "sm" },
+ { label: "Large", value: "lg" },
+ { label: "Icon", value: "icon" },
+ ],
+ },
+ },
+ },
+}
+
+export default ButtonConfig
diff --git a/components/elements/Button.tsx b/components/elements/Button.tsx
new file mode 100644
index 0000000..65dc458
--- /dev/null
+++ b/components/elements/Button.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+
+import {
+ Button as ShadcnButton,
+ type buttonVariants,
+} from "@/components/ui/button"
+import type { VariantProps } from "class-variance-authority"
+
+export type ButtonVariant = NonNullable<
+ VariantProps["variant"]
+>
+export type ButtonSize = NonNullable<
+ VariantProps["size"]
+>
+
+export interface ButtonProps
+ extends React.ComponentProps {
+ variant?: ButtonVariant
+ size?: ButtonSize
+}
+
+function Button({ variant = "default", size = "default", ...props }: ButtonProps) {
+ return
+}
+
+export { Button }
diff --git a/components/elements/Card.config.tsx b/components/elements/Card.config.tsx
new file mode 100644
index 0000000..30917a7
--- /dev/null
+++ b/components/elements/Card.config.tsx
@@ -0,0 +1,33 @@
+import type { ElementConfig } from "./types"
+
+export const CardConfig: ElementConfig = {
+ Card: {
+ label: "Card",
+ fields: {
+ image: {
+ type: "text",
+ label: "Image URL",
+ },
+ title: {
+ type: "text",
+ label: "Title",
+ },
+ subtitle: {
+ type: "text",
+ label: "Subtitle",
+ },
+ tags: {
+ type: "array",
+ label: "Tags",
+ arrayFields: {
+ tag: {
+ type: "text",
+ label: "Tag",
+ },
+ },
+ },
+ },
+ },
+}
+
+export default CardConfig
diff --git a/components/elements/Card.tsx b/components/elements/Card.tsx
new file mode 100644
index 0000000..ac9c65d
--- /dev/null
+++ b/components/elements/Card.tsx
@@ -0,0 +1,73 @@
+import * as React from "react"
+
+import {
+ Card as ShadcnCard,
+ CardContent,
+ CardHeader,
+} from "@/components/ui/card"
+import { Badge } from "@/components/ui/badge"
+import { cn } from "@/lib/utils"
+import { Image } from "./Image"
+import { Typography } from "./Typography"
+
+export interface CardTag {
+ tag: string
+}
+
+export interface CardProps extends React.ComponentProps {
+ image?: string
+ title?: string
+ subtitle?: string
+ tags?: CardTag[]
+}
+
+function Card({
+ image,
+ title,
+ subtitle,
+ tags,
+ className,
+ ...props
+}: CardProps) {
+ return (
+
+ {image ? (
+
+ ) : null}
+
+
+ {title ? : null}
+ {subtitle ? (
+
+ ) : null}
+
+
+ {tags && tags.length > 0 ? (
+
+ {tags.map((item, index) => (
+
+ {item.tag}
+
+ ))}
+
+ ) : null}
+
+ )
+}
+
+export { Card }
diff --git a/components/elements/Icon.config.tsx b/components/elements/Icon.config.tsx
new file mode 100644
index 0000000..dbc1218
--- /dev/null
+++ b/components/elements/Icon.config.tsx
@@ -0,0 +1,23 @@
+import type { ElementConfig } from "./types"
+
+export const IconConfig: ElementConfig = {
+ Icon: {
+ label: "Icon",
+ fields: {
+ name: {
+ type: "icon",
+ label: "Icon",
+ },
+ size: {
+ type: "text",
+ label: "Size",
+ },
+ className: {
+ type: "text",
+ label: "Class Name",
+ },
+ },
+ },
+}
+
+export default IconConfig
diff --git a/components/elements/Icon.tsx b/components/elements/Icon.tsx
new file mode 100644
index 0000000..7ec2741
--- /dev/null
+++ b/components/elements/Icon.tsx
@@ -0,0 +1,25 @@
+import * as React from "react"
+import { DynamicIcon, type IconName } from "lucide-react/dynamic"
+
+import { cn } from "@/lib/utils"
+
+export interface IconProps
+ extends Omit, "name"> {
+ name: IconName
+ size?: number
+ className?: string
+}
+
+function Icon({ name, size = 24, className, ...props }: IconProps) {
+ return (
+
+ )
+}
+
+export { Icon }
+export type { IconName }
diff --git a/components/elements/Image.config.tsx b/components/elements/Image.config.tsx
new file mode 100644
index 0000000..f362ccf
--- /dev/null
+++ b/components/elements/Image.config.tsx
@@ -0,0 +1,42 @@
+import type { ElementConfig } from "./types"
+
+export const ImageConfig: ElementConfig = {
+ Image: {
+ label: "Image",
+ fields: {
+ src: {
+ type: "text",
+ label: "Source URL",
+ },
+ alt: {
+ type: "text",
+ label: "Alt Text",
+ },
+ objectFit: {
+ type: "select",
+ label: "Object Fit",
+ options: [
+ { label: "Cover", value: "cover" },
+ { label: "Contain", value: "contain" },
+ { label: "Fill", value: "fill" },
+ { label: "None", value: "none" },
+ { label: "Scale Down", value: "scale-down" },
+ ],
+ },
+ circle: {
+ type: "boolean",
+ label: "Circle",
+ },
+ width: {
+ type: "text",
+ label: "Width",
+ },
+ height: {
+ type: "text",
+ label: "Height",
+ },
+ },
+ },
+}
+
+export default ImageConfig
diff --git a/components/elements/Image.tsx b/components/elements/Image.tsx
new file mode 100644
index 0000000..1a24cc6
--- /dev/null
+++ b/components/elements/Image.tsx
@@ -0,0 +1,56 @@
+import * as React from "react"
+import NextImage from "next/image"
+
+import { cn } from "@/lib/utils"
+
+export type ObjectFit =
+ | "contain"
+ | "cover"
+ | "fill"
+ | "none"
+ | "scale-down"
+
+const objectFitStyles: Record = {
+ contain: "object-contain",
+ cover: "object-cover",
+ fill: "object-fill",
+ none: "object-none",
+ "scale-down": "object-scale-down",
+}
+
+export interface ImageProps {
+ src: string
+ alt?: string
+ objectFit?: ObjectFit
+ circle?: boolean
+ height?: number
+ width?: number
+ className?: string
+}
+
+function Image({
+ src,
+ alt = "",
+ objectFit = "cover",
+ circle = false,
+ height = 300,
+ width = 300,
+ className,
+}: ImageProps) {
+ return (
+
+ )
+}
+
+export { Image }
diff --git a/components/elements/Typography.config.tsx b/components/elements/Typography.config.tsx
new file mode 100644
index 0000000..ce14044
--- /dev/null
+++ b/components/elements/Typography.config.tsx
@@ -0,0 +1,59 @@
+import type { ElementConfig } from "./types"
+
+export const TypographyConfig: ElementConfig = {
+ Typography: {
+ label: "Typography",
+ fields: {
+ text: {
+ type: "textarea",
+ label: "Text",
+ },
+ variant: {
+ type: "select",
+ label: "Variant",
+ options: [
+ { label: "Heading 1", value: "h1" },
+ { label: "Heading 2", value: "h2" },
+ { label: "Heading 3", value: "h3" },
+ { label: "Heading 4", value: "h4" },
+ { label: "Heading 5", value: "h5" },
+ { label: "Heading 6", value: "h6" },
+ { label: "Body 1", value: "body1" },
+ { label: "Body 2", value: "body2" },
+ { label: "Caption", value: "caption" },
+ ],
+ },
+ textAlign: {
+ type: "select",
+ label: "Text Align",
+ options: [
+ { label: "Left", value: "left" },
+ { label: "Center", value: "center" },
+ { label: "Right", value: "right" },
+ { label: "Justify", value: "justify" },
+ ],
+ },
+ fontWeight: {
+ type: "select",
+ label: "Font Weight",
+ options: [
+ { label: "Thin", value: "thin" },
+ { label: "Extra Light", value: "extralight" },
+ { label: "Light", value: "light" },
+ { label: "Normal", value: "normal" },
+ { label: "Medium", value: "medium" },
+ { label: "Semibold", value: "semibold" },
+ { label: "Bold", value: "bold" },
+ { label: "Extra Bold", value: "extrabold" },
+ { label: "Black", value: "black" },
+ ],
+ },
+ className: {
+ type: "text",
+ label: "Class Name",
+ },
+ },
+ },
+}
+
+export default TypographyConfig
diff --git a/components/elements/Typography.tsx b/components/elements/Typography.tsx
new file mode 100644
index 0000000..ad0e3ac
--- /dev/null
+++ b/components/elements/Typography.tsx
@@ -0,0 +1,120 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export type TypographyVariant =
+ | "h1"
+ | "h2"
+ | "h3"
+ | "h4"
+ | "h5"
+ | "h6"
+ | "body1"
+ | "body2"
+ | "caption"
+
+export type TypographyAlign = "left" | "center" | "right" | "justify"
+
+export type TypographyWeight =
+ | "thin"
+ | "extralight"
+ | "light"
+ | "normal"
+ | "medium"
+ | "semibold"
+ | "bold"
+ | "extrabold"
+ | "black"
+
+const variantStyles: Record = {
+ h1: "font-heading scroll-m-20 text-4xl tracking-tight text-balance lg:text-5xl",
+ h2: "font-heading scroll-m-20 text-3xl tracking-tight",
+ h3: "font-heading scroll-m-20 text-2xl tracking-tight",
+ h4: "font-heading scroll-m-20 text-xl tracking-tight",
+ h5: "font-heading scroll-m-20 text-lg tracking-tight",
+ h6: "font-heading scroll-m-20 text-base tracking-tight",
+ body1: "leading-7 text-base",
+ body2: "leading-6 text-sm",
+ caption: "text-xs text-muted-foreground",
+}
+
+const variantElement: Record = {
+ h1: "h1",
+ h2: "h2",
+ h3: "h3",
+ h4: "h4",
+ h5: "h5",
+ h6: "h6",
+ body1: "p",
+ body2: "p",
+ caption: "span",
+}
+
+const weightStyles: Record = {
+ thin: "font-thin",
+ extralight: "font-extralight",
+ light: "font-light",
+ normal: "font-normal",
+ medium: "font-medium",
+ semibold: "font-semibold",
+ bold: "font-bold",
+ extrabold: "font-extrabold",
+ black: "font-black",
+}
+
+const alignStyles: Record = {
+ left: "text-left",
+ center: "text-center",
+ right: "text-right",
+ justify: "text-justify",
+}
+
+const defaultWeight: Record = {
+ h1: "bold",
+ h2: "bold",
+ h3: "bold",
+ h4: "bold",
+ h5: "bold",
+ h6: "bold",
+ body1: "normal",
+ body2: "normal",
+ caption: "normal",
+}
+
+export interface TypographyProps
+ extends Omit, "children"> {
+ text?: React.ReactNode
+ variant?: TypographyVariant
+ textAlign?: TypographyAlign
+ fontWeight?: TypographyWeight
+ className?: string
+}
+
+function Typography({
+ text,
+ variant = "body1",
+ textAlign,
+ fontWeight,
+ className,
+ ...props
+}: TypographyProps) {
+ const Comp = variantElement[variant]
+
+ return (
+
+ {text}
+
+ )
+}
+
+export { Typography }
diff --git a/components/elements/index.ts b/components/elements/index.ts
new file mode 100644
index 0000000..8cb1550
--- /dev/null
+++ b/components/elements/index.ts
@@ -0,0 +1,26 @@
+export * from "./types"
+
+export { Typography } from "./Typography"
+export type {
+ TypographyProps,
+ TypographyVariant,
+ TypographyAlign,
+ TypographyWeight,
+} from "./Typography"
+export { TypographyConfig } from "./Typography.config"
+
+export { Button } from "./Button"
+export type { ButtonProps, ButtonVariant, ButtonSize } from "./Button"
+export { ButtonConfig } from "./Button.config"
+
+export { Image } from "./Image"
+export type { ImageProps, ObjectFit } from "./Image"
+export { ImageConfig } from "./Image.config"
+
+export { Icon } from "./Icon"
+export type { IconProps, IconName } from "./Icon"
+export { IconConfig } from "./Icon.config"
+
+export { Card } from "./Card"
+export type { CardProps, CardTag } from "./Card"
+export { CardConfig } from "./Card.config"
diff --git a/components/elements/types.ts b/components/elements/types.ts
new file mode 100644
index 0000000..a4648b3
--- /dev/null
+++ b/components/elements/types.ts
@@ -0,0 +1,29 @@
+export type FieldType =
+ | "text"
+ | "textarea"
+ | "color"
+ | "icon"
+ | "array"
+ | "object"
+ | "boolean"
+ | "select"
+
+export interface FieldOption {
+ label: string
+ value: string
+}
+
+export interface FieldConfig {
+ type: FieldType
+ label?: string
+ options?: FieldOption[]
+ arrayFields?: Record
+ objectFields?: Record
+}
+
+export interface ComponentConfig {
+ label: string
+ fields: Record
+}
+
+export type ElementConfig = Record