fulldev-ui 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Fulldev UI
2
2
 
3
+ ## 0.9.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e641911`](https://github.com/fulldotdev/ui/commit/e64191143efe448a46999d63ca7ed6d8bd320d1e) Thanks [@silveltman](https://github.com/silveltman)! - Add the gradient component to the registry, keep Lucide icons unfilled, and fix marquee spacing during looping animations.
8
+
3
9
  ## 0.9.2
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fulldev-ui",
3
3
  "type": "module",
4
- "version": "0.9.2",
4
+ "version": "0.9.3",
5
5
  "description": "Open-source Astro UI components and blocks",
6
6
  "homepage": "https://ui.full.dev",
7
7
  "bugs": "https://github.com/fulldotdev/ui/issues",
@@ -12,12 +12,6 @@
12
12
  "type": "registry:file",
13
13
  "target": "src/content.config.ts"
14
14
  },
15
- {
16
- "path": "src/components/layout-renderer.astro",
17
- "content": "---\n// Keep this renderer generic and stable. New page types should be wired by\n// adding a matching schema and src/layouts/<type>.astro file, not by adding\n// layout-specific branches here.\nimport { getEntry, render, type CollectionEntry } from \"astro:content\"\n\ntype Props = CollectionEntry<\"pages\">\n\nconst page = Astro.props\nconst pageData = page.data\nconst { Content, headings } = await render(page)\nconst global = await getEntry(\"globals\", Astro.currentLocale || \"en\")\nif (!global) throw new Error(\"Add a global to content/globals\")\nconst globalData = global.data\n\nconst layoutImports = import.meta.glob(\"../layouts/*.astro\", { eager: true })\nconst layoutPath = `../layouts/${pageData.type}.astro`\nconst layoutImport = layoutImports[layoutPath] as { default?: any } | undefined\nconst Layout = layoutImport?.default\n\nif (!Layout) throw new Error(`Add a layout for page type \"${pageData.type}\"`)\n---\n\n<Layout global={globalData} page={pageData} headings={headings}>\n <Content />\n</Layout>\n",
18
- "type": "registry:file",
19
- "target": "src/components/layout-renderer.astro"
20
- },
21
15
  {
22
16
  "path": "src/lib/pages.ts",
23
17
  "content": "import { getCollection, type CollectionEntry } from \"astro:content\"\n\ntype Page = CollectionEntry<\"pages\">\n\nexport type PageSearchItem = {\n label: string\n href: string\n title: string\n path: string\n description: string\n group: string\n}\n\nexport type PageBreadcrumbItem = {\n label: string\n href: string\n}\n\nexport const normalizePath = (path: string) => {\n if (path === \"/\") return path\n\n return path.replace(/\\/$/, \"\")\n}\n\nexport const getPageHref = (page: Page) =>\n page.id === \"index\" ? \"/\" : `/${page.id}/`\n\nexport const formatSlug = (slug: string) =>\n slug\n .split(\"-\")\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \")\n\nconst getPageSearchGroup = (href: string) => {\n const [slug] = href.split(\"/\").filter(Boolean)\n\n return slug ? formatSlug(slug) : \"Overview\"\n}\n\nexport const getPageSearchItems = async (): Promise<PageSearchItem[]> => {\n const pages = await getCollection(\"pages\")\n\n return pages\n .map((page) => ({\n label: page.data.title,\n href: getPageHref(page),\n title: page.data.title,\n path: getPageHref(page),\n description: page.data.description,\n group: getPageSearchGroup(getPageHref(page)),\n }))\n .sort((a, b) => a.label.localeCompare(b.label))\n}\n\nexport const getPageBreadcrumbItems = async (\n pathname: string\n): Promise<PageBreadcrumbItem[]> => {\n const currentPath = normalizePath(pathname)\n const pages = await getCollection(\"pages\")\n const pageTitlesByHref = new Map(\n pages.map((page) => [getPageHref(page), page.data.title])\n )\n const homeLink = {\n label: pageTitlesByHref.get(\"/\") ?? \"Home\",\n href: \"/\",\n }\n\n if (currentPath === \"/\") return [homeLink]\n\n return [\n homeLink,\n ...currentPath\n .split(\"/\")\n .filter(Boolean)\n .map((slug, index, slugs) => {\n const href = `/${slugs.slice(0, index + 1).join(\"/\")}/`\n\n return {\n label: pageTitlesByHref.get(href) ?? formatSlug(slug),\n href,\n }\n }),\n ]\n}\n",
@@ -26,7 +20,7 @@
26
20
  },
27
21
  {
28
22
  "path": "src/pages/[...page].astro",
29
- "content": "---\nimport { getCollection } from \"astro:content\"\n\nimport LayoutRenderer from \"@/components/layout-renderer.astro\"\n\nexport async function getStaticPaths() {\n const pages = await getCollection(\"pages\")\n return pages.map((page) => {\n return {\n params: {\n page: page.id === \"index\" ? undefined : page.id,\n },\n props: page,\n }\n })\n}\n---\n\n<LayoutRenderer {...Astro.props} />\n",
23
+ "content": "---\nimport { getCollection, getEntry, render } from \"astro:content\"\n\nexport const prerender = true\n\nexport async function getStaticPaths() {\n const pages = await getCollection(\"pages\")\n return pages.map((page) => {\n return {\n params: {\n page: page.id === \"index\" ? undefined : page.id,\n },\n props: page,\n }\n })\n}\n\nconst page = Astro.props\nconst pageData = page.data\nconst { Content, headings } = await render(page)\nconst global = await getEntry(\"globals\", Astro.currentLocale || \"en\")\nif (!global) throw new Error(\"Add a global to content/globals\")\nconst globalData = global.data\n\nconst layoutImports = import.meta.glob(\"../layouts/*.astro\", { eager: true })\nconst layoutPath = `../layouts/${pageData.type}.astro`\nconst layoutImport = layoutImports[layoutPath] as { default?: any } | undefined\nconst Layout = layoutImport?.default\n\nif (!Layout) throw new Error(`Add a layout for page type \"${pageData.type}\"`)\n---\n\n<Layout global={globalData} page={pageData} headings={headings}>\n <Content />\n</Layout>\n",
30
24
  "type": "registry:file",
31
25
  "target": "src/pages/[...page].astro"
32
26
  },
@@ -19,6 +19,7 @@
19
19
  "@fulldev/dropdown-menu",
20
20
  "@fulldev/empty",
21
21
  "@fulldev/field",
22
+ "@fulldev/gradient",
22
23
  "@fulldev/header",
23
24
  "@fulldev/hover-card",
24
25
  "@fulldev/input",
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "gradient",
4
+ "files": [
5
+ {
6
+ "path": "src/components/ui/gradient/gradient.astro",
7
+ "content": "---\nimport type { HTMLAttributes } from \"astro/types\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst gradientVariants = cva(\n \"bg-primary pointer-events-none absolute inset-0 opacity-50 [--gradient-stops:black,transparent]\",\n {\n variants: {\n variant: {\n radial:\n \"[mask-image:radial-gradient(ellipse_at_var(--gradient-position),var(--gradient-stops))] [-webkit-mask-image:radial-gradient(ellipse_at_var(--gradient-position),var(--gradient-stops))]\",\n linear:\n \"[mask-image:linear-gradient(var(--gradient-direction),var(--gradient-stops))] [-webkit-mask-image:linear-gradient(var(--gradient-direction),var(--gradient-stops))]\",\n },\n direction: {\n left: \"[--gradient-direction:to_left] [--gradient-position:0%_50%]\",\n right: \"[--gradient-direction:to_right] [--gradient-position:100%_50%]\",\n top: \"[--gradient-direction:to_top] [--gradient-position:50%_0%]\",\n bottom:\n \"[--gradient-direction:to_bottom] [--gradient-position:50%_100%]\",\n center:\n \"[--gradient-direction:to_bottom] [--gradient-position:50%_50%]\",\n },\n },\n defaultVariants: {\n variant: \"radial\",\n direction: \"bottom\",\n },\n }\n)\n\ntype Props = HTMLAttributes<\"div\"> & VariantProps<typeof gradientVariants>\n\nconst { class: className, variant, direction, ...props } = Astro.props\n---\n\n<div\n data-slot=\"gradient\"\n aria-hidden=\"true\"\n class={cn(gradientVariants({ variant, direction }), className)}\n {...props}\n>\n</div>\n",
8
+ "type": "registry:ui"
9
+ },
10
+ {
11
+ "path": "src/components/ui/gradient/index.ts",
12
+ "content": "export { default as Gradient } from \"./gradient.astro\"\n",
13
+ "type": "registry:ui"
14
+ }
15
+ ],
16
+ "type": "registry:ui"
17
+ }
@@ -8,7 +8,7 @@
8
8
  "files": [
9
9
  {
10
10
  "path": "src/components/ui/icon/icon.astro",
11
- "content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\ntype Props = Omit<HTMLAttributes<\"svg\">, \"name\" | \"href\"> & {\n href?: HTMLAttributes<\"a\">[\"href\"]\n name?: string | number | null\n}\n\nconst { name, href, ...props } = Astro.props\n\nconst hrefMap = {\n \"x.com\": \"x\",\n twitter: \"twitter\",\n facebook: \"facebook\",\n instagram: \"instagram\",\n pinterest: \"pinterest\",\n youtube: \"youtube\",\n tiktok: \"tiktok\",\n snapchat: \"snapchat\",\n reddit: \"reddit\",\n tumblr: \"tumblr\",\n \"wa.me\": \"whatsapp\",\n telegram: \"telegram\",\n discord: \"discord\",\n vimeo: \"vimeo\",\n flickr: \"flickr\",\n yelp: \"yelp\",\n spotify: \"spotify\",\n behance: \"behance\",\n dribbble: \"dribbble\",\n soundcloud: \"soundcloud\",\n github: \"github\",\n twitch: \"twitch\",\n \"tel:\": \"phone\",\n \"mailto:\": \"mail\",\n \"maps.app.goo.gl\": \"map-pin\",\n linkedin: \"linkedin\",\n} as const\n\nconst icons = import.meta.glob(\n [\n \"/node_modules/lucide-static/icons/*.svg\",\n \"/node_modules/simple-icons/icons/*.svg\",\n ],\n { eager: true }\n) as Record<string, { default: any }>\n\nconst foundPath = () => {\n let foundPath: string | undefined = undefined\n\n // For x.com, use the Simple Icons X instead of Lucide's X icon.\n if (name === \"x.com\") {\n foundPath = \"/node_modules/simple-icons/icons/x.svg\"\n } else if (name) {\n foundPath = Object.keys(icons).find((key) => key.includes(`/${name}.svg`))\n } else if (href) {\n const hrefKey = Object.keys(hrefMap).find((key) =>\n href.toString().includes(key)\n )\n const hrefValue = hrefMap[hrefKey as keyof typeof hrefMap]\n foundPath = Object.keys(icons)\n .reverse()\n .find((key) => key.includes(`/${hrefValue}.svg`))\n }\n\n return foundPath\n}\n\nconst iconPath = foundPath()\nconst module = iconPath ? icons[iconPath] : undefined\nconst Comp = module?.default\n---\n\n{\n Comp && foundPath()?.includes(\"simple-icons\") && (\n <Comp\n height={24}\n width={24}\n fill=\"currentColor\"\n class=\"size-[1em] text-base\"\n {...props}\n />\n )\n}\n\n{\n Comp && foundPath()?.includes(\"lucide-static\") && (\n <Comp height={24} width={24} class=\"size-[1em] text-base\" {...props} />\n )\n}\n",
11
+ "content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\ntype Props = Omit<HTMLAttributes<\"svg\">, \"name\" | \"href\"> & {\n href?: HTMLAttributes<\"a\">[\"href\"]\n name?: string | number | null\n}\n\nconst { name, href, ...props } = Astro.props\n\nconst hrefMap = {\n \"x.com\": \"x\",\n twitter: \"twitter\",\n facebook: \"facebook\",\n instagram: \"instagram\",\n pinterest: \"pinterest\",\n youtube: \"youtube\",\n tiktok: \"tiktok\",\n snapchat: \"snapchat\",\n reddit: \"reddit\",\n tumblr: \"tumblr\",\n \"wa.me\": \"whatsapp\",\n telegram: \"telegram\",\n discord: \"discord\",\n vimeo: \"vimeo\",\n flickr: \"flickr\",\n yelp: \"yelp\",\n spotify: \"spotify\",\n behance: \"behance\",\n dribbble: \"dribbble\",\n soundcloud: \"soundcloud\",\n github: \"github\",\n twitch: \"twitch\",\n \"tel:\": \"phone\",\n \"mailto:\": \"mail\",\n \"maps.app.goo.gl\": \"map-pin\",\n linkedin: \"linkedin\",\n} as const\n\nconst icons = import.meta.glob(\n [\n \"/node_modules/lucide-static/icons/*.svg\",\n \"/node_modules/simple-icons/icons/*.svg\",\n ],\n { eager: true }\n) as Record<string, { default: any }>\n\nconst foundPath = () => {\n let foundPath: string | undefined = undefined\n\n // For x.com, use the Simple Icons X instead of Lucide's X icon.\n if (name === \"x.com\") {\n foundPath = \"/node_modules/simple-icons/icons/x.svg\"\n } else if (name) {\n foundPath = Object.keys(icons).find((key) => key.includes(`/${name}.svg`))\n } else if (href) {\n const hrefKey = Object.keys(hrefMap).find((key) =>\n href.toString().includes(key)\n )\n const hrefValue = hrefMap[hrefKey as keyof typeof hrefMap]\n foundPath = Object.keys(icons)\n .reverse()\n .find((key) => key.includes(`/${hrefValue}.svg`))\n }\n\n return foundPath\n}\n\nconst iconPath = foundPath()\nconst module = iconPath ? icons[iconPath] : undefined\nconst Comp = module?.default\n---\n\n{\n Comp && foundPath()?.includes(\"simple-icons\") && (\n <Comp\n height={24}\n width={24}\n fill=\"currentColor\"\n class=\"size-[1em] text-base\"\n {...props}\n />\n )\n}\n\n{\n Comp && foundPath()?.includes(\"lucide-static\") && (\n <Comp\n height={24}\n width={24}\n fill=\"none\"\n class=\"size-[1em] text-base\"\n {...props}\n />\n )\n}\n",
12
12
  "type": "registry:ui"
13
13
  },
14
14
  {
@@ -4,7 +4,7 @@
4
4
  "files": [
5
5
  {
6
6
  "path": "src/components/ui/marquee/marquee.astro",
7
- "content": "---\nimport { cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = {\n direction?: \"left\" | \"right\" | \"top\" | \"bottom\"\n duration?: number | string\n gap?: string\n infinite?: boolean\n pauseOnHover?: boolean\n time?: number | string\n variant?: \"gradient\" | \"solid\"\n class?: string\n style?: string\n}\n\nconst {\n class: className = \"\",\n direction = \"left\",\n duration,\n gap = \"1rem\",\n infinite = true,\n pauseOnHover = false,\n style,\n time = 30,\n variant = \"gradient\",\n ...props\n} = Astro.props\n\nconst resolvedDuration = duration ?? time\nconst styleValue = `--duration:${\n typeof resolvedDuration === \"number\"\n ? `${resolvedDuration}s`\n : resolvedDuration\n}; --gap:${gap}; ${style ?? \"\"}`\nconst contentClass = cn(\n \"marquee-content flex min-w-full shrink-0 justify-around gap-[var(--gap)] will-change-transform group-data-[direction=bottom]:[animation-direction:reverse] group-data-[direction=right]:[animation-direction:reverse] data-[direction=bottom]:min-h-full data-[direction=bottom]:flex-col data-[direction=top]:min-h-full data-[direction=top]:flex-col motion-reduce:[animation-play-state:paused]\",\n infinite &&\n \"animate-[marquee-x_var(--duration,20s)_linear_infinite] group-data-[direction=bottom]:animate-[marquee-y_var(--duration,20s)_linear_infinite] group-data-[direction=top]:animate-[marquee-y_var(--duration,20s)_linear_infinite]\",\n direction === \"top\" && \"flex-col\",\n direction === \"bottom\" && \"flex-col\"\n)\n\nconst marqueeVariants = cva(\"relative w-full\", {\n variants: {\n variant: {\n gradient:\n \"[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] [-webkit-mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] [&[data-direction=bottom]]:[mask-image:linear-gradient(to_top,transparent,black_10%,black_90%,transparent)] [&[data-direction=bottom]]:[-webkit-mask-image:linear-gradient(to_top,transparent,black_10%,black_90%,transparent)] [&[data-direction=top]]:[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)] [&[data-direction=top]]:[-webkit-mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]\",\n solid: \"\",\n },\n },\n defaultVariants: {\n variant: \"gradient\",\n },\n})\n---\n\n<div\n data-marquee\n data-direction={direction}\n data-pause-on-hover={pauseOnHover ? \"true\" : undefined}\n class={cn(\n \"\",\n // container utilities\n \"group flex w-full overflow-hidden data-[direction=bottom]:flex-col data-[direction=top]:flex-col\",\n marqueeVariants({ variant }),\n className\n )}\n style={styleValue}\n {...props}\n>\n <div class={contentClass}>\n <slot />\n </div>\n {\n infinite && (\n <div class={contentClass} aria-hidden=\"true\">\n <slot />\n </div>\n )\n }\n</div>\n\n<style>\n @keyframes marquee-x {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(-100%);\n }\n }\n @keyframes marquee-y {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(-100%);\n }\n }\n\n [data-marquee][data-pause-on-hover=\"true\"]:hover .marquee-content {\n animation-play-state: paused;\n }\n</style>\n",
7
+ "content": "---\nimport { cva } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype Props = {\n direction?: \"left\" | \"right\" | \"top\" | \"bottom\"\n duration?: number | string\n gap?: string\n infinite?: boolean\n pauseOnHover?: boolean\n time?: number | string\n variant?: \"gradient\" | \"solid\"\n class?: string\n style?: string\n}\n\nconst {\n class: className = \"\",\n direction = \"left\",\n duration,\n gap = \"1rem\",\n infinite = true,\n pauseOnHover = false,\n style,\n time = 30,\n variant = \"gradient\",\n ...props\n} = Astro.props\n\nconst resolvedDuration = duration ?? time\nconst styleValue = `--duration:${\n typeof resolvedDuration === \"number\"\n ? `${resolvedDuration}s`\n : resolvedDuration\n}; --gap:${gap}; ${style ?? \"\"}`\nconst contentClass = cn(\n \"marquee-content flex min-w-full shrink-0 justify-around gap-[var(--gap)] will-change-transform group-data-[direction=bottom]:[animation-direction:reverse] group-data-[direction=right]:[animation-direction:reverse] data-[direction=bottom]:min-h-full data-[direction=bottom]:flex-col data-[direction=top]:min-h-full data-[direction=top]:flex-col motion-reduce:[animation-play-state:paused]\",\n infinite &&\n \"animate-[marquee-x_var(--duration,20s)_linear_infinite] group-data-[direction=bottom]:animate-[marquee-y_var(--duration,20s)_linear_infinite] group-data-[direction=top]:animate-[marquee-y_var(--duration,20s)_linear_infinite]\",\n direction === \"top\" && \"flex-col\",\n direction === \"bottom\" && \"flex-col\"\n)\n\nconst marqueeVariants = cva(\"relative w-full\", {\n variants: {\n variant: {\n gradient:\n \"[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] [-webkit-mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] [&[data-direction=bottom]]:[mask-image:linear-gradient(to_top,transparent,black_10%,black_90%,transparent)] [&[data-direction=bottom]]:[-webkit-mask-image:linear-gradient(to_top,transparent,black_10%,black_90%,transparent)] [&[data-direction=top]]:[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)] [&[data-direction=top]]:[-webkit-mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]\",\n solid: \"\",\n },\n },\n defaultVariants: {\n variant: \"gradient\",\n },\n})\n---\n\n<div\n data-marquee\n data-direction={direction}\n data-pause-on-hover={pauseOnHover ? \"true\" : undefined}\n class={cn(\n \"\",\n // container utilities\n \"group flex w-full gap-[var(--gap)] overflow-hidden data-[direction=bottom]:flex-col data-[direction=top]:flex-col\",\n marqueeVariants({ variant }),\n className\n )}\n style={styleValue}\n {...props}\n>\n <div class={contentClass}>\n <slot />\n </div>\n {\n infinite && (\n <div class={contentClass} aria-hidden=\"true\">\n <slot />\n </div>\n )\n }\n</div>\n\n<style>\n @keyframes marquee-x {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(calc(-100% - var(--gap)));\n }\n }\n @keyframes marquee-y {\n from {\n transform: translateY(0);\n }\n to {\n transform: translateY(calc(-100% - var(--gap)));\n }\n }\n\n [data-marquee][data-pause-on-hover=\"true\"]:hover .marquee-content {\n animation-play-state: paused;\n }\n</style>\n",
8
8
  "type": "registry:ui"
9
9
  },
10
10
  {
@@ -651,6 +651,20 @@
651
651
  }
652
652
  ]
653
653
  },
654
+ {
655
+ "name": "gradient",
656
+ "type": "registry:ui",
657
+ "files": [
658
+ {
659
+ "path": "src/components/ui/gradient/gradient.astro",
660
+ "type": "registry:ui"
661
+ },
662
+ {
663
+ "path": "src/components/ui/gradient/index.ts",
664
+ "type": "registry:ui"
665
+ }
666
+ ]
667
+ },
654
668
  {
655
669
  "name": "header",
656
670
  "type": "registry:ui",
@@ -1686,6 +1700,7 @@
1686
1700
  "@fulldev/dropdown-menu",
1687
1701
  "@fulldev/empty",
1688
1702
  "@fulldev/field",
1703
+ "@fulldev/gradient",
1689
1704
  "@fulldev/header",
1690
1705
  "@fulldev/hover-card",
1691
1706
  "@fulldev/input",
@@ -1848,11 +1863,6 @@
1848
1863
  "type": "registry:file",
1849
1864
  "target": "src/content.config.ts"
1850
1865
  },
1851
- {
1852
- "path": "src/components/layout-renderer.astro",
1853
- "type": "registry:file",
1854
- "target": "src/components/layout-renderer.astro"
1855
- },
1856
1866
  {
1857
1867
  "path": "src/lib/pages.ts",
1858
1868
  "type": "registry:file",
package/registry.json CHANGED
@@ -651,6 +651,20 @@
651
651
  }
652
652
  ]
653
653
  },
654
+ {
655
+ "name": "gradient",
656
+ "type": "registry:ui",
657
+ "files": [
658
+ {
659
+ "path": "src/components/ui/gradient/gradient.astro",
660
+ "type": "registry:ui"
661
+ },
662
+ {
663
+ "path": "src/components/ui/gradient/index.ts",
664
+ "type": "registry:ui"
665
+ }
666
+ ]
667
+ },
654
668
  {
655
669
  "name": "header",
656
670
  "type": "registry:ui",
@@ -1686,6 +1700,7 @@
1686
1700
  "@fulldev/dropdown-menu",
1687
1701
  "@fulldev/empty",
1688
1702
  "@fulldev/field",
1703
+ "@fulldev/gradient",
1689
1704
  "@fulldev/header",
1690
1705
  "@fulldev/hover-card",
1691
1706
  "@fulldev/input",
@@ -1848,11 +1863,6 @@
1848
1863
  "type": "registry:file",
1849
1864
  "target": "src/content.config.ts"
1850
1865
  },
1851
- {
1852
- "path": "src/components/layout-renderer.astro",
1853
- "type": "registry:file",
1854
- "target": "src/components/layout-renderer.astro"
1855
- },
1856
1866
  {
1857
1867
  "path": "src/lib/pages.ts",
1858
1868
  "type": "registry:file",
@@ -0,0 +1,45 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const gradientVariants = cva(
8
+ "bg-primary pointer-events-none absolute inset-0 opacity-50 [--gradient-stops:black,transparent]",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ radial:
13
+ "[mask-image:radial-gradient(ellipse_at_var(--gradient-position),var(--gradient-stops))] [-webkit-mask-image:radial-gradient(ellipse_at_var(--gradient-position),var(--gradient-stops))]",
14
+ linear:
15
+ "[mask-image:linear-gradient(var(--gradient-direction),var(--gradient-stops))] [-webkit-mask-image:linear-gradient(var(--gradient-direction),var(--gradient-stops))]",
16
+ },
17
+ direction: {
18
+ left: "[--gradient-direction:to_left] [--gradient-position:0%_50%]",
19
+ right: "[--gradient-direction:to_right] [--gradient-position:100%_50%]",
20
+ top: "[--gradient-direction:to_top] [--gradient-position:50%_0%]",
21
+ bottom:
22
+ "[--gradient-direction:to_bottom] [--gradient-position:50%_100%]",
23
+ center:
24
+ "[--gradient-direction:to_bottom] [--gradient-position:50%_50%]",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: "radial",
29
+ direction: "bottom",
30
+ },
31
+ }
32
+ )
33
+
34
+ type Props = HTMLAttributes<"div"> & VariantProps<typeof gradientVariants>
35
+
36
+ const { class: className, variant, direction, ...props } = Astro.props
37
+ ---
38
+
39
+ <div
40
+ data-slot="gradient"
41
+ aria-hidden="true"
42
+ class={cn(gradientVariants({ variant, direction }), className)}
43
+ {...props}
44
+ >
45
+ </div>
@@ -0,0 +1 @@
1
+ export { default as Gradient } from "./gradient.astro"
@@ -85,6 +85,12 @@ const Comp = module?.default
85
85
 
86
86
  {
87
87
  Comp && foundPath()?.includes("lucide-static") && (
88
- <Comp height={24} width={24} class="size-[1em] text-base" {...props} />
88
+ <Comp
89
+ height={24}
90
+ width={24}
91
+ fill="none"
92
+ class="size-[1em] text-base"
93
+ {...props}
94
+ />
89
95
  )
90
96
  }
@@ -63,7 +63,7 @@ const marqueeVariants = cva("relative w-full", {
63
63
  class={cn(
64
64
  "",
65
65
  // container utilities
66
- "group flex w-full overflow-hidden data-[direction=bottom]:flex-col data-[direction=top]:flex-col",
66
+ "group flex w-full gap-[var(--gap)] overflow-hidden data-[direction=bottom]:flex-col data-[direction=top]:flex-col",
67
67
  marqueeVariants({ variant }),
68
68
  className
69
69
  )}
@@ -88,7 +88,7 @@ const marqueeVariants = cva("relative w-full", {
88
88
  transform: translateX(0);
89
89
  }
90
90
  to {
91
- transform: translateX(-100%);
91
+ transform: translateX(calc(-100% - var(--gap)));
92
92
  }
93
93
  }
94
94
  @keyframes marquee-y {
@@ -96,7 +96,7 @@ const marqueeVariants = cva("relative w-full", {
96
96
  transform: translateY(0);
97
97
  }
98
98
  to {
99
- transform: translateY(-100%);
99
+ transform: translateY(calc(-100% - var(--gap)));
100
100
  }
101
101
  }
102
102